summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--KNOWN-ISSUES11
-rw-r--r--Makefile53
-rw-r--r--README.rst312
-rw-r--r--USAGE.rst128
-rw-r--r--VERSION1
-rw-r--r--commit-id1
-rw-r--r--distfiles/debian/compat1
-rw-r--r--distfiles/debian/control41
-rw-r--r--distfiles/debian/copyright42
-rw-r--r--distfiles/debian/docs1
-rwxr-xr-xdistfiles/debian/rules54
-rw-r--r--distfiles/mic.conf.in36
-rw-r--r--distfiles/mic.spec110
-rw-r--r--doc/mic.1235
-rw-r--r--mic/3rdparty/pykickstart/__init__.py0
-rw-r--r--mic/3rdparty/pykickstart/base.py466
-rw-r--r--mic/3rdparty/pykickstart/commands/__init__.py26
-rw-r--r--mic/3rdparty/pykickstart/commands/authconfig.py40
-rw-r--r--mic/3rdparty/pykickstart/commands/autopart.py119
-rw-r--r--mic/3rdparty/pykickstart/commands/autostep.py55
-rw-r--r--mic/3rdparty/pykickstart/commands/bootloader.py265
-rw-r--r--mic/3rdparty/pykickstart/commands/clearpart.py86
-rw-r--r--mic/3rdparty/pykickstart/commands/device.py125
-rw-r--r--mic/3rdparty/pykickstart/commands/deviceprobe.py40
-rw-r--r--mic/3rdparty/pykickstart/commands/displaymode.py68
-rw-r--r--mic/3rdparty/pykickstart/commands/dmraid.py91
-rw-r--r--mic/3rdparty/pykickstart/commands/driverdisk.py184
-rw-r--r--mic/3rdparty/pykickstart/commands/fcoe.py114
-rw-r--r--mic/3rdparty/pykickstart/commands/firewall.py193
-rw-r--r--mic/3rdparty/pykickstart/commands/firstboot.py62
-rw-r--r--mic/3rdparty/pykickstart/commands/group.py88
-rw-r--r--mic/3rdparty/pykickstart/commands/ignoredisk.py139
-rw-r--r--mic/3rdparty/pykickstart/commands/interactive.py58
-rw-r--r--mic/3rdparty/pykickstart/commands/iscsi.py133
-rw-r--r--mic/3rdparty/pykickstart/commands/iscsiname.py54
-rw-r--r--mic/3rdparty/pykickstart/commands/key.py64
-rw-r--r--mic/3rdparty/pykickstart/commands/keyboard.py55
-rw-r--r--mic/3rdparty/pykickstart/commands/lang.py60
-rw-r--r--mic/3rdparty/pykickstart/commands/langsupport.py58
-rw-r--r--mic/3rdparty/pykickstart/commands/lilocheck.py54
-rw-r--r--mic/3rdparty/pykickstart/commands/logging.py66
-rw-r--r--mic/3rdparty/pykickstart/commands/logvol.py304
-rw-r--r--mic/3rdparty/pykickstart/commands/mediacheck.py53
-rw-r--r--mic/3rdparty/pykickstart/commands/method.py186
-rw-r--r--mic/3rdparty/pykickstart/commands/monitor.py106
-rw-r--r--mic/3rdparty/pykickstart/commands/mouse.py70
-rw-r--r--mic/3rdparty/pykickstart/commands/multipath.py111
-rw-r--r--mic/3rdparty/pykickstart/commands/network.py363
-rw-r--r--mic/3rdparty/pykickstart/commands/partition.py348
-rw-r--r--mic/3rdparty/pykickstart/commands/raid.py365
-rw-r--r--mic/3rdparty/pykickstart/commands/reboot.py79
-rw-r--r--mic/3rdparty/pykickstart/commands/repo.py249
-rw-r--r--mic/3rdparty/pykickstart/commands/rescue.py68
-rw-r--r--mic/3rdparty/pykickstart/commands/rootpw.py93
-rw-r--r--mic/3rdparty/pykickstart/commands/selinux.py64
-rw-r--r--mic/3rdparty/pykickstart/commands/services.py71
-rw-r--r--mic/3rdparty/pykickstart/commands/skipx.py54
-rw-r--r--mic/3rdparty/pykickstart/commands/sshpw.py105
-rw-r--r--mic/3rdparty/pykickstart/commands/timezone.py86
-rw-r--r--mic/3rdparty/pykickstart/commands/updates.py60
-rw-r--r--mic/3rdparty/pykickstart/commands/upgrade.py106
-rw-r--r--mic/3rdparty/pykickstart/commands/user.py173
-rw-r--r--mic/3rdparty/pykickstart/commands/vnc.py114
-rw-r--r--mic/3rdparty/pykickstart/commands/volgroup.py102
-rw-r--r--mic/3rdparty/pykickstart/commands/xconfig.py184
-rw-r--r--mic/3rdparty/pykickstart/commands/zerombr.py69
-rw-r--r--mic/3rdparty/pykickstart/commands/zfcp.py134
-rw-r--r--mic/3rdparty/pykickstart/constants.py57
-rw-r--r--mic/3rdparty/pykickstart/errors.py103
-rw-r--r--mic/3rdparty/pykickstart/handlers/__init__.py0
-rw-r--r--mic/3rdparty/pykickstart/handlers/control.py1307
-rw-r--r--mic/3rdparty/pykickstart/handlers/f10.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f11.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f12.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f13.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f14.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f15.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f16.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f7.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f8.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/f9.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/fc3.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/fc4.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/fc5.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/fc6.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/rhel3.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/rhel4.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/rhel5.py24
-rw-r--r--mic/3rdparty/pykickstart/handlers/rhel6.py24
-rw-r--r--mic/3rdparty/pykickstart/ko.py37
-rw-r--r--mic/3rdparty/pykickstart/options.py204
-rw-r--r--mic/3rdparty/pykickstart/parser.py702
-rw-r--r--mic/3rdparty/pykickstart/sections.py244
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/__init__.py53
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/byterange.py463
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/grabber.py1477
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/keepalive.py617
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/mirror.py458
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/progress.py530
-rw-r--r--mic/3rdparty/pykickstart/urlgrabber/sslfactory.py90
-rw-r--r--mic/3rdparty/pykickstart/version.py197
-rw-r--r--mic/__init__.py4
-rw-r--r--mic/bootstrap.py261
-rw-r--r--mic/chroot.py340
-rw-r--r--mic/conf.py233
-rw-r--r--mic/creator.py305
-rw-r--r--mic/imager/__init__.py0
-rw-r--r--mic/imager/baseimager.py1242
-rw-r--r--mic/imager/fs.py99
-rw-r--r--mic/imager/livecd.py725
-rw-r--r--mic/imager/liveusb.py306
-rw-r--r--mic/imager/loop.py403
-rw-r--r--mic/imager/raw.py505
-rw-r--r--mic/kickstart/__init__.py915
-rw-r--r--mic/kickstart/custom_commands/__init__.py10
-rw-r--r--mic/kickstart/custom_commands/desktop.py95
-rw-r--r--mic/kickstart/custom_commands/micboot.py45
-rw-r--r--mic/kickstart/custom_commands/moblinrepo.py107
-rw-r--r--mic/kickstart/custom_commands/partition.py47
-rw-r--r--mic/msger.py309
-rw-r--r--mic/plugin.py94
-rw-r--r--mic/pluginbase.py101
-rw-r--r--mic/rt_util.py240
-rw-r--r--mic/utils/__init__.py0
-rw-r--r--mic/utils/cmdln.py1586
-rw-r--r--mic/utils/errors.py63
-rw-r--r--mic/utils/fs_related.py944
-rw-r--r--mic/utils/misc.py925
-rw-r--r--mic/utils/partitionedfs.py657
-rw-r--r--mic/utils/proxy.py183
-rw-r--r--mic/utils/rpmmisc.py667
-rw-r--r--mic/utils/runner.py108
-rw-r--r--packaging/mic.changes182
-rw-r--r--packaging/mic.spec76
-rw-r--r--plugins/backend/yumpkgmgr.py469
-rwxr-xr-xplugins/backend/zypppkgmgr.py864
-rw-r--r--plugins/hook/.py0
-rw-r--r--plugins/hook/empty_hook.py3
-rw-r--r--plugins/imager/fs_plugin.py140
-rw-r--r--plugins/imager/livecd_plugin.py251
-rw-r--r--plugins/imager/liveusb_plugin.py255
-rw-r--r--plugins/imager/loop_plugin.py248
-rw-r--r--plugins/imager/raw_plugin.py261
-rw-r--r--setup.cfg2
-rw-r--r--setup.py109
-rwxr-xr-xtools/mic239
146 files changed, 29095 insertions, 0 deletions
diff --git a/KNOWN-ISSUES b/KNOWN-ISSUES
new file mode 100644
index 0000000..1f58731
--- /dev/null
+++ b/KNOWN-ISSUES
@@ -0,0 +1,11 @@
+Known Issues
+============
+
+Bug of latest "syslinux" package
+--------------------------------
+In some new Linux distributions, the "syslinux" package in their official
+software repositories is the version 4.04. It will cause segment fault for
+a fatal bug, and mic will failed with syslinux installation errors.
+
+The solution is to install the patched "syslinux" package in MeeGo or Tizen's
+tools repos, until the official released one being fixed.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5165fd2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,53 @@
+PYTHON ?= python
+VERSION = $(shell cat VERSION)
+TAGVER = $(shell cat VERSION | sed -e "s/\([0-9\.]*\).*/\1/")
+
+PKGNAME = mic
+
+ifeq ($(VERSION), $(TAGVER))
+ TAG = $(TAGVER)
+else
+ TAG = "HEAD"
+endif
+
+
+all: build
+
+build:
+ $(PYTHON) setup.py build
+
+dist-common: man
+ git archive --format=tar --prefix=$(PKGNAME)-$(TAGVER)/ $(TAG) | tar xpf -
+ git show $(TAG) --oneline | head -1 > $(PKGNAME)-$(TAGVER)/commit-id
+ mkdir $(PKGNAME)-$(TAGVER)/doc; mv mic.1 $(PKGNAME)-$(TAGVER)/doc
+ rm -rf $(PKGNAME)-$(TAGVER)/tests
+
+dist-bz2: dist-common
+ tar jcpf $(PKGNAME)-$(TAGVER).tar.bz2 $(PKGNAME)-$(TAGVER)
+ rm -rf $(PKGNAME)-$(TAGVER)
+
+dist-gz: dist-common
+ tar zcpf $(PKGNAME)-$(TAGVER).tar.gz $(PKGNAME)-$(TAGVER)
+ rm -rf $(PKGNAME)-$(TAGVER)
+
+man: USAGE.rst
+ rst2man $< >mic.1
+
+install: build
+ $(PYTHON) setup.py install
+
+develop: build
+ $(PYTHON) setup.py develop
+
+test:
+ cd tests/unittest/ && $(PYTHON) suite.py
+
+clean:
+ rm -f *.tar.gz
+ rm -f *.tar.bz2
+ rm -f mic/__version__.*
+ rm -f tools/*.py[co]
+ rm -f mic.1
+ rm -rf *.egg-info
+ rm -rf build/
+ rm -rf dist/
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..4bdab22
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,312 @@
+=====
+ mic
+=====
+-------------------------------------
+image creator for Linux distributions
+-------------------------------------
+:Copyright: GPLv2
+:Manual section: 1
+
+Overview
+========
+The tool `mic` is used to create and manipulate images for Linux distributions.
+It's composed of three subcommand: create, convert, chroot. Subcommand `create`
+is used to create images with different types, including fs image, loop image,
+live CD image, live USB image, raw image, etc. For each image type, there is a
+corresponding subcommand. (Details in the following sections)
+
+It supports native running in many mainstream Linux distributions, including:
+
+* Fedora (14 and above)
+* openSUSE (11.3 and above)
+* Ubuntu (10.04 and above)
+* Debian (5.0 and above)
+* MeeGo
+
+Installation
+============
+
+Repositories
+------------
+So far we support `mic` binary rpms/debs for many popular Linux distributions,
+please see the following list. And you can get the corresponding repository on
+
+ `<http://download.meego.com/live/devel:/tools:/building>`_
+
+If there is no the distribution you want in the list, please install it from
+source code.
+
+* Debian 6.0
+* Fedora 14
+* Fedora 15
+* Fedora 16
+* openSUSE 11.3
+* openSUSE 11.4
+* openSUSE 12.1
+* Ubuntu 10.04
+* Ubuntu 10.10
+* Ubuntu 11.04
+* Ubuntu 11.10
+
+Binary Installation
+-------------------
+
+Fedora Installation
+~~~~~~~~~~~~~~~~~~~
+1. Add devel:tools:building repo:
+::
+
+ $ sudo cat <<REPO > /etc/yum.repos.d/devel-tools-building.repo
+ > [devel-tools-building]
+ > name=Tools for Fedora
+ > baseurl=http://download.meego.com/live/devel:/tools:/building/Fedora_<VERSION>
+ > enabled=1
+ > gpgcheck=0
+ > REPO
+
+Also you can take the repo file on devel:tools:building as example. For example,
+Fedora 13 can use:
+`<http://download.meego.com/live/devel:/tools:/building/Fedora_13/devel:tools:building.repo>`_.
+
+2. Update repolist:
+::
+
+ $ sudo yum makecache
+
+3. Install mic:
+::
+
+ $ sudo yum install mic
+
+openSUSE Installation
+~~~~~~~~~~~~~~~~~~~~~
+1. Add devel:tools:building repo:
+::
+
+ $ sudo zypper addrepo http:/download.meego.com/live/devel:/tools:/building/openSUSE_<VERSION>/ devel-tools-building
+
+2. Update repolist:
+::
+
+ $ sudo zypper refresh
+
+3. Update libzypp:
+::
+
+ $ sudo zypper update libzypp
+
+4. Install mic:
+::
+
+ $ sudo zypper install mic
+
+Ubuntu/Debian Installation
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+1. Append repo source:
+::
+
+ $ sudo cat <<REPO >> /etc/apt-sources.list
+ > deb http://download.meego.com/live/devel:/tools:/building/<Ubuntu/Debian>_<VERSION>/ /
+ > REPO
+
+*Tips*: for Ubuntu 10.10, you need to use xUbuntu_10.10 to replace
+<Ubuntu/Debian>_<VERSIN>.
+
+2. Update repolist:
+::
+
+ $ sudo apt-get update
+
+3. Install mic:
+::
+
+ $ sudo apt-get install mic
+
+Source Installation
+-------------------
+First, get the source of mic (`<TBD>`_). Then unpack the tar ball, and use make
+to process the installation.
+
+1. Unpack:
+::
+
+ $ tar xzvf mic.tar.gz
+
+2. Build:
+::
+
+ $ cd micng
+ $ make clean
+ $ make
+
+3. Install:
+::
+
+ $ sudo make install
+
+Configuration file
+==================
+The configure file for mic can be provided as `/etc/mic/mic.conf`, where you
+can specify the global settings.
+The blow is the content of one sample file: ::
+
+ [common]
+ ; general settings
+
+ [create]
+ ; settings for create subcommand
+ tmpdir= /var/tmp/mic
+ cachedir= /var/tmp/mic/cache
+ outdir= .
+ pkgmgr = zypp
+
+ ; proxy = http://proxy.yourcompany.com:8080/
+ ; no_proxy = localhost,127.0.0.0/8,.yourcompany.com
+ ; ssl_verify = no
+
+ [convert]
+ ; settings for convert subcommand
+
+ [chroot]
+ ; settings for chroot subcommand
+
+In this configuration file, there are four sections: [common] is for general
+setting, and [create] [convert] [chroot] sections are for the options of
+corresponding mic subcommands: create, convert, and chroot.
+
+In the [create] section, the following values can be specified:
+
+tmpdir
+ Temporary directory used in the image creation
+
+cachedir
+ Directory to store cached repos and downloaded rpm files
+
+outdir
+ Output directory
+
+pkgmgr
+ Default backend package manager: yum or zypp
+
+Usages
+======
+It's recommended to use `--help` or `help <subcmd>` to get the help message, for
+the tool is more or less self-documented.
+
+Running 'mic create'
+--------------------
+Subcommand *create* is used for creating images. To create an image, you should
+give the sub-sub commands which presents the image type you want, and also you
+should provide an argument which presents the kickstart file for using, such
+as: ::
+
+ $ sudo mic create fs test.ks
+
+The supported image types can be listed using `mic create --help` ::
+
+ fs create fs image
+ livecd create livecd image
+ liveusb create liveusb image
+ loop create loop image
+ raw create raw image
+
+For each image type, you can get their own options by `--help` option, like
+`mic cr fs --help`. Meanwhile, there are some common options that can be used
+by all image types, as the following ::
+
+ -h, --help show this help message and exit
+ --logfile=LOGFILE Path of logfile
+ -c CONFIG, --config=CONFIG
+ Specify config file for mic
+ -k CACHEDIR, --cachedir=CACHEDIR
+ Cache directory to store the downloaded
+ -o OUTDIR, --outdir=OUTDIR
+ Output directory
+ -A ARCH, --arch=ARCH
+ Specify repo architecture
+ --release=RID Generate a release of RID with all necessary
+ files,when @BUILD_ID@ is contained in kickstart file,
+ it will be replaced by RID
+ --record-pkgs=RECORD_PKGS
+ Record the info of installed packages, multiple values
+ can be specified which joined by ",", valid values:
+ "name", "content", "license"
+ --pkgmgr=PKGMGR Specify backend package manager
+ --local-pkgs-path=LOCAL_PKGS_PATH
+ Path for local pkgs(rpms) to be installed
+
+*Tips*: the common options can be normally put before sub-sub command, but also
+can be after them, such as: ::
+
+ $ sudo mic cr --outdir output fs test.ks
+
+or ::
+
+ $ sudo mic cr fs test.ks --outdir output
+
+*Tips*: if you failed to create armv7* image, the reason may be: qemu/qemu-arm
+on your host is lower than required, please upgrade qemu/qemu-arm higher than
+version 0.13.0.
+
+Running 'mic chroot'
+--------------------
+Subcommand *chroot* is used to chroot an image file. Given an image file, you
+can use `mic chroot` to chroot inside the image, and then you can do some
+modification to the image. After you logout, the image file will keep your
+changes. It's a convenient way to hack your image file.
+
+Sample command: ::
+
+ $ sudo mic chroot test.img
+
+Running 'mic convert'
+---------------------
+Subcommand *convert* is used for converting an image to another one with
+different image type. Using `convert`, you can get your needed image type
+comfortably. So far converting livecd to liveusb and liveusb to livecd is
+supported.
+
+Sample command: ::
+
+ $ sudo mic convert test.iso liveusb
+
+Debug/Verbose Output
+--------------------
+When you encounter some errors, and you want to know more about it, please use
+debug/verbose output to get more details in the process by adding `-d/-v`. And
+it's recommended to add `-d/--debug` or `-v/--verbose` like: ::
+
+ $ sudo mic -d cr fs test.ks
+
+Advance Features
+================
+
+Proxy support
+-------------
+proxy setting in mic.conf is not enabled, but you can set proxy in repo section
+of ks file, example as follows: ::
+
+ repo --name=1.2-oss --baseurl=http://repo.meego.com/MeeGo/releases/1.2.0/repos/oss/ia32/packages/ --proxy=http://host:port --save --debuginfo --source --gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-meego
+
+Multiple running instances support
+----------------------------------
+mic support running multi-instance, but cache dir can't be shared between
+instances, so you should specify different cachedir for different instance
+using `--cachedir`. Also outdir should be specified to a different directory
+for each instance using `--outdir`, example as follows: ::
+
+ mic cr fs netbook1.ks --cachedir=/var/tmp/cache/mic1 --outdir=out1
+ mic cr fs netbook2.ks --cachedir=/var/tmp/cache/mic2 --outdir=out2
+
+Known Issues
+============
+
+Bug of latest "syslinux" package
+--------------------------------
+In some new Linux distributions, the "syslinux" package in their official
+software repositories is the version 4.04. It will cause segment fault for
+a fatal bug, and mic will failed with syslinux installation errors.
+
+The solution is to install the patched "syslinux" package in MeeGo or Tizen's
+tools repos, until the official released one being fixed.
+
diff --git a/USAGE.rst b/USAGE.rst
new file mode 100644
index 0000000..c6454d6
--- /dev/null
+++ b/USAGE.rst
@@ -0,0 +1,128 @@
+=====
+ mic
+=====
+
+-----------------------------------------------
+mic Means Image Creator for Linux distributions
+-----------------------------------------------
+:Date: 2012-03-02
+:Copyright: GPLv2
+:Version: 0.1
+:Manual section: 1
+:Manual group: System
+
+SYNOPSIS
+========
+
+| mic create SUBCOMMAND <ksfile> [OPTION]
+| mic chroot [OPTION] <imgfile>
+| mic convert [OPTION] <imgfile> <format>
+
+DESCRIPTION
+===========
+The tools `mic` is used to create and manipulate images for Linux distributions.
+It is composed of three subcommand: `create`, `convert`, `chroot`.
+
+USAGE
+=====
+
+create
+------
+This command is used to create various images, including live CD, live USB,
+loop, raw.
+
+Usage:
+
+ | mic create(cr) SUBCOMMAND <ksfile> [OPTION]
+
+Subcommands:
+
+ | help(?) give detailed help on a specific sub-command
+ | fs create fs image, which is also chroot directory
+ | livecd create live CD image, used for CD booting
+ | liveusb create live USB image, used for USB booting
+ | loop create loop image, including multi-partitions
+ | raw create raw image, containing multi-partitions
+
+Options:
+
+ -h, --help show the help message
+ --logfile=LOGFILE specify the path of logfile, save the output to logfile LOGFILE
+ -c CONFIG, --config=CONFIG specify configure file for mic, default is /etc/mic/mic.conf
+ -k CACHEDIR, --cachedir=CACHEDIR cache directory used to store the downloaded files and packages
+ -o OUTDIR, --outdir=OUTDIR directory used to locate the output image and files
+ -A ARCH, --arch=ARCH specify repo architecture, genarally mic would detect the architecture, if existed more than one architecture, mic would give hint to you
+ --local-pkgs-path=LOCAL_PKGS_PATH specify the path for local rpm packages, which would be stored your own rpm packages
+ --pkgmgr=PKGMGR specify backend package mananger, currently yum and zypp available
+ --record-pkgs=RECORD_PKGS record the info of installed packages, multiple values can be specified which joined by ",", valid values: "name", "content", "license"
+ --copy-kernel Copy kernel files from image /boot directory to the image output directory.
+ --compress-disk-image=COMPRESS_DISK_IMAGE Sets the disk image compression. NOTE: The available values might depend on the used filesystem type.
+ --release=RID Generate a release of RID with all necessary files, when @BUILD_ID@ is contained in kickstart file, it will be replaced by RID. sample values: "latest", "tizen_20120101.1"
+
+Examples:
+
+ | mic create loop tizen.ks
+ | mic create livecd tizen.ks --release=latest
+ | mic cr fs tizen.ks --local-pkgs-path=localrpm
+
+chroot
+------
+This command is used to chroot inside the image, it's a great enhancement of chroot command in linux system.
+
+Usage:
+
+ | mic chroot(ch) <imgfile>
+
+Options:
+
+ -h, --help show the help message
+ -s SAVETO, --saveto=SAVETO save the unpacked image to specified directory SAVETO
+
+Examples:
+
+ | mic chroot loop.img
+ | mic chroot tizen.iso
+ | mic ch -s tizenfs tizen.usbimg
+
+convert
+-------
+This command is used for converting an image to another format.
+
+Usage:
+
+ | mic convert(cv) <imagefile> <destformat>
+
+Options:
+
+ -h, --help show the help message
+ -S, --shell launch interactive shell before packing the new image in the converting
+
+Examples:
+
+ | mic convert tizen.iso liveusb
+ | mic convert tizen.usbimg livecd
+ | mic cv --shell tizen.iso liveusb
+
+KNOWN ISSUES
+============
+Bug of latest syslinux package
+------------------------------
+In some new Linux distributions, the "syslinux" package in their official
+software repositories is the version 4.04. It will cause segment fault for
+a fatal bug, and mic will failed with syslinux installation errors.
+
+The solution is to install the patched "syslinux" package in MeeGo or Tizen's
+tools repos, until the official released one being fixed.
+
+Failed to create btrfs image in openSUSE
+----------------------------------------
+When creating btrfs image in openSUSE, it would hang up with showing image kernel
+panic. This issue impact all openSUSE distributions: 12.1, 11.4, 11.3, etc
+
+REPORTING BUGS
+==============
+The source code is tracked in github.com:
+
+ https://github.com/jfding/mic
+
+Please report issues for bugs or feature requests.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..948a547
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.14
diff --git a/commit-id b/commit-id
new file mode 100644
index 0000000..a8cf13c
--- /dev/null
+++ b/commit-id
@@ -0,0 +1 @@
+tag 0.14
diff --git a/distfiles/debian/compat b/distfiles/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/distfiles/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/distfiles/debian/control b/distfiles/debian/control
new file mode 100644
index 0000000..b34aa3e
--- /dev/null
+++ b/distfiles/debian/control
@@ -0,0 +1,41 @@
+Source: mic
+Section: devel
+Priority: extra
+Maintainer: Jian-feng Ding <jian-feng.ding@intel.com>
+Build-Depends: debhelper (>= 7.0.15), cdbs, python-dev, python-support
+Standards-Version: 3.8.0
+Homepage: http://www.tizen.org
+
+Package: mic
+Architecture: all
+Depends: ${misc:Depends}, ${python:Depends},
+ python-rpm,
+ bzip2,
+ dmsetup,
+ dosfstools,
+ e2fsprogs (>= 1.41),
+ isomd5sum,
+ genisoimage,
+ kpartx,
+ parted,
+ psmisc,
+ squashfs-tools (>= 4.0),
+ qemu-user-static | qemu-arm-static,
+ syslinux (>= 4.05),
+ extlinux (>= 4.05),
+ yum (>= 3.2),
+ libzypp,
+ python-zypp,
+ python-urlgrabber
+Recommends:
+ binfmt-support,
+ btrfs-tools,
+ udisks | hal
+Replaces:
+ mic2
+Description: image creator for Linux distributions
+ The tool mic is used to create and manipulate images for Linux distributions.
+ It is composed of three subcommand\: create, convert, chroot. Subcommand create
+ is used to create images with different types; subcommand convert is used to
+ convert an image to a specified type; subcommand chroot is used to chroot into
+ an image.
diff --git a/distfiles/debian/copyright b/distfiles/debian/copyright
new file mode 100644
index 0000000..0c99694
--- /dev/null
+++ b/distfiles/debian/copyright
@@ -0,0 +1,42 @@
+This work was packaged for Debian by:
+
+ Ding Jianfeng <jian-feng.ding@intel.com> on Fri, 26 Dec 2008 23:00:35 +0800
+
+It was downloaded from:
+
+ https://github.com/jfding/mic
+
+Upstream Authors:
+
+ Red Hat Inc.
+ Intel Inc.
+
+Copyright:
+
+ Copyright (C) 2007-2008 Red Hat Inc.
+ Copyright (C) 2008-2012 Intel Inc.
+
+Copyright for mic/cmdln.py:
+
+ Copyright (C) 2002-2005 ActiveState Corp.
+ Copyright (C) Trent Mick <trentm@activestate.com>
+
+ MIT License
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
diff --git a/distfiles/debian/docs b/distfiles/debian/docs
new file mode 100644
index 0000000..a1320b1
--- /dev/null
+++ b/distfiles/debian/docs
@@ -0,0 +1 @@
+README.rst
diff --git a/distfiles/debian/rules b/distfiles/debian/rules
new file mode 100755
index 0000000..af51936
--- /dev/null
+++ b/distfiles/debian/rules
@@ -0,0 +1,54 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+build: build-stamp
+build-stamp:
+ dh_testdir
+
+ python setup.py build
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Installing package
+ mkdir -p $(CURDIR)/debian/mic $(CURDIR)/debian/mic/usr/bin $(CURDIR)/debian/mic/usr/share/man/man1
+ install -m644 doc/mic.1 $(CURDIR)/debian/mic/usr/share/man/man1
+ #make DESTDIR=$(CURDIR)/debian/mic installman
+ #make DESTDIR=$(CURDIR)/debian/micng installconf
+ #make DESTDIR=$(CURDIR)/debian/micng installsymlinks
+ python setup.py install --root=$(CURDIR)/debian/mic
+
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_install
+ dh_installman
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_pysupport
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary-arch: build install
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/distfiles/mic.conf.in b/distfiles/mic.conf.in
new file mode 100644
index 0000000..a1d0d70
--- /dev/null
+++ b/distfiles/mic.conf.in
@@ -0,0 +1,36 @@
+[common]
+; general settings
+distro_name = Tizen
+
+plugin_dir = @PREFIX@/lib/mic/plugins
+
+[create]
+; settings for create subcommand
+tmpdir= /var/tmp/mic
+cachedir= /var/tmp/mic/cache
+outdir= ./mic-output
+bootstrapdir= /var/tmp/mic/bootstrap
+
+pkgmgr = zypp
+
+# to set global proxy for repos
+#proxy = http://proxy.yourcompany.com:8080/
+#no_proxy = localhost,127.0.0.0/8,.yourcompany.com
+
+# prefix will be added in front of generated files
+#name_prefix = output
+
+# to skip all ssl verification for repos
+#ssl_verify = no
+
+[convert]
+; settings for convert subcommand
+
+[chroot]
+; settings for chroot subcommand
+
+[bootstrap1]
+name=micbootstrap
+bootstrap=http://download.tizen.org/tools/micbootstrap/
+; bootstrap_proxy=http://proxy.yourcompany.com:port
+
diff --git a/distfiles/mic.spec b/distfiles/mic.spec
new file mode 100644
index 0000000..5ad6769
--- /dev/null
+++ b/distfiles/mic.spec
@@ -0,0 +1,110 @@
+%define is_tizen %(test -e /etc/tizen-release -o -e /etc/meego-release && echo 1 || echo 0)
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+Name: mic
+Summary: Image Creator for Linux Distributions
+Version: 0.14
+Release: 1
+Group: System/Base
+License: GPLv2
+BuildArch: noarch
+URL: http://www.tizen.org
+Source0: %{name}-%{version}.tar.gz
+Requires: rpm-python
+Requires: util-linux
+Requires: coreutils
+Requires: python >= 2.5
+Requires: e2fsprogs
+Requires: dosfstools >= 2.11-8
+%if 0%{is_tizen} == 0
+Requires: yum >= 3.2.24
+%endif
+%if 0%{?suse_version} == 1210
+Requires: syslinux == 4.04.1
+%else
+Requires: syslinux >= 3.82
+%endif
+Requires: kpartx
+Requires: parted
+Requires: device-mapper
+Requires: /usr/bin/genisoimage
+Requires: cpio
+Requires: isomd5sum
+Requires: gzip
+Requires: bzip2
+Requires: squashfs-tools >= 4.0
+Requires: python-urlgrabber
+%if 0%{?suse_version}
+Requires: btrfsprogs
+%else
+Requires: btrfs-progs
+%endif
+
+%if 0%{?fedora_version} || 0%{is_tizen} == 1
+Requires: m2crypto
+%else
+%if 0%{?suse_version} == 1210
+Requires: python-M2Crypto
+%else
+Requires: python-m2crypto
+%endif
+%endif
+
+%if 0%{?fedora_version} == 16
+Requires: syslinux-extlinux
+%endif
+
+%if 0%{?suse_version} == 1210
+Requires: python-zypp == 0.5.50
+%else
+Requires: python-zypp >= 0.5.9.1
+%endif
+BuildRequires: python-devel
+
+Obsoletes: mic2
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+
+%description
+The tool mic is used to create and manipulate images for Linux distributions.
+It is composed of three subcommand\: create, convert, chroot. Subcommand create
+is used to create images with different types; subcommand convert is used to
+convert an image to a specified type; subcommand chroot is used to chroot into
+an image.
+
+
+%prep
+%setup -q -n %{name}-%{version}
+
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+%if 0%{?suse_version}
+%{__python} setup.py install --root=$RPM_BUILD_ROOT --prefix=%{_prefix}
+%else
+%{__python} setup.py install --root=$RPM_BUILD_ROOT -O1
+%endif
+
+# install man page
+# remove yum backend for tizen
+%if 0%{is_tizen} == 1
+rm -rf %{buildroot}/%{_prefix}/lib/%{name}/plugins/backend/yumpkgmgr.py
+%endif
+mkdir -p %{buildroot}/%{_prefix}/share/man/man1
+install -m644 doc/mic.1 %{buildroot}/%{_prefix}/share/man/man1
+
+
+%files
+%defattr(-,root,root,-)
+%doc README.rst
+%{_mandir}/man1/*
+%dir %{_sysconfdir}/%{name}
+%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
+%{python_sitelib}/*
+%dir %{_prefix}/lib/%{name}
+%{_prefix}/lib/%{name}/*
+%{_bindir}/*
diff --git a/doc/mic.1 b/doc/mic.1
new file mode 100644
index 0000000..31cdb74
--- /dev/null
+++ b/doc/mic.1
@@ -0,0 +1,235 @@
+.\" Man page generated from reStructeredText.
+.
+.TH MIC 1 "2012-03-02" "0.1" "System"
+.SH NAME
+mic \- mic Means Image Creator for Linux distributions
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH SYNOPSIS
+.nf
+mic create SUBCOMMAND <ksfile> [OPTION]
+mic chroot [OPTION] <imgfile>
+mic convert [OPTION] <imgfile> <format>
+.fi
+.sp
+.SH DESCRIPTION
+.sp
+The tools \fImic\fP is used to create and manipulate images for Linux distributions.
+It is composed of three subcommand: \fIcreate\fP, \fIconvert\fP, \fIchroot\fP.
+.SH USAGE
+.SS create
+.sp
+This command is used to create various images, including live CD, live USB,
+loop, raw.
+.sp
+Usage:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic create(cr) SUBCOMMAND <ksfile> [OPTION]
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.sp
+Subcommands:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+help(?) give detailed help on a specific sub\-command
+fs create fs image, which is also chroot directory
+livecd create live CD image, used for CD booting
+liveusb create live USB image, used for USB booting
+loop create loop image, including multi\-partitions
+raw create raw image, containing multi\-partitions
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.sp
+Options:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show the help message
+.TP
+.BI \-\-logfile\fB= LOGFILE
+specify the path of logfile, save the output to logfile LOGFILE
+.TP
+.BI \-c \ CONFIG, \ \-\-config\fB= CONFIG
+specify configure file for mic, default is /etc/mic/mic.conf
+.TP
+.BI \-k \ CACHEDIR, \ \-\-cachedir\fB= CACHEDIR
+cache directory used to store the downloaded files and packages
+.TP
+.BI \-o \ OUTDIR, \ \-\-outdir\fB= OUTDIR
+directory used to locate the output image and files
+.TP
+.BI \-A \ ARCH, \ \-\-arch\fB= ARCH
+specify repo architecture, genarally mic would detect the architecture, if existed more than one architecture, mic would give hint to you
+.TP
+.BI \-\-local\-pkgs\-path\fB= LOCAL_PKGS_PATH
+specify the path for local rpm packages, which would be stored your own rpm packages
+.TP
+.BI \-\-pkgmgr\fB= PKGMGR
+specify backend package mananger, currently yum and zypp available
+.TP
+.BI \-\-record\-pkgs\fB= RECORD_PKGS
+record the info of installed packages, multiple values can be specified which joined by ",", valid values: "name", "content", "license"
+.TP
+.B \-\-copy\-kernel
+Copy kernel files from image /boot directory to the image output directory.
+.TP
+.BI \-\-compress\-disk\-image\fB= COMPRESS_DISK_IMAGE
+Sets the disk image compression. NOTE: The available values might depend on the used filesystem type.
+.TP
+.BI \-\-release\fB= RID
+Generate a release of RID with all necessary files, when @BUILD_ID@ is contained in kickstart file, it will be replaced by RID. sample values: "latest", "tizen_20120101.1"
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+Examples:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic create loop tizen.ks
+mic create livecd tizen.ks \-\-release=latest
+mic cr fs tizen.ks \-\-local\-pkgs\-path=localrpm
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.SS chroot
+.sp
+This command is used to chroot inside the image, it\(aqs a great enhancement of chroot command in linux system.
+.sp
+Usage:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic chroot(ch) <imgfile>
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.sp
+Options:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show the help message
+.TP
+.BI \-s \ SAVETO, \ \-\-saveto\fB= SAVETO
+save the unpacked image to specified directory SAVETO
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+Examples:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic chroot loop.img
+mic chroot tizen.iso
+mic ch \-s tizenfs tizen.usbimg
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.SS convert
+.sp
+This command is used for converting an image to another format.
+.sp
+Usage:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic convert(cv) <imagefile> <destformat>
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.sp
+Options:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show the help message
+.TP
+.B \-S, \-\-shell
+launch interactive shell before packing the new image in the converting
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+Examples:
+.INDENT 0.0
+.INDENT 3.5
+.nf
+mic convert tizen.iso liveusb
+mic convert tizen.usbimg livecd
+mic cv \-\-shell tizen.iso liveusb
+.fi
+.sp
+.UNINDENT
+.UNINDENT
+.SH KNOWN ISSUES
+.SS Bug of latest syslinux package
+.sp
+In some new Linux distributions, the "syslinux" package in their official
+software repositories is the version 4.04. It will cause segment fault for
+a fatal bug, and mic will failed with syslinux installation errors.
+.sp
+The solution is to install the patched "syslinux" package in MeeGo or Tizen\(aqs
+tools repos, until the official released one being fixed.
+.SS Failed to create btrfs image in openSUSE
+.sp
+When creating btrfs image in openSUSE, it would hang up with showing image kernel
+panic. This issue impact all openSUSE distributions: 12.1, 11.4, 11.3, etc
+.SH REPORTING BUGS
+.sp
+The source code is tracked in github.com:
+.INDENT 0.0
+.INDENT 3.5
+\fI\%https://github.com/jfding/mic\fP
+.UNINDENT
+.UNINDENT
+.sp
+Please report issues for bugs or feature requests.
+.SH COPYRIGHT
+GPLv2
+.\" Generated by docutils manpage writer.
+.\"
+.
diff --git a/mic/3rdparty/pykickstart/__init__.py b/mic/3rdparty/pykickstart/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mic/3rdparty/pykickstart/__init__.py
diff --git a/mic/3rdparty/pykickstart/base.py b/mic/3rdparty/pykickstart/base.py
new file mode 100644
index 0000000..e6c8f56
--- /dev/null
+++ b/mic/3rdparty/pykickstart/base.py
@@ -0,0 +1,466 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Base classes for creating commands and syntax version object.
+
+This module exports several important base classes:
+
+ BaseData - The base abstract class for all data objects. Data objects
+ are contained within a BaseHandler object.
+
+ BaseHandler - The base abstract class from which versioned kickstart
+ handler are derived. Subclasses of BaseHandler hold
+ BaseData and KickstartCommand objects.
+
+ DeprecatedCommand - An abstract subclass of KickstartCommand that should
+ be further subclassed by users of this module. When
+ a subclass is used, a warning message will be
+ printed.
+
+ KickstartCommand - The base abstract class for all kickstart commands.
+ Command objects are contained within a BaseHandler
+ object.
+"""
+import gettext
+gettext.textdomain("pykickstart")
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+import types
+import warnings
+from pykickstart.errors import *
+from pykickstart.ko import *
+from pykickstart.parser import Packages
+from pykickstart.version import versionToString
+
+###
+### COMMANDS
+###
+class KickstartCommand(KickstartObject):
+ """The base class for all kickstart commands. This is an abstract class."""
+ removedKeywords = []
+ removedAttrs = []
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ """Create a new KickstartCommand instance. This method must be
+ provided by all subclasses, but subclasses must call
+ KickstartCommand.__init__ first. Instance attributes:
+
+ currentCmd -- The name of the command in the input file that
+ caused this handler to be run.
+ currentLine -- The current unprocessed line from the input file
+ that caused this handler to be run.
+ handler -- A reference to the BaseHandler subclass this
+ command is contained withing. This is needed to
+ allow referencing of Data objects.
+ lineno -- The current line number in the input file.
+ writePriority -- An integer specifying when this command should be
+ printed when iterating over all commands' __str__
+ methods. The higher the number, the later this
+ command will be written. All commands with the
+ same priority will be written alphabetically.
+ """
+
+ # We don't want people using this class by itself.
+ if self.__class__ is KickstartCommand:
+ raise TypeError, "KickstartCommand is an abstract class."
+
+ KickstartObject.__init__(self, *args, **kwargs)
+
+ self.writePriority = writePriority
+
+ # These will be set by the dispatcher.
+ self.currentCmd = ""
+ self.currentLine = ""
+ self.handler = None
+ self.lineno = 0
+
+ # If a subclass provides a removedKeywords list, remove all the
+ # members from the kwargs list before we start processing it. This
+ # ensures that subclasses don't continue to recognize arguments that
+ # were removed.
+ for arg in filter(kwargs.has_key, self.removedKeywords):
+ kwargs.pop(arg)
+
+ def __call__(self, *args, **kwargs):
+ """Set multiple attributes on a subclass of KickstartCommand at once
+ via keyword arguments. Valid attributes are anything specified in
+ a subclass, but unknown attributes will be ignored.
+ """
+ for (key, val) in kwargs.items():
+ # Ignore setting attributes that were removed in a subclass, as
+ # if they were unknown attributes.
+ if key in self.removedAttrs:
+ continue
+
+ if hasattr(self, key):
+ setattr(self, key, val)
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file. This
+ method must be provided by all subclasses.
+ """
+ return KickstartObject.__str__(self)
+
+ def parse(self, args):
+ """Parse the list of args and set data on the KickstartCommand object.
+ This method must be provided by all subclasses.
+ """
+ raise TypeError, "parse() not implemented for KickstartCommand"
+
+ def apply(self, instroot="/"):
+ """Write out the configuration related to the KickstartCommand object.
+ Subclasses which do not provide this method will not have their
+ configuration written out.
+ """
+ return
+
+ def dataList(self):
+ """For commands that can occur multiple times in a single kickstart
+ file (like network, part, etc.), return the list that we should
+ append more data objects to.
+ """
+ return None
+
+ def deleteRemovedAttrs(self):
+ """Remove all attributes from self that are given in the removedAttrs
+ list. This method should be called from __init__ in a subclass,
+ but only after the superclass's __init__ method has been called.
+ """
+ for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
+ delattr(self, attr)
+
+ # Set the contents of the opts object (an instance of optparse.Values
+ # returned by parse_args) as attributes on the KickstartCommand object.
+ # It's useful to call this from KickstartCommand subclasses after parsing
+ # the arguments.
+ def _setToSelf(self, optParser, opts):
+ self._setToObj(optParser, opts, self)
+
+ # Sets the contents of the opts object (an instance of optparse.Values
+ # returned by parse_args) as attributes on the provided object obj. It's
+ # useful to call this from KickstartCommand subclasses that handle lists
+ # of objects (like partitions, network devices, etc.) and need to populate
+ # a Data object.
+ def _setToObj(self, optParser, opts, obj):
+ for key in filter (lambda k: getattr(opts, k) != None, optParser.keys()):
+ setattr(obj, key, getattr(opts, key))
+
+class DeprecatedCommand(KickstartCommand):
+ """Specify that a command is deprecated and no longer has any function.
+ Any command that is deprecated should be subclassed from this class,
+ only specifying an __init__ method that calls the superclass's __init__.
+ This is an abstract class.
+ """
+ def __init__(self, writePriority=None, *args, **kwargs):
+ # We don't want people using this class by itself.
+ if self.__class__ is KickstartCommand:
+ raise TypeError, "DeprecatedCommand is an abstract class."
+
+ # Create a new DeprecatedCommand instance.
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+
+ def __str__(self):
+ """Placeholder since DeprecatedCommands don't work anymore."""
+ return ""
+
+ def parse(self, args):
+ """Print a warning message if the command is seen in the input file."""
+ mapping = {"lineno": self.lineno, "cmd": self.currentCmd}
+ warnings.warn(_("Ignoring deprecated command on line %(lineno)s: The %(cmd)s command has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this command.") % mapping, DeprecationWarning)
+
+
+###
+### HANDLERS
+###
+class BaseHandler(KickstartObject):
+ """Each version of kickstart syntax is provided by a subclass of this
+ class. These subclasses are what users will interact with for parsing,
+ extracting data, and writing out kickstart files. This is an abstract
+ class.
+
+ version -- The version this syntax handler supports. This is set by
+ a class attribute of a BaseHandler subclass and is used to
+ set up the command dict. It is for read-only use.
+ """
+ version = None
+
+ def __init__(self, mapping=None, dataMapping=None, commandUpdates=None,
+ dataUpdates=None, *args, **kwargs):
+ """Create a new BaseHandler instance. This method must be provided by
+ all subclasses, but subclasses must call BaseHandler.__init__ first.
+
+ mapping -- A custom map from command strings to classes,
+ useful when creating your own handler with
+ special command objects. It is otherwise unused
+ and rarely needed. If you give this argument,
+ the mapping takes the place of the default one
+ and so must include all commands you want
+ recognized.
+ dataMapping -- This is the same as mapping, but for data
+ objects. All the same comments apply.
+ commandUpdates -- This is similar to mapping, but does not take
+ the place of the defaults entirely. Instead,
+ this mapping is applied after the defaults and
+ updates it with just the commands you want to
+ modify.
+ dataUpdates -- This is the same as commandUpdates, but for
+ data objects.
+
+
+ Instance attributes:
+
+ commands -- A mapping from a string command to a KickstartCommand
+ subclass object that handles it. Multiple strings can
+ map to the same object, but only one instance of the
+ command object should ever exist. Most users should
+ never have to deal with this directly, as it is
+ manipulated internally and called through dispatcher.
+ currentLine -- The current unprocessed line from the input file
+ that caused this handler to be run.
+ packages -- An instance of pykickstart.parser.Packages which
+ describes the packages section of the input file.
+ platform -- A string describing the hardware platform, which is
+ needed only by system-config-kickstart.
+ scripts -- A list of pykickstart.parser.Script instances, which is
+ populated by KickstartParser.addScript and describes the
+ %pre/%post/%traceback script section of the input file.
+ """
+
+ # We don't want people using this class by itself.
+ if self.__class__ is BaseHandler:
+ raise TypeError, "BaseHandler is an abstract class."
+
+ KickstartObject.__init__(self, *args, **kwargs)
+
+ # This isn't really a good place for these, but it's better than
+ # everything else I can think of.
+ self.scripts = []
+ self.packages = Packages()
+ self.platform = ""
+
+ # These will be set by the dispatcher.
+ self.commands = {}
+ self.currentLine = 0
+
+ # A dict keyed by an integer priority number, with each value being a
+ # list of KickstartCommand subclasses. This dict is maintained by
+ # registerCommand and used in __str__. No one else should be touching
+ # it.
+ self._writeOrder = {}
+
+ self._registerCommands(mapping, dataMapping, commandUpdates, dataUpdates)
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file."""
+ retval = ""
+
+ if self.platform != "":
+ retval += "#platform=%s\n" % self.platform
+
+ retval += "#version=%s\n" % versionToString(self.version)
+
+ lst = self._writeOrder.keys()
+ lst.sort()
+
+ for prio in lst:
+ for obj in self._writeOrder[prio]:
+ retval += obj.__str__()
+
+ for script in self.scripts:
+ retval += script.__str__()
+
+ retval += self.packages.__str__()
+
+ return retval
+
+ def _insertSorted(self, lst, obj):
+ length = len(lst)
+ i = 0
+
+ while i < length:
+ # If the two classes have the same name, it's because we are
+ # overriding an existing class with one from a later kickstart
+ # version, so remove the old one in favor of the new one.
+ if obj.__class__.__name__ > lst[i].__class__.__name__:
+ i += 1
+ elif obj.__class__.__name__ == lst[i].__class__.__name__:
+ lst[i] = obj
+ return
+ elif obj.__class__.__name__ < lst[i].__class__.__name__:
+ break
+
+ if i >= length:
+ lst.append(obj)
+ else:
+ lst.insert(i, obj)
+
+ def _setCommand(self, cmdObj):
+ # Add an attribute on this version object. We need this to provide a
+ # way for clients to access the command objects. We also need to strip
+ # off the version part from the front of the name.
+ if cmdObj.__class__.__name__.find("_") != -1:
+ name = unicode(cmdObj.__class__.__name__.split("_", 1)[1])
+ else:
+ name = unicode(cmdObj.__class__.__name__).lower()
+
+ setattr(self, name.lower(), cmdObj)
+
+ # Also, add the object into the _writeOrder dict in the right place.
+ if cmdObj.writePriority is not None:
+ if self._writeOrder.has_key(cmdObj.writePriority):
+ self._insertSorted(self._writeOrder[cmdObj.writePriority], cmdObj)
+ else:
+ self._writeOrder[cmdObj.writePriority] = [cmdObj]
+
+ def _registerCommands(self, mapping=None, dataMapping=None, commandUpdates=None,
+ dataUpdates=None):
+ if mapping == {} or mapping == None:
+ from pykickstart.handlers.control import commandMap
+ cMap = commandMap[self.version]
+ else:
+ cMap = mapping
+
+ if dataMapping == {} or dataMapping == None:
+ from pykickstart.handlers.control import dataMap
+ dMap = dataMap[self.version]
+ else:
+ dMap = dataMapping
+
+ if type(commandUpdates) == types.DictType:
+ cMap.update(commandUpdates)
+
+ if type(dataUpdates) == types.DictType:
+ dMap.update(dataUpdates)
+
+ for (cmdName, cmdClass) in cMap.iteritems():
+ # First make sure we haven't instantiated this command handler
+ # already. If we have, we just need to make another mapping to
+ # it in self.commands.
+ cmdObj = None
+
+ for (key, val) in self.commands.iteritems():
+ if val.__class__.__name__ == cmdClass.__name__:
+ cmdObj = val
+ break
+
+ # If we didn't find an instance in self.commands, create one now.
+ if cmdObj == None:
+ cmdObj = cmdClass()
+ self._setCommand(cmdObj)
+
+ # Finally, add the mapping to the commands dict.
+ self.commands[cmdName] = cmdObj
+ self.commands[cmdName].handler = self
+
+ # We also need to create attributes for the various data objects.
+ # No checks here because dMap is a bijection. At least, that's what
+ # the comment says. Hope no one screws that up.
+ for (dataName, dataClass) in dMap.iteritems():
+ setattr(self, dataName, dataClass)
+
+ def dispatcher(self, args, lineno):
+ """Call the appropriate KickstartCommand handler for the current line
+ in the kickstart file. A handler for the current command should
+ be registered, though a handler of None is not an error. Returns
+ the data object returned by KickstartCommand.parse.
+
+ args -- A list of arguments to the current command
+ lineno -- The line number in the file, for error reporting
+ """
+ cmd = args[0]
+
+ if not self.commands.has_key(cmd):
+ raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown command: %s" % cmd))
+ elif self.commands[cmd] != None:
+ self.commands[cmd].currentCmd = cmd
+ self.commands[cmd].currentLine = self.currentLine
+ self.commands[cmd].lineno = lineno
+
+ # The parser returns the data object that was modified. This could
+ # be a BaseData subclass that should be put into a list, or it
+ # could be the command handler object itself.
+ obj = self.commands[cmd].parse(args[1:])
+ lst = self.commands[cmd].dataList()
+ if lst is not None:
+ lst.append(obj)
+
+ return obj
+
+ def maskAllExcept(self, lst):
+ """Set all entries in the commands dict to None, except the ones in
+ the lst. All other commands will not be processed.
+ """
+ self._writeOrder = {}
+
+ for (key, val) in self.commands.iteritems():
+ if not key in lst:
+ self.commands[key] = None
+
+ def hasCommand(self, cmd):
+ """Return true if there is a handler for the string cmd."""
+ return hasattr(self, cmd)
+
+
+###
+### DATA
+###
+class BaseData(KickstartObject):
+ """The base class for all data objects. This is an abstract class."""
+ removedKeywords = []
+ removedAttrs = []
+
+ def __init__(self, *args, **kwargs):
+ """Create a new BaseData instance.
+
+ lineno -- Line number in the ks-file where this object was defined
+ """
+
+ # We don't want people using this class by itself.
+ if self.__class__ is BaseData:
+ raise TypeError, "BaseData is an abstract class."
+
+ KickstartObject.__init__(self, *args, **kwargs)
+ self.lineno = 0
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file."""
+ return ""
+
+ def __call__(self, *args, **kwargs):
+ """Set multiple attributes on a subclass of BaseData at once via
+ keyword arguments. Valid attributes are anything specified in a
+ subclass, but unknown attributes will be ignored.
+ """
+ for (key, val) in kwargs.items():
+ # Ignore setting attributes that were removed in a subclass, as
+ # if they were unknown attributes.
+ if key in self.removedAttrs:
+ continue
+
+ if hasattr(self, key):
+ setattr(self, key, val)
+
+ def deleteRemovedAttrs(self):
+ """Remove all attributes from self that are given in the removedAttrs
+ list. This method should be called from __init__ in a subclass,
+ but only after the superclass's __init__ method has been called.
+ """
+ for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
+ delattr(self, attr)
diff --git a/mic/3rdparty/pykickstart/commands/__init__.py b/mic/3rdparty/pykickstart/commands/__init__.py
new file mode 100644
index 0000000..da48ff5
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/__init__.py
@@ -0,0 +1,26 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+import authconfig, autopart, autostep, bootloader, clearpart, device
+import deviceprobe, displaymode, dmraid, driverdisk, fcoe, firewall, firstboot
+import group, ignoredisk, interactive, iscsi, iscsiname, key, keyboard, lang
+import langsupport, lilocheck, logging, logvol, mediacheck, method, monitor
+import mouse, multipath, network, partition, raid, reboot, repo, rescue, rootpw
+import selinux, services, skipx, sshpw, timezone, updates, upgrade, user, vnc
+import volgroup, xconfig, zerombr, zfcp
diff --git a/mic/3rdparty/pykickstart/commands/authconfig.py b/mic/3rdparty/pykickstart/commands/authconfig.py
new file mode 100644
index 0000000..9af9c0f
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/authconfig.py
@@ -0,0 +1,40 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+
+class FC3_Authconfig(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, *args, **kwargs)
+ self.authconfig = kwargs.get("authconfig", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.authconfig:
+ retval += "# System authorization information\nauth %s\n" % self.authconfig
+
+ return retval
+
+ def parse(self, args):
+ self.authconfig = self.currentLine[len(self.currentCmd):].strip()
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/autopart.py b/mic/3rdparty/pykickstart/commands/autopart.py
new file mode 100644
index 0000000..cf28b5c
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/autopart.py
@@ -0,0 +1,119 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_AutoPart(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=100, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.autopart = kwargs.get("autopart", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.autopart:
+ retval += "autopart\n"
+
+ return retval
+
+ def parse(self, args):
+ if len(args) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "autopart")
+
+ self.autopart = True
+ return self
+
+class F9_AutoPart(FC3_AutoPart):
+ removedKeywords = FC3_AutoPart.removedKeywords
+ removedAttrs = FC3_AutoPart.removedAttrs
+
+ def __init__(self, writePriority=100, *args, **kwargs):
+ FC3_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs)
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ self.op = self._getParser()
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.autopart:
+ retval += "autopart"
+
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\""% self.passphrase
+
+ if retval != "":
+ retval += "\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ self.autopart = True
+ return self
+
+class F12_AutoPart(F9_AutoPart):
+ removedKeywords = F9_AutoPart.removedKeywords
+ removedAttrs = F9_AutoPart.removedAttrs
+
+ def __init__(self, writePriority=100, *args, **kwargs):
+ F9_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs)
+
+ self.escrowcert = kwargs.get("escrowcert", "")
+ self.backuppassphrase = kwargs.get("backuppassphrase", False)
+
+ def __str__(self):
+ retval = F9_AutoPart.__str__(self)
+
+ if self.encrypted and self.escrowcert != "":
+ retval = retval.strip()
+
+ retval += " --escrowcert=\"%s\"" % self.escrowcert
+
+ if self.backuppassphrase:
+ retval += " --backuppassphrase"
+
+ retval += "\n"
+
+ return retval
+
+ def _getParser(self):
+ op = F9_AutoPart._getParser(self)
+ op.add_option("--escrowcert")
+ op.add_option("--backuppassphrase", action="store_true", default=False)
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/autostep.py b/mic/3rdparty/pykickstart/commands/autostep.py
new file mode 100644
index 0000000..e6ae71c
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/autostep.py
@@ -0,0 +1,55 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+class FC3_AutoStep(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.autostep = kwargs.get("autostep", False)
+ self.autoscreenshot = kwargs.get("autoscreenshot", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.autostep:
+ if self.autoscreenshot:
+ retval += "autostep --autoscreenshot\n"
+ else:
+ retval += "autostep\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--autoscreenshot", dest="autoscreenshot",
+ action="store_true", default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ self.autostep = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/bootloader.py b/mic/3rdparty/pykickstart/commands/bootloader.py
new file mode 100644
index 0000000..b227fac
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/bootloader.py
@@ -0,0 +1,265 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+class FC3_Bootloader(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.driveorder = kwargs.get("driveorder", [])
+ self.appendLine = kwargs.get("appendLine", "")
+ self.forceLBA = kwargs.get("forceLBA", False)
+ self.linear = kwargs.get("linear", True)
+ self.location = kwargs.get("location", "")
+ self.md5pass = kwargs.get("md5pass", "")
+ self.password = kwargs.get("password", "")
+ self.upgrade = kwargs.get("upgrade", False)
+ self.useLilo = kwargs.get("useLilo", False)
+
+ self.deleteRemovedAttrs()
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.appendLine != "":
+ retval += " --append=\"%s\"" % self.appendLine
+ if self.linear:
+ retval += " --linear"
+ if self.location:
+ retval += " --location=%s" % self.location
+ if hasattr(self, "forceLBA") and self.forceLBA:
+ retval += " --lba32"
+ if self.password != "":
+ retval += " --password=\"%s\"" % self.password
+ if self.md5pass != "":
+ retval += " --md5pass=\"%s\"" % self.md5pass
+ if self.upgrade:
+ retval += " --upgrade"
+ if self.useLilo:
+ retval += " --useLilo"
+ if len(self.driveorder) > 0:
+ retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
+
+ return retval
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.location != "":
+ retval += "# System bootloader configuration\nbootloader"
+ retval += self._getArgsAsStr() + "\n"
+
+ return retval
+
+ def _getParser(self):
+ def driveorder_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = KSOptionParser()
+ op.add_option("--append", dest="appendLine")
+ op.add_option("--linear", dest="linear", action="store_true",
+ default=True)
+ op.add_option("--nolinear", dest="linear", action="store_false")
+ op.add_option("--location", dest="location", type="choice",
+ default="mbr",
+ choices=["mbr", "partition", "none", "boot"])
+ op.add_option("--lba32", dest="forceLBA", action="store_true",
+ default=False)
+ op.add_option("--password", dest="password", default="")
+ op.add_option("--md5pass", dest="md5pass", default="")
+ op.add_option("--upgrade", dest="upgrade", action="store_true",
+ default=False)
+ op.add_option("--useLilo", dest="useLilo", action="store_true",
+ default=False)
+ op.add_option("--driveorder", dest="driveorder", action="callback",
+ callback=driveorder_cb, nargs=1, type="string")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if self.currentCmd == "lilo":
+ self.useLilo = True
+
+ return self
+
+class FC4_Bootloader(FC3_Bootloader):
+ removedKeywords = FC3_Bootloader.removedKeywords + ["linear", "useLilo"]
+ removedAttrs = FC3_Bootloader.removedAttrs + ["linear", "useLilo"]
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ FC3_Bootloader.__init__(self, writePriority, *args, **kwargs)
+
+ def _getArgsAsStr(self):
+ retval = ""
+ if self.appendLine != "":
+ retval += " --append=\"%s\"" % self.appendLine
+ if self.location:
+ retval += " --location=%s" % self.location
+ if hasattr(self, "forceLBA") and self.forceLBA:
+ retval += " --lba32"
+ if self.password != "":
+ retval += " --password=\"%s\"" % self.password
+ if self.md5pass != "":
+ retval += " --md5pass=\"%s\"" % self.md5pass
+ if self.upgrade:
+ retval += " --upgrade"
+ if len(self.driveorder) > 0:
+ retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
+ return retval
+
+ def _getParser(self):
+ op = FC3_Bootloader._getParser(self)
+ op.remove_option("--linear")
+ op.remove_option("--nolinear")
+ op.remove_option("--useLilo")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
+
+class F8_Bootloader(FC4_Bootloader):
+ removedKeywords = FC4_Bootloader.removedKeywords
+ removedAttrs = FC4_Bootloader.removedAttrs
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ FC4_Bootloader.__init__(self, writePriority, *args, **kwargs)
+
+ self.timeout = kwargs.get("timeout", None)
+ self.default = kwargs.get("default", "")
+
+ def _getArgsAsStr(self):
+ ret = FC4_Bootloader._getArgsAsStr(self)
+
+ if self.timeout is not None:
+ ret += " --timeout=%d" %(self.timeout,)
+ if self.default:
+ ret += " --default=%s" %(self.default,)
+
+ return ret
+
+ def _getParser(self):
+ op = FC4_Bootloader._getParser(self)
+ op.add_option("--timeout", dest="timeout", type="int")
+ op.add_option("--default", dest="default")
+ return op
+
+class F12_Bootloader(F8_Bootloader):
+ removedKeywords = F8_Bootloader.removedKeywords
+ removedAttrs = F8_Bootloader.removedAttrs
+
+ def _getParser(self):
+ op = F8_Bootloader._getParser(self)
+ op.add_option("--lba32", dest="forceLBA", deprecated=1, action="store_true")
+ return op
+
+class F14_Bootloader(F12_Bootloader):
+ removedKeywords = F12_Bootloader.removedKeywords + ["forceLBA"]
+ removedAttrs = F12_Bootloader.removedKeywords + ["forceLBA"]
+
+ def _getParser(self):
+ op = F12_Bootloader._getParser(self)
+ op.remove_option("--lba32")
+ return op
+
+class F15_Bootloader(F14_Bootloader):
+ removedKeywords = F14_Bootloader.removedKeywords
+ removedAttrs = F14_Bootloader.removedAttrs
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ F14_Bootloader.__init__(self, writePriority, *args, **kwargs)
+
+ self.isCrypted = kwargs.get("isCrypted", False)
+
+ def _getArgsAsStr(self):
+ ret = F14_Bootloader._getArgsAsStr(self)
+
+ if self.isCrypted:
+ ret += " --iscrypted"
+
+ return ret
+
+ def _getParser(self):
+ def password_cb(option, opt_str, value, parser):
+ parser.values.isCrypted = True
+ parser.values.password = value
+
+ op = F14_Bootloader._getParser(self)
+ op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False)
+ op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string")
+ return op
+
+class RHEL5_Bootloader(FC4_Bootloader):
+ removedKeywords = FC4_Bootloader.removedKeywords
+ removedAttrs = FC4_Bootloader.removedAttrs
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ FC4_Bootloader.__init__(self, writePriority, *args, **kwargs)
+
+ self.hvArgs = kwargs.get("hvArgs", "")
+
+ def _getArgsAsStr(self):
+ ret = FC4_Bootloader._getArgsAsStr(self)
+
+ if self.hvArgs:
+ ret += " --hvargs=\"%s\"" %(self.hvArgs,)
+
+ return ret
+
+ def _getParser(self):
+ op = FC4_Bootloader._getParser(self)
+ op.add_option("--hvargs", dest="hvArgs", type="string")
+ return op
+
+class RHEL6_Bootloader(F12_Bootloader):
+ removedKeywords = F12_Bootloader.removedKeywords
+ removedAttrs = F12_Bootloader.removedAttrs
+
+ def __init__(self, writePriority=10, *args, **kwargs):
+ F12_Bootloader.__init__(self, writePriority, *args, **kwargs)
+
+ self.isCrypted = kwargs.get("isCrypted", False)
+
+ def _getArgsAsStr(self):
+ ret = F12_Bootloader._getArgsAsStr(self)
+
+ if self.isCrypted:
+ ret += " --iscrypted"
+
+ return ret
+
+ def _getParser(self):
+ def password_cb(option, opt_str, value, parser):
+ parser.values.isCrypted = True
+ parser.values.password = value
+
+ op = F12_Bootloader._getParser(self)
+ op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False)
+ op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/clearpart.py b/mic/3rdparty/pykickstart/commands/clearpart.py
new file mode 100644
index 0000000..a8089fc
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/clearpart.py
@@ -0,0 +1,86 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+class FC3_ClearPart(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=120, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.drives = kwargs.get("drives", [])
+ self.initAll = kwargs.get("initAll", False)
+ self.type = kwargs.get("type", None)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.type is None:
+ return retval
+
+ if self.type == CLEARPART_TYPE_NONE:
+ clearstr = "--none"
+ elif self.type == CLEARPART_TYPE_LINUX:
+ clearstr = "--linux"
+ elif self.type == CLEARPART_TYPE_ALL:
+ clearstr = "--all"
+ else:
+ clearstr = ""
+
+ if self.initAll:
+ initstr = "--initlabel"
+ else:
+ initstr = ""
+
+ if len(self.drives) > 0:
+ drivestr = "--drives=" + ",".join(self.drives)
+ else:
+ drivestr = ""
+
+ retval += "# Partition clearing information\nclearpart %s %s %s\n" % (clearstr, initstr, drivestr)
+ return retval
+
+ def _getParser(self):
+ def drive_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = KSOptionParser()
+ op.add_option("--all", dest="type", action="store_const",
+ const=CLEARPART_TYPE_ALL)
+ op.add_option("--drives", dest="drives", action="callback",
+ callback=drive_cb, nargs=1, type="string")
+ op.add_option("--initlabel", dest="initAll", action="store_true",
+ default=False)
+ op.add_option("--linux", dest="type", action="store_const",
+ const=CLEARPART_TYPE_LINUX)
+ op.add_option("--none", dest="type", action="store_const",
+ const=CLEARPART_TYPE_NONE)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/device.py b/mic/3rdparty/pykickstart/commands/device.py
new file mode 100644
index 0000000..321410e
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/device.py
@@ -0,0 +1,125 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F8_DeviceData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.moduleName = kwargs.get("moduleName", "")
+ self.moduleOpts = kwargs.get("moduleOpts", "")
+
+ def __eq__(self, y):
+ return self.moduleName == y.moduleName
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+
+ if self.moduleName != "":
+ retval += "device %s" % self.moduleName
+
+ if self.moduleOpts != "":
+ retval += " --opts=\"%s\"" % self.moduleOpts
+
+ return retval + "\n"
+
+class FC3_Device(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.type = kwargs.get("type", "")
+ self.moduleName = kwargs.get("moduleName", "")
+ self.moduleOpts = kwargs.get("moduleOpts", "")
+
+ def __eq__(self, y):
+ return self.moduleName == y.moduleName
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.moduleName != "":
+ retval += "device %s %s" % (self.type, self.moduleName)
+
+ if self.moduleOpts != "":
+ retval += " --opts=\"%s\"" % self.moduleOpts
+
+ return retval + "\n"
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--opts", dest="moduleOpts", default="")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 2:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("device command requires two arguments: module type and name"))
+
+ self.moduleOpts = opts.moduleOpts
+ self.type = extra[0]
+ self.moduleName = extra[1]
+ return self
+
+class F8_Device(FC3_Device):
+ removedKeywords = FC3_Device.removedKeywords
+ removedAttrs = FC3_Device.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_Device.__init__(self, writePriority, *args, **kwargs)
+ self.deviceList = kwargs.get("deviceList", [])
+
+ def __str__(self):
+ retval = ""
+ for device in self.deviceList:
+ retval += device.__str__()
+
+ return retval
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("%s command requires a single argument: %s") % ("device", "module name"))
+
+ dd = F8_DeviceData()
+ self._setToObj(self.op, opts, dd)
+ dd.lineno = self.lineno
+ dd.moduleName = extra[0]
+
+ # Check for duplicates in the data list.
+ if dd in self.dataList():
+ warnings.warn(_("A module with the name %s has already been defined.") % dd.moduleName)
+
+ return dd
+
+ def dataList(self):
+ return self.deviceList
diff --git a/mic/3rdparty/pykickstart/commands/deviceprobe.py b/mic/3rdparty/pykickstart/commands/deviceprobe.py
new file mode 100644
index 0000000..9f462fd
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/deviceprobe.py
@@ -0,0 +1,40 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+
+class FC3_DeviceProbe(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.deviceprobe = kwargs.get("deviceprobe", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.deviceprobe != "":
+ retval += "deviceprobe %s\n" % self.deviceprobe
+
+ return retval
+
+ def parse(self, args):
+ self.deviceprobe = " ".join(args)
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/displaymode.py b/mic/3rdparty/pykickstart/commands/displaymode.py
new file mode 100644
index 0000000..6a12d58
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/displaymode.py
@@ -0,0 +1,68 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_DisplayMode(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.displayMode = kwargs.get("displayMode", None)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.displayMode is None:
+ return retval
+
+ if self.displayMode == DISPLAY_MODE_CMDLINE:
+ retval += "cmdline\n"
+ elif self.displayMode == DISPLAY_MODE_GRAPHICAL:
+ retval += "# Use graphical install\ngraphical\n"
+ elif self.displayMode == DISPLAY_MODE_TEXT:
+ retval += "# Use text mode install\ntext\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 0:
+ raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % self.currentCmd)
+
+ if self.currentCmd == "cmdline":
+ self.displayMode = DISPLAY_MODE_CMDLINE
+ elif self.currentCmd == "graphical":
+ self.displayMode = DISPLAY_MODE_GRAPHICAL
+ elif self.currentCmd == "text":
+ self.displayMode = DISPLAY_MODE_TEXT
+
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/dmraid.py b/mic/3rdparty/pykickstart/commands/dmraid.py
new file mode 100644
index 0000000..993575a
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/dmraid.py
@@ -0,0 +1,91 @@
+#
+# Chris Lumens <clumens@redhat.com>
+# Peter Jones <pjones@redhat.com>
+#
+# Copyright 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_DmRaidData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+
+ self.name = kwargs.get("name", "")
+ self.devices = kwargs.get("devices", [])
+ self.dmset = kwargs.get("dmset", None)
+
+ def __eq__(self, y):
+ return self.name == y.name and self.devices == y.devices
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "dmraid --name=%s" % self.name
+
+ for dev in self.devices:
+ retval += " --dev=\"%s\"" % dev
+
+ return retval + "\n"
+
+class FC6_DmRaid(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=60, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.dmraids = kwargs.get("dmraids", [])
+
+ def __str__(self):
+ retval = ""
+ for dm in self.dmraids:
+ retval += dm.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--name", dest="name", action="store", type="string",
+ required=1)
+ op.add_option("--dev", dest="devices", action="append", type="string",
+ required=1)
+ return op
+
+ def parse(self, args):
+ dm = FC6_DmRaidData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ dm.name = dm.name.split('/')[-1]
+ self._setToObj(self.op, opts, dm)
+ dm.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if dm in self.dataList():
+ warnings.warn(_("A DM RAID device with the name %s and devices %s has already been defined.") % (dm.name, dm.devices))
+
+ return dm
+
+ def dataList(self):
+ return self.dmraids
diff --git a/mic/3rdparty/pykickstart/commands/driverdisk.py b/mic/3rdparty/pykickstart/commands/driverdisk.py
new file mode 100644
index 0000000..82a58c0
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/driverdisk.py
@@ -0,0 +1,184 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_DriverDiskData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+
+ self.partition = kwargs.get("partition", "")
+ self.source = kwargs.get("source", "")
+ self.type = kwargs.get("type", "")
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.partition:
+ retval += "%s" % self.partition
+
+ if hasattr(self, "type") and self.type:
+ retval += " --type=%s" % self.type
+ elif self.source:
+ retval += "--source=%s" % self.source
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "driverdisk %s\n" % self._getArgsAsStr()
+ return retval
+
+class FC4_DriverDiskData(FC3_DriverDiskData):
+ removedKeywords = FC3_DriverDiskData.removedKeywords
+ removedAttrs = FC3_DriverDiskData.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_DriverDiskData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.biospart = kwargs.get("biospart", "")
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.partition:
+ retval += "%s" % self.partition
+
+ if hasattr(self, "type") and self.type:
+ retval += " --type=%s" % self.type
+ elif self.source:
+ retval += "--source=%s" % self.source
+ elif self.biospart:
+ retval += "--biospart=%s" % self.biospart
+
+ return retval
+
+class F12_DriverDiskData(FC4_DriverDiskData):
+ removedKeywords = FC4_DriverDiskData.removedKeywords + ["type"]
+ removedAttrs = FC4_DriverDiskData.removedAttrs + ["type"]
+
+ def __init__(self, *args, **kwargs):
+ FC4_DriverDiskData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+F14_DriverDiskData = F12_DriverDiskData
+
+class FC3_DriverDisk(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.driverdiskList = kwargs.get("driverdiskList", [])
+
+ def __str__(self):
+ retval = ""
+ for dd in self.driverdiskList:
+ retval += dd.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--source")
+ op.add_option("--type")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command."))
+
+ if len(extra) == 1 and opts.source:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command."))
+
+ if not extra and not opts.source:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source or partition must be specified for driverdisk command."))
+
+ ddd = self.handler.DriverDiskData()
+ self._setToObj(self.op, opts, ddd)
+ ddd.lineno = self.lineno
+ if len(extra) == 1:
+ ddd.partition = extra[0]
+
+ return ddd
+
+ def dataList(self):
+ return self.driverdiskList
+
+class FC4_DriverDisk(FC3_DriverDisk):
+ removedKeywords = FC3_DriverDisk.removedKeywords
+ removedAttrs = FC3_DriverDisk.removedKeywords
+
+ def _getParser(self):
+ op = FC3_DriverDisk._getParser(self)
+ op.add_option("--biospart")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command."))
+
+ if len(extra) == 1 and opts.source:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command."))
+ elif len(extra) == 1 and opts.biospart:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and partition may be specified for driverdisk command."))
+ elif opts.source and opts.biospart:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and --source may be specified for driverdisk command."))
+
+ if not extra and not opts.source and not opts.biospart:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source, --biospart, or partition must be specified for driverdisk command."))
+
+ ddd = self.handler.DriverDiskData()
+ self._setToObj(self.op, opts, ddd)
+ ddd.lineno = self.lineno
+ if len(extra) == 1:
+ ddd.partition = extra[0]
+
+ return ddd
+
+class F12_DriverDisk(FC4_DriverDisk):
+ removedKeywords = FC4_DriverDisk.removedKeywords
+ removedAttrs = FC4_DriverDisk.removedKeywords
+
+ def _getParser(self):
+ op = FC4_DriverDisk._getParser(self)
+ op.add_option("--type", deprecated=1)
+ return op
+
+class F14_DriverDisk(F12_DriverDisk):
+ removedKeywords = F12_DriverDisk.removedKeywords
+ removedAttrs = F12_DriverDisk.removedKeywords
+
+ def _getParser(self):
+ op = F12_DriverDisk._getParser(self)
+ op.remove_option("--type")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/fcoe.py b/mic/3rdparty/pykickstart/commands/fcoe.py
new file mode 100644
index 0000000..3320849
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/fcoe.py
@@ -0,0 +1,114 @@
+#
+# Hans de Goede <hdegoede@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F12_FcoeData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.nic = kwargs.get("nic", None)
+
+ def __eq__(self, y):
+ return self.nic == y.nic
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.nic:
+ retval += " --nic=%s" % self.nic
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "fcoe%s\n" % self._getArgsAsStr()
+ return retval
+
+class F13_FcoeData(F12_FcoeData):
+ removedKeywords = F12_FcoeData.removedKeywords
+ removedAttrs = F12_FcoeData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F12_FcoeData.__init__(self, *args, **kwargs)
+ self.dcb = kwargs.get("dcb", False)
+
+ def _getArgsAsStr(self):
+ retval = F12_FcoeData._getArgsAsStr(self)
+
+ if self.dcb:
+ retval += " --dcb"
+
+ return retval
+
+class F12_Fcoe(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=71, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.fcoe = kwargs.get("fcoe", [])
+
+ def __str__(self):
+ retval = ""
+ for fcoe in self.fcoe:
+ retval += fcoe.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--nic", dest="nic", required=1)
+ return op
+
+ def parse(self, args):
+ zd = self.handler.FcoeData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) > 0:
+ mapping = {"command": "fcoe", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
+
+ self._setToObj(self.op, opts, zd)
+ zd.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if zd in self.dataList():
+ warnings.warn(_("A FCOE device with the name %s has already been defined.") % zd.nic)
+
+ return zd
+
+ def dataList(self):
+ return self.fcoe
+
+class F13_Fcoe(F12_Fcoe):
+ removedKeywords = F12_Fcoe.removedKeywords
+ removedAttrs = F12_Fcoe.removedAttrs
+
+ def _getParser(self):
+ op = F12_Fcoe._getParser(self)
+ op.add_option("--dcb", dest="dcb", action="store_true", default=False)
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/firewall.py b/mic/3rdparty/pykickstart/commands/firewall.py
new file mode 100644
index 0000000..24a01bd
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/firewall.py
@@ -0,0 +1,193 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Firewall(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.enabled = kwargs.get("enabled", None)
+ self.ports = kwargs.get("ports", [])
+ self.trusts = kwargs.get("trusts", [])
+
+ def __str__(self):
+ extra = []
+ filteredPorts = []
+
+ retval = KickstartCommand.__str__(self)
+
+ if self.enabled is None:
+ return retval
+
+ if self.enabled:
+ # It's possible we have words in the ports list instead of
+ # port:proto (s-c-kickstart may do this). So, filter those
+ # out into their own list leaving what we expect.
+ for port in self.ports:
+ if port == "ssh":
+ extra.append(" --ssh")
+ elif port == "telnet":
+ extra.append(" --telnet")
+ elif port == "smtp":
+ extra.append(" --smtp")
+ elif port == "http":
+ extra.append(" --http")
+ elif port == "ftp":
+ extra.append(" --ftp")
+ else:
+ filteredPorts.append(port)
+
+ # All the port:proto strings go into a comma-separated list.
+ portstr = ",".join(filteredPorts)
+ if len(portstr) > 0:
+ portstr = " --port=" + portstr
+ else:
+ portstr = ""
+
+ extrastr = "".join(extra)
+ truststr = ",".join(self.trusts)
+
+ if len(truststr) > 0:
+ truststr = " --trust=" + truststr
+
+ # The output port list consists only of port:proto for
+ # everything that we don't recognize, and special options for
+ # those that we do.
+ retval += "# Firewall configuration\nfirewall --enabled%s%s%s\n" % (extrastr, portstr, truststr)
+ else:
+ retval += "# Firewall configuration\nfirewall --disabled\n"
+
+ return retval
+
+ def _getParser(self):
+ def firewall_port_cb (option, opt_str, value, parser):
+ for p in value.split(","):
+ p = p.strip()
+ if p.find(":") == -1:
+ p = "%s:tcp" % p
+ parser.values.ensure_value(option.dest, []).append(p)
+
+ op = KSOptionParser(mapping={"ssh":["22:tcp"], "telnet":["23:tcp"],
+ "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
+ "ftp":["21:tcp"]})
+
+ op.add_option("--disable", "--disabled", dest="enabled",
+ action="store_false")
+ op.add_option("--enable", "--enabled", dest="enabled",
+ action="store_true", default=True)
+ op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
+ dest="ports", action="map_extend")
+ op.add_option("--high", deprecated=1)
+ op.add_option("--medium", deprecated=1)
+ op.add_option("--port", dest="ports", action="callback",
+ callback=firewall_port_cb, nargs=1, type="string")
+ op.add_option("--trust", dest="trusts", action="append")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 0:
+ mapping = {"command": "firewall", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
+
+ self._setToSelf(self.op, opts)
+ return self
+
+class F9_Firewall(FC3_Firewall):
+ removedKeywords = FC3_Firewall.removedKeywords
+ removedAttrs = FC3_Firewall.removedAttrs
+
+ def _getParser(self):
+ op = FC3_Firewall._getParser(self)
+ op.remove_option("--high")
+ op.remove_option("--medium")
+ return op
+
+class F10_Firewall(F9_Firewall):
+ removedKeywords = F9_Firewall.removedKeywords
+ removedAttrs = F9_Firewall.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F9_Firewall.__init__(self, writePriority, *args, **kwargs)
+ self.services = kwargs.get("services", [])
+
+ def __str__(self):
+ if self.enabled is None:
+ return ""
+
+ retval = F9_Firewall.__str__(self)
+ if self.enabled:
+ retval = retval.strip()
+
+ svcstr = ",".join(self.services)
+ if len(svcstr) > 0:
+ svcstr = " --service=" + svcstr
+ else:
+ svcstr = ""
+
+ return retval + "%s\n" % svcstr
+ else:
+ return retval
+
+ def _getParser(self):
+ def service_cb (option, opt_str, value, parser):
+ # python2.4 does not support action="append_const" that we were
+ # using for these options. Instead, we have to fake it by
+ # appending whatever the option string is to the service list.
+ if not value:
+ parser.values.ensure_value(option.dest, []).append(opt_str[2:])
+ return
+
+ for p in value.split(","):
+ p = p.strip()
+ parser.values.ensure_value(option.dest, []).append(p)
+
+ op = F9_Firewall._getParser(self)
+ op.add_option("--service", dest="services", action="callback",
+ callback=service_cb, nargs=1, type="string")
+ op.add_option("--ftp", dest="services", action="callback",
+ callback=service_cb)
+ op.add_option("--http", dest="services", action="callback",
+ callback=service_cb)
+ op.add_option("--smtp", dest="services", action="callback",
+ callback=service_cb)
+ op.add_option("--ssh", dest="services", action="callback",
+ callback=service_cb)
+ op.add_option("--telnet", deprecated=1)
+ return op
+
+class F14_Firewall(F10_Firewall):
+ removedKeywords = F10_Firewall.removedKeywords + ["telnet"]
+ removedAttrs = F10_Firewall.removedAttrs + ["telnet"]
+
+ def _getParser(self):
+ op = F10_Firewall._getParser(self)
+ op.remove_option("--telnet")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/firstboot.py b/mic/3rdparty/pykickstart/commands/firstboot.py
new file mode 100644
index 0000000..05c0ac1
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/firstboot.py
@@ -0,0 +1,62 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.options import *
+
+class FC3_Firstboot(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.firstboot = kwargs.get("firstboot", None)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.firstboot is None:
+ return retval
+
+ if self.firstboot == FIRSTBOOT_SKIP:
+ retval += "firstboot --disable\n"
+ elif self.firstboot == FIRSTBOOT_DEFAULT:
+ retval += "# Run the Setup Agent on first boot\nfirstboot --enable\n"
+ elif self.firstboot == FIRSTBOOT_RECONFIG:
+ retval += "# Run the Setup Agent on first boot\nfirstboot --reconfig\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--disable", "--disabled", dest="firstboot",
+ action="store_const", const=FIRSTBOOT_SKIP)
+ op.add_option("--enable", "--enabled", dest="firstboot",
+ action="store_const", const=FIRSTBOOT_DEFAULT)
+ op.add_option("--reconfig", dest="firstboot", action="store_const",
+ const=FIRSTBOOT_RECONFIG)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self.firstboot = opts.firstboot
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/group.py b/mic/3rdparty/pykickstart/commands/group.py
new file mode 100644
index 0000000..80ba5bd
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/group.py
@@ -0,0 +1,88 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F12_GroupData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.name = kwargs.get("name", "")
+ self.gid = kwargs.get("gid", None)
+
+ def __eq__(self, y):
+ return self.name == y.name
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "group"
+
+ if self.name:
+ retval += " --name=%s" % self.name
+ if self.gid:
+ retval += " --gid=%s" % self.gid
+
+ return retval + "\n"
+
+class F12_Group(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.groupList = kwargs.get("groupList", [])
+
+ def __str__(self):
+ retval = ""
+ for user in self.groupList:
+ retval += user.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--name", required=1)
+ op.add_option("--gid", type="int")
+ return op
+
+ def parse(self, args):
+ gd = self.handler.GroupData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToObj(self.op, opts, gd)
+ gd.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if gd in self.dataList():
+ warnings.warn(_("A group with the name %s has already been defined.") % gd.name)
+
+ return gd
+
+ def dataList(self):
+ return self.groupList
diff --git a/mic/3rdparty/pykickstart/commands/ignoredisk.py b/mic/3rdparty/pykickstart/commands/ignoredisk.py
new file mode 100644
index 0000000..676d080
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/ignoredisk.py
@@ -0,0 +1,139 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_IgnoreDisk(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.ignoredisk = kwargs.get("ignoredisk", [])
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if len(self.ignoredisk) > 0:
+ retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk)
+
+ return retval
+
+ def _getParser(self):
+ def drive_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = KSOptionParser()
+ op.add_option("--drives", dest="ignoredisk", action="callback",
+ callback=drive_cb, nargs=1, type="string", required=1)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
+
+class F8_IgnoreDisk(FC3_IgnoreDisk):
+ removedKeywords = FC3_IgnoreDisk.removedKeywords
+ removedAttrs = FC3_IgnoreDisk.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_IgnoreDisk.__init__(self, writePriority, *args, **kwargs)
+
+ self.onlyuse = kwargs.get("onlyuse", [])
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if len(self.ignoredisk) > 0:
+ retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk)
+ elif len(self.onlyuse) > 0:
+ retval += "ignoredisk --only-use=%s\n" % ",".join(self.onlyuse)
+
+ return retval
+
+ def parse(self, args, errorCheck=True):
+ retval = FC3_IgnoreDisk.parse(self, args)
+
+ if errorCheck:
+ if (len(self.ignoredisk) == 0 and len(self.onlyuse) == 0) or (len(self.ignoredisk) > 0 and (len(self.onlyuse) > 0)):
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives or --only-use must be specified for ignoredisk command."))
+
+ return retval
+
+ def _getParser(self):
+ def drive_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = FC3_IgnoreDisk._getParser(self)
+ op.add_option("--drives", dest="ignoredisk", action="callback",
+ callback=drive_cb, nargs=1, type="string")
+ op.add_option("--only-use", dest="onlyuse", action="callback",
+ callback=drive_cb, nargs=1, type="string")
+ return op
+
+class RHEL6_IgnoreDisk(F8_IgnoreDisk):
+ removedKeywords = F8_IgnoreDisk.removedKeywords
+ removedAttrs = F8_IgnoreDisk.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F8_IgnoreDisk.__init__(self, writePriority, *args, **kwargs)
+
+ self.interactive = kwargs.get("interactive", False)
+ if self.interactive:
+ self.ignoredisk = []
+
+ def __str__(self):
+ retval = F8_IgnoreDisk.__str__(self)
+
+ if self.interactive:
+ retval = "ignoredisk --interactive\n"
+
+ return retval
+
+ def parse(self, args):
+ retval = F8_IgnoreDisk.parse(self, args, errorCheck=False)
+
+ howmany = 0
+ if len(self.ignoredisk) > 0:
+ howmany += 1
+ if len(self.onlyuse) > 0:
+ howmany += 1
+ if self.interactive:
+ howmany += 1
+ if howmany != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives , --only-use , or --interactive must be specified for ignoredisk command."))
+
+ return retval
+
+ def _getParser(self):
+ op = F8_IgnoreDisk._getParser(self)
+ op.add_option("--interactive", dest="interactive", action="store_true",
+ default=False)
+ return op
+
+F14_IgnoreDisk = RHEL6_IgnoreDisk
diff --git a/mic/3rdparty/pykickstart/commands/interactive.py b/mic/3rdparty/pykickstart/commands/interactive.py
new file mode 100644
index 0000000..fa3dc02
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/interactive.py
@@ -0,0 +1,58 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Interactive(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.interactive = kwargs.get("interactive", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.interactive:
+ retval += "# Use interactive kickstart installation method\ninteractive\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "interactive")
+
+ self.interactive = True
+ return self
+
+class F14_Interactive(DeprecatedCommand):
+ def __init__(self):
+ DeprecatedCommand.__init__(self)
diff --git a/mic/3rdparty/pykickstart/commands/iscsi.py b/mic/3rdparty/pykickstart/commands/iscsi.py
new file mode 100644
index 0000000..da5a544
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/iscsi.py
@@ -0,0 +1,133 @@
+#
+# Chris Lumens <clumens@redhat.com>
+# Peter Jones <pjones@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_IscsiData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.ipaddr = kwargs.get("ipaddr", "")
+ self.port = kwargs.get("port", "3260")
+ self.target = kwargs.get("target", "")
+ self.user = kwargs.get("user", None)
+ self.password = kwargs.get("password", None)
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.target != "":
+ retval += " --target=%s" % self.target
+ if self.ipaddr != "":
+ retval += " --ipaddr=%s" % self.ipaddr
+ if self.port != "3260":
+ retval += " --port=%s" % self.port
+ if self.user is not None:
+ retval += " --user=%s" % self.user
+ if self.password is not None:
+ retval += " --password=%s" % self.password
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "iscsi%s\n" % self._getArgsAsStr()
+ return retval
+
+class F10_IscsiData(FC6_IscsiData):
+ removedKeywords = FC6_IscsiData.removedKeywords
+ removedAttrs = FC6_IscsiData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC6_IscsiData.__init__(self, *args, **kwargs)
+ self.user_in = kwargs.get("user_in", None)
+ self.password_in = kwargs.get("password_in", None)
+
+ def _getArgsAsStr(self):
+ retval = FC6_IscsiData._getArgsAsStr(self)
+
+ if self.user_in is not None:
+ retval += " --reverse-user=%s" % self.user_in
+ if self.password_in is not None:
+ retval += " --reverse-password=%s" % self.password_in
+
+ return retval
+
+class FC6_Iscsi(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=71, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.iscsi = kwargs.get("iscsi", [])
+
+ def __str__(self):
+ retval = ""
+ for iscsi in self.iscsi:
+ retval += iscsi.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--target", dest="target", action="store", type="string")
+ op.add_option("--ipaddr", dest="ipaddr", action="store", type="string",
+ required=1)
+ op.add_option("--port", dest="port", action="store", type="string")
+ op.add_option("--user", dest="user", action="store", type="string")
+ op.add_option("--password", dest="password", action="store",
+ type="string")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 0:
+ mapping = {"command": "iscsi", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
+
+ dd = self.handler.IscsiData()
+ self._setToObj(self.op, opts, dd)
+ dd.lineno = self.lineno
+ return dd
+
+ def dataList(self):
+ return self.iscsi
+
+class F10_Iscsi(FC6_Iscsi):
+ removedKeywords = FC6_Iscsi.removedKeywords
+ removedAttrs = FC6_Iscsi.removedAttrs
+
+ def _getParser(self):
+ op = FC6_Iscsi._getParser(self)
+ op.add_option("--reverse-user", dest="user_in", action="store",
+ type="string")
+ op.add_option("--reverse-password", dest="password_in", action="store",
+ type="string")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/iscsiname.py b/mic/3rdparty/pykickstart/commands/iscsiname.py
new file mode 100644
index 0000000..a87d063
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/iscsiname.py
@@ -0,0 +1,54 @@
+#
+# Chris Lumens <clumens@redhat.com>
+# Peter Jones <pjones@redhat.com>
+#
+# Copyright 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_IscsiName(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=70, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.iscsiname = kwargs.get("iscsiname", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.iscsiname != "":
+ retval += "iscsiname %s\n" % self.iscsiname
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "iscsiname")
+ self.iscsiname = extra[0]
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/key.py b/mic/3rdparty/pykickstart/commands/key.py
new file mode 100644
index 0000000..c20c423
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/key.py
@@ -0,0 +1,64 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class RHEL5_Key(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.key = kwargs.get("key", "")
+ self.skip = kwargs.get("skip", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.key == KS_INSTKEY_SKIP:
+ retval += "key --skip\n"
+ elif self.key != "":
+ retval += "key %s\n" % self.key
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--skip", action="store_true", default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if self.skip:
+ self.key = KS_INSTKEY_SKIP
+ elif len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "key")
+ else:
+ self.key = extra[0]
+
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/keyboard.py b/mic/3rdparty/pykickstart/commands/keyboard.py
new file mode 100644
index 0000000..babc2ac
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/keyboard.py
@@ -0,0 +1,55 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Keyboard(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.keyboard = kwargs.get("keyboard", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.keyboard != "":
+ retval += "# System keyboard\nkeyboard %s\n" % self.keyboard
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "keyboard")
+
+ self.keyboard = extra[0]
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/lang.py b/mic/3rdparty/pykickstart/commands/lang.py
new file mode 100644
index 0000000..cf5e46c
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/lang.py
@@ -0,0 +1,60 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Lang(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.lang = kwargs.get("lang", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.lang != "":
+ retval += "# System language\nlang %s\n" % self.lang
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "lang")
+
+ self.lang = extra[0]
+ return self
+
+ def apply(self, instroot="/"):
+ if self.lang == "": return
+ f = open(instroot + "/etc/sysconfig/i18n", "w+")
+ f.write("LANG=\"%s\"\n" %(self.lang,))
+ f.close()
diff --git a/mic/3rdparty/pykickstart/commands/langsupport.py b/mic/3rdparty/pykickstart/commands/langsupport.py
new file mode 100644
index 0000000..73a9e53
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/langsupport.py
@@ -0,0 +1,58 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+class FC3_LangSupport(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.deflang = kwargs.get("deflang", "")
+ self.supported = kwargs.get("supported", [])
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.deflang:
+ retval += "langsupport --default=%s" % self.deflang
+
+ if self.supported:
+ retval += " %s" % " ".join(self.supported)
+
+ return retval + "\n"
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--default", dest="deflang", default="en_US.UTF-8")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ self.supported = extra
+ return self
+
+class FC5_LangSupport(DeprecatedCommand):
+ def __init__(self):
+ DeprecatedCommand.__init__(self)
diff --git a/mic/3rdparty/pykickstart/commands/lilocheck.py b/mic/3rdparty/pykickstart/commands/lilocheck.py
new file mode 100644
index 0000000..92b3f93
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/lilocheck.py
@@ -0,0 +1,54 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_LiloCheck(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.check = kwargs.get("check", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.check:
+ retval += "lilocheck\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "lilocheck")
+
+ self.check = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/logging.py b/mic/3rdparty/pykickstart/commands/logging.py
new file mode 100644
index 0000000..6985619
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/logging.py
@@ -0,0 +1,66 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007, 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_Logging(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.host = kwargs.get("host", "")
+ self.level = kwargs.get("level", "info")
+ self.port = kwargs.get("port", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+ retval += "# Installation logging level\nlogging --level=%s" % self.level
+
+ if self.host != "":
+ retval += " --host=%s" % self.host
+
+ if self.port != "":
+ retval += " --port=%s" % self.port
+
+ return retval + "\n"
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--host")
+ op.add_option("--level", type="choice", default="info",
+ choices=["debug", "info", "warning", "error", "critical"])
+ op.add_option("--port")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if opts.port and not opts.host:
+ raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Can't specify --port without --host."))
+
+ self._setToSelf(self.op, opts)
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/logvol.py b/mic/3rdparty/pykickstart/commands/logvol.py
new file mode 100644
index 0000000..c1b9cc3
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/logvol.py
@@ -0,0 +1,304 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_LogVolData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.fstype = kwargs.get("fstype", "")
+ self.grow = kwargs.get("grow", False)
+ self.maxSizeMB = kwargs.get("maxSizeMB", 0)
+ self.name = kwargs.get("name", "")
+ self.format = kwargs.get("format", True)
+ self.percent = kwargs.get("percent", 0)
+ self.recommended = kwargs.get("recommended", False)
+ self.size = kwargs.get("size", None)
+ self.preexist = kwargs.get("preexist", False)
+ self.vgname = kwargs.get("vgname", "")
+ self.mountpoint = kwargs.get("mountpoint", "")
+
+ def __eq__(self, y):
+ return self.vgname == y.vgname and self.name == y.name
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.fstype != "":
+ retval += " --fstype=\"%s\"" % self.fstype
+ if self.grow:
+ retval += " --grow"
+ if self.maxSizeMB > 0:
+ retval += " --maxsize=%d" % self.maxSizeMB
+ if not self.format:
+ retval += " --noformat"
+ if self.percent > 0:
+ retval += " --percent=%d" % self.percent
+ if self.recommended:
+ retval += " --recommended"
+ if self.size > 0:
+ retval += " --size=%d" % self.size
+ if self.preexist:
+ retval += " --useexisting"
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "logvol %s %s --name=%s --vgname=%s\n" % (self.mountpoint, self._getArgsAsStr(), self.name, self.vgname)
+ return retval
+
+class FC4_LogVolData(FC3_LogVolData):
+ removedKeywords = FC3_LogVolData.removedKeywords
+ removedAttrs = FC3_LogVolData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC3_LogVolData.__init__(self, *args, **kwargs)
+ self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
+ self.fsopts = kwargs.get("fsopts", "")
+
+ def _getArgsAsStr(self):
+ retval = FC3_LogVolData._getArgsAsStr(self)
+
+ if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
+ retval += " --bytes-per-inode=%d" % self.bytesPerInode
+ if self.fsopts != "":
+ retval += " --fsoptions=\"%s\"" % self.fsopts
+
+ return retval
+
+class RHEL5_LogVolData(FC4_LogVolData):
+ removedKeywords = FC4_LogVolData.removedKeywords
+ removedAttrs = FC4_LogVolData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC4_LogVolData.__init__(self, *args, **kwargs)
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC4_LogVolData._getArgsAsStr(self)
+
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+class F9_LogVolData(FC4_LogVolData):
+ removedKeywords = FC4_LogVolData.removedKeywords + ["bytesPerInode"]
+ removedAttrs = FC4_LogVolData.removedAttrs + ["bytesPerInode"]
+
+ def __init__(self, *args, **kwargs):
+ FC4_LogVolData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.fsopts = kwargs.get("fsopts", "")
+ self.fsprofile = kwargs.get("fsprofile", "")
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC4_LogVolData._getArgsAsStr(self)
+
+ if self.fsprofile != "":
+ retval += " --fsprofile=\"%s\"" % self.fsprofile
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+class F12_LogVolData(F9_LogVolData):
+ removedKeywords = F9_LogVolData.removedKeywords
+ removedAttrs = F9_LogVolData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F9_LogVolData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.escrowcert = kwargs.get("escrowcert", "")
+ self.backuppassphrase = kwargs.get("backuppassphrase", False)
+
+ def _getArgsAsStr(self):
+ retval = F9_LogVolData._getArgsAsStr(self)
+
+ if self.encrypted and self.escrowcert != "":
+ retval += " --escrowcert=\"%s\"" % self.escrowcert
+
+ if self.backuppassphrase:
+ retval += " --backuppassphrase"
+
+ return retval
+
+F14_LogVolData = F12_LogVolData
+
+class F15_LogVolData(F14_LogVolData):
+ removedKeywords = F14_LogVolData.removedKeywords
+ removedAttrs = F14_LogVolData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F14_LogVolData.__init__(self, *args, **kwargs)
+ self.label = kwargs.get("label", "")
+
+ def _getArgsAsStr(self):
+ retval = F14_LogVolData._getArgsAsStr(self)
+
+ if self.label != "":
+ retval += " --label=\"%s\"" % self.label
+
+ return retval
+
+class FC3_LogVol(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=133, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.lvList = kwargs.get("lvList", [])
+
+ def __str__(self):
+ retval = ""
+
+ for part in self.lvList:
+ retval += part.__str__()
+
+ return retval
+
+ def _getParser(self):
+ def lv_cb (option, opt_str, value, parser):
+ parser.values.format = False
+ parser.values.preexist = True
+
+ op = KSOptionParser()
+ op.add_option("--fstype", dest="fstype")
+ op.add_option("--grow", dest="grow", action="store_true",
+ default=False)
+ op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
+ nargs=1)
+ op.add_option("--name", dest="name", required=1)
+ op.add_option("--noformat", action="callback", callback=lv_cb,
+ dest="format", default=True, nargs=0)
+ op.add_option("--percent", dest="percent", action="store", type="int",
+ nargs=1)
+ op.add_option("--recommended", dest="recommended", action="store_true",
+ default=False)
+ op.add_option("--size", dest="size", action="store", type="int",
+ nargs=1)
+ op.add_option("--useexisting", dest="preexist", action="store_true",
+ default=False)
+ op.add_option("--vgname", dest="vgname", required=1)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) == 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "logvol")
+
+ lvd = self.handler.LogVolData()
+ self._setToObj(self.op, opts, lvd)
+ lvd.lineno = self.lineno
+ lvd.mountpoint=extra[0]
+
+ # Check for duplicates in the data list.
+ if lvd in self.dataList():
+ warnings.warn(_("A logical volume with the name %s has already been defined in volume group %s.") % (lvd.device, lvd.vgname))
+
+ return lvd
+
+ def dataList(self):
+ return self.lvList
+
+class FC4_LogVol(FC3_LogVol):
+ removedKeywords = FC3_LogVol.removedKeywords
+ removedAttrs = FC3_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = FC3_LogVol._getParser(self)
+ op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
+ type="int", nargs=1)
+ op.add_option("--fsoptions", dest="fsopts")
+ return op
+
+class RHEL5_LogVol(FC4_LogVol):
+ removedKeywords = FC4_LogVol.removedKeywords
+ removedAttrs = FC4_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = FC4_LogVol._getParser(self)
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F9_LogVol(FC4_LogVol):
+ removedKeywords = FC4_LogVol.removedKeywords
+ removedAttrs = FC4_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = FC4_LogVol._getParser(self)
+ op.add_option("--bytes-per-inode", deprecated=1)
+ op.add_option("--fsprofile", dest="fsprofile", action="store",
+ type="string", nargs=1)
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F12_LogVol(F9_LogVol):
+ removedKeywords = F9_LogVol.removedKeywords
+ removedAttrs = F9_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = F9_LogVol._getParser(self)
+ op.add_option("--escrowcert")
+ op.add_option("--backuppassphrase", action="store_true", default=False)
+ return op
+
+class F14_LogVol(F12_LogVol):
+ removedKeywords = F12_LogVol.removedKeywords
+ removedAttrs = F12_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = F12_LogVol._getParser(self)
+ op.remove_option("--bytes-per-inode")
+ return op
+
+class F15_LogVol(F14_LogVol):
+ removedKeywords = F14_LogVol.removedKeywords
+ removedAttrs = F14_LogVol.removedAttrs
+
+ def _getParser(self):
+ op = F14_LogVol._getParser(self)
+ op.add_option("--label")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/mediacheck.py b/mic/3rdparty/pykickstart/commands/mediacheck.py
new file mode 100644
index 0000000..388823a
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/mediacheck.py
@@ -0,0 +1,53 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC4_MediaCheck(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.mediacheck = kwargs.get("mediacheck", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+ if self.mediacheck:
+ retval += "mediacheck\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "mediacheck")
+
+ self.mediacheck = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/method.py b/mic/3rdparty/pykickstart/commands/method.py
new file mode 100644
index 0000000..e21064a
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/method.py
@@ -0,0 +1,186 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007, 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Method(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.method = kwargs.get("method", "")
+
+ # Set all these attributes so calls to this command's __call__
+ # method can set them. However we don't want to provide them as
+ # arguments to __init__ because method is special.
+ self.biospart = None
+ self.partition = None
+ self.server = None
+ self.dir = None
+ self.url = None
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.method == "cdrom":
+ retval += "# Use CDROM installation media\ncdrom\n"
+ elif self.method == "harddrive":
+ msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir
+
+ if self.biospart is not None:
+ retval += msg + " --biospart=%s\n" % self.biospart
+ else:
+ retval += msg + " --partition=%s\n" % self.partition
+ elif self.method == "nfs":
+ retval += "# Use NFS installation media\nnfs --server=%s --dir=%s\n" % (self.server, self.dir)
+ elif self.method == "url":
+ retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+
+ # method = "cdrom" falls through to the return
+ if self.currentCmd == "harddrive":
+ op.add_option("--biospart", dest="biospart")
+ op.add_option("--partition", dest="partition")
+ op.add_option("--dir", dest="dir", required=1)
+ elif self.currentCmd == "nfs":
+ op.add_option("--server", dest="server", required=1)
+ op.add_option("--dir", dest="dir", required=1)
+ elif self.currentCmd == "url":
+ op.add_option("--url", dest="url", required=1)
+
+ return op
+
+ def parse(self, args):
+ self.method = self.currentCmd
+
+ op = self._getParser()
+ (opts, extra) = op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(op, opts)
+
+ if self.currentCmd == "harddrive":
+ if self.biospart is None and self.partition is None or \
+ self.biospart is not None and self.partition is not None:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of biospart or partition options must be specified."))
+
+ return self
+
+class FC6_Method(FC3_Method):
+ removedKeywords = FC3_Method.removedKeywords
+ removedAttrs = FC3_Method.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_Method.__init__(self, writePriority, *args, **kwargs)
+
+ # Same reason for this attribute as the comment in FC3_Method.
+ self.opts = None
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.method == "cdrom":
+ retval += "# Use CDROM installation media\ncdrom\n"
+ elif self.method == "harddrive":
+ msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir
+
+ if self.biospart is not None:
+ retval += msg + " --biospart=%s\n" % self.biospart
+ else:
+ retval += msg + " --partition=%s\n" % self.partition
+ elif self.method == "nfs":
+ retval += "# Use NFS installation media\nnfs --server=%s --dir=%s" % (self.server, self.dir)
+ if self.opts is not None:
+ retval += " --opts=\"%s\"" % self.opts
+ retval += "\n"
+ elif self.method == "url":
+ retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url
+
+ return retval
+
+ def _getParser(self):
+ op = FC3_Method._getParser(self)
+
+ if self.currentCmd == "nfs":
+ op.add_option("--opts", dest="opts")
+
+ return op
+
+class F13_Method(FC6_Method):
+ removedKeywords = FC6_Method.removedKeywords
+ removedAttrs = FC6_Method.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC6_Method.__init__(self, *args, **kwargs)
+
+ # And same as all the other __init__ methods.
+ self.proxy = ""
+
+ def __str__(self):
+ retval = FC6_Method.__str__(self)
+
+ if self.method == "url" and self.proxy:
+ retval = retval.strip()
+ retval += " --proxy=\"%s\"\n" % self.proxy
+
+ return retval
+
+ def _getParser(self):
+ op = FC6_Method._getParser(self)
+
+ if self.currentCmd == "url":
+ op.add_option("--proxy")
+
+ return op
+
+class F14_Method(F13_Method):
+ removedKeywords = F13_Method.removedKeywords
+ removedAttrs = F13_Method.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F13_Method.__init__(self, *args, **kwargs)
+
+ self.noverifyssl = False
+
+ def __str__(self):
+ retval = F13_Method.__str__(self)
+
+ if self.method == "url" and self.noverifyssl:
+ retval = retval.strip()
+ retval += " --noverifyssl\n"
+
+ return retval
+
+ def _getParser(self):
+ op = F13_Method._getParser(self)
+
+ if self.currentCmd == "url":
+ op.add_option("--noverifyssl", action="store_true", default=False)
+
+ return op
+
+RHEL6_Method = F14_Method
diff --git a/mic/3rdparty/pykickstart/commands/monitor.py b/mic/3rdparty/pykickstart/commands/monitor.py
new file mode 100644
index 0000000..8c8c2c4
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/monitor.py
@@ -0,0 +1,106 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Monitor(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.hsync = kwargs.get("hsync", "")
+ self.monitor = kwargs.get("monitor", "")
+ self.vsync = kwargs.get("vsync", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+ retval += "monitor"
+
+ if self.hsync != "":
+ retval += " --hsync=%s" % self.hsync
+ if self.monitor != "":
+ retval += " --monitor=\"%s\"" % self.monitor
+ if self.vsync != "":
+ retval += " --vsync=%s" % self.vsync
+
+ if retval != "monitor":
+ return retval + "\n"
+ else:
+ return ""
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--hsync")
+ op.add_option("--monitor")
+ op.add_option("--vsync")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if extra:
+ mapping = {"cmd": "monitor", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(cmd)s command: %(options)s") % mapping)
+
+ self._setToSelf(self.op, opts)
+ return self
+
+class FC6_Monitor(FC3_Monitor):
+ removedKeywords = FC3_Monitor.removedKeywords
+ removedAttrs = FC3_Monitor.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_Monitor.__init__(self, writePriority, *args, **kwargs)
+ self.probe = kwargs.get("probe", True)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+ retval += "monitor"
+
+ if self.hsync != "":
+ retval += " --hsync=%s" % self.hsync
+ if self.monitor != "":
+ retval += " --monitor=\"%s\"" % self.monitor
+ if not self.probe:
+ retval += " --noprobe"
+ if self.vsync != "":
+ retval += " --vsync=%s" % self.vsync
+
+ if retval != "monitor":
+ return retval + "\n"
+ else:
+ return ""
+
+ def _getParser(self):
+ op = FC3_Monitor._getParser(self)
+ op.add_option("--noprobe", dest="probe", action="store_false",
+ default=True)
+ return op
+
+class F10_Monitor(DeprecatedCommand):
+ def __init__(self):
+ DeprecatedCommand.__init__(self)
diff --git a/mic/3rdparty/pykickstart/commands/mouse.py b/mic/3rdparty/pykickstart/commands/mouse.py
new file mode 100644
index 0000000..c643bce
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/mouse.py
@@ -0,0 +1,70 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class RHEL3_Mouse(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.device = kwargs.get("device", "")
+ self.emulthree = kwargs.get("emulthree", False)
+ self.mouse = kwargs.get("mouse", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ opts = ""
+ if self.device:
+ opts += "--device=%s " % self.device
+ if self.emulthree:
+ opts += "--emulthree "
+
+ if self.mouse:
+ retval += "# System mouse\nmouse %s%s\n" % (opts, self.mouse)
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--device", dest="device", default="")
+ op.add_option("--emulthree", dest="emulthree", default=False, action="store_true")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "mouse")
+
+ self.mouse = extra[0]
+ return self
+
+class FC3_Mouse(DeprecatedCommand):
+ def __init__(self):
+ DeprecatedCommand.__init__(self)
diff --git a/mic/3rdparty/pykickstart/commands/multipath.py b/mic/3rdparty/pykickstart/commands/multipath.py
new file mode 100644
index 0000000..84ba755
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/multipath.py
@@ -0,0 +1,111 @@
+#
+# Chris Lumens <clumens@redhat.com>
+# Peter Jones <pjones@redhat.com>
+#
+# Copyright 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_MpPathData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.mpdev = kwargs.get("mpdev", "")
+ self.device = kwargs.get("device", "")
+ self.rule = kwargs.get("rule", "")
+
+ def __str__(self):
+ return " --device=%s --rule=\"%s\"" % (self.device, self.rule)
+
+class FC6_MultiPathData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.name = kwargs.get("name", "")
+ self.paths = kwargs.get("paths", [])
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+
+ for path in self.paths:
+ retval += "multipath --mpdev=%s %s\n" % (self.name, path.__str__())
+
+ return retval
+
+class FC6_MultiPath(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=50, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.mpaths = kwargs.get("mpaths", [])
+
+ def __str__(self):
+ retval = ""
+ for mpath in self.mpaths:
+ retval += mpath.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--name", dest="name", action="store", type="string",
+ required=1)
+ op.add_option("--device", dest="device", action="store", type="string",
+ required=1)
+ op.add_option("--rule", dest="rule", action="store", type="string",
+ required=1)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ dd = FC6_MpPathData()
+ self._setToObj(self.op, opts, dd)
+ dd.lineno = self.lineno
+ dd.mpdev = dd.mpdev.split('/')[-1]
+
+ parent = None
+ for x in range(0, len(self.mpaths)):
+ mpath = self.mpaths[x]
+ for path in mpath.paths:
+ if path.device == dd.device:
+ mapping = {"device": path.device, "multipathdev": path.mpdev}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Device '%(device)s' is already used in multipath '%(multipathdev)s'") % mapping)
+ if mpath.name == dd.mpdev:
+ parent = x
+
+ if parent is None:
+ mpath = FC6_MultiPathData()
+ return mpath
+ else:
+ mpath = self.mpaths[parent]
+
+ return dd
+
+ def dataList(self):
+ return self.mpaths
diff --git a/mic/3rdparty/pykickstart/commands/network.py b/mic/3rdparty/pykickstart/commands/network.py
new file mode 100644
index 0000000..9b67f92
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/network.py
@@ -0,0 +1,363 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_NetworkData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.bootProto = kwargs.get("bootProto", BOOTPROTO_DHCP)
+ self.dhcpclass = kwargs.get("dhcpclass", "")
+ self.device = kwargs.get("device", "")
+ self.essid = kwargs.get("essid", "")
+ self.ethtool = kwargs.get("ethtool", "")
+ self.gateway = kwargs.get("gateway", "")
+ self.hostname = kwargs.get("hostname", "")
+ self.ip = kwargs.get("ip", "")
+ self.mtu = kwargs.get("mtu", "")
+ self.nameserver = kwargs.get("nameserver", "")
+ self.netmask = kwargs.get("netmask", "")
+ self.nodns = kwargs.get("nodns", False)
+ self.onboot = kwargs.get("onboot", True)
+ self.wepkey = kwargs.get("wepkey", "")
+
+ def __eq__(self, y):
+ return self.device and self.device == y.device
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.bootProto != "":
+ retval += " --bootproto=%s" % self.bootProto
+ if self.dhcpclass != "":
+ retval += " --dhcpclass=%s" % self.dhcpclass
+ if self.device != "":
+ retval += " --device=%s" % self.device
+ if self.essid != "":
+ retval += " --essid=\"%s\"" % self.essid
+ if self.ethtool != "":
+ retval += " --ethtool=\"%s\"" % self.ethtool
+ if self.gateway != "":
+ retval += " --gateway=%s" % self.gateway
+ if self.hostname != "":
+ retval += " --hostname=%s" % self.hostname
+ if self.ip != "":
+ retval += " --ip=%s" % self.ip
+ if self.mtu != "":
+ retval += " --mtu=%s" % self.mtu
+ if self.nameserver != "":
+ retval += " --nameserver=%s" % self.nameserver
+ if self.netmask != "":
+ retval += " --netmask=%s" % self.netmask
+ if self.nodns:
+ retval += " --nodns"
+ if not self.onboot:
+ retval += " --onboot=off"
+ if self.wepkey != "":
+ retval += " --wepkey=%s" % self.wepkey
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "network %s\n" % self._getArgsAsStr()
+ return retval
+
+class FC4_NetworkData(FC3_NetworkData):
+ removedKeywords = FC3_NetworkData.removedKeywords
+ removedAttrs = FC3_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC3_NetworkData.__init__(self, *args, **kwargs)
+ self.notksdevice = kwargs.get("notksdevice", False)
+
+ def _getArgsAsStr(self):
+ retval = FC3_NetworkData._getArgsAsStr(self)
+
+ if self.notksdevice:
+ retval += " --notksdevice"
+
+ return retval
+
+class FC6_NetworkData(FC4_NetworkData):
+ removedKeywords = FC4_NetworkData.removedKeywords
+ removedAttrs = FC4_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC4_NetworkData.__init__(self, *args, **kwargs)
+ self.noipv4 = kwargs.get("noipv4", False)
+ self.noipv6 = kwargs.get("noipv6", False)
+
+ def _getArgsAsStr(self):
+ retval = FC4_NetworkData._getArgsAsStr(self)
+
+ if self.noipv4:
+ retval += " --noipv4"
+ if self.noipv6:
+ retval += " --noipv6"
+
+ return retval
+
+class F8_NetworkData(FC6_NetworkData):
+ removedKeywords = FC6_NetworkData.removedKeywords
+ removedAttrs = FC6_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC6_NetworkData.__init__(self, *args, **kwargs)
+ self.ipv6 = kwargs.get("ipv6", "")
+
+ def _getArgsAsStr(self):
+ retval = FC6_NetworkData._getArgsAsStr(self)
+
+ if self.ipv6 != "":
+ retval += " --ipv6" % self.ipv6
+
+ return retval
+
+class F16_NetworkData(F8_NetworkData):
+ removedKeywords = F8_NetworkData.removedKeywords
+ removedAttrs = F8_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F8_NetworkData.__init__(self, *args, **kwargs)
+ self.activate = kwargs.get("activate", False)
+ self.nodefroute = kwargs.get("nodefroute", False)
+ self.wpakey = kwargs.get("wpakey", "")
+
+ def _getArgsAsStr(self):
+ retval = F8_NetworkData._getArgsAsStr(self)
+
+ if self.activate:
+ retval += " --activate"
+ if self.nodefroute:
+ retval += " --nodefroute"
+ if self.wpakey != "":
+ retval += "--wpakey=%s" % self.wpakey
+
+ return retval
+
+class RHEL4_NetworkData(FC3_NetworkData):
+ removedKeywords = FC3_NetworkData.removedKeywords
+ removedAttrs = FC3_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC3_NetworkData.__init__(self, *args, **kwargs)
+ self.notksdevice = kwargs.get("notksdevice", False)
+
+ def _getArgsAsStr(self):
+ retval = FC3_NetworkData._getArgsAsStr(self)
+
+ if self.notksdevice:
+ retval += " --notksdevice"
+
+ return retval
+
+class RHEL6_NetworkData(F8_NetworkData):
+ removedKeywords = F8_NetworkData.removedKeywords
+ removedAttrs = F8_NetworkData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F8_NetworkData.__init__(self, *args, **kwargs)
+ self.activate = kwargs.get("activate", False)
+ self.nodefroute = kwargs.get("nodefroute", False)
+
+ def _getArgsAsStr(self):
+ retval = F8_NetworkData._getArgsAsStr(self)
+
+ if self.activate:
+ retval += " --activate"
+ if self.nodefroute:
+ retval += " --nodefroute"
+
+ return retval
+
+class FC3_Network(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.bootprotoList = [BOOTPROTO_DHCP, BOOTPROTO_BOOTP,
+ BOOTPROTO_STATIC]
+
+ self.op = self._getParser()
+
+ self.network = kwargs.get("network", [])
+
+ def __str__(self):
+ retval = ""
+
+ for nic in self.network:
+ retval += nic.__str__()
+
+ if retval != "":
+ return "# Network information\n" + retval
+ else:
+ return ""
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--bootproto", dest="bootProto",
+ default=BOOTPROTO_DHCP,
+ choices=self.bootprotoList)
+ op.add_option("--dhcpclass", dest="dhcpclass")
+ op.add_option("--device", dest="device")
+ op.add_option("--essid", dest="essid")
+ op.add_option("--ethtool", dest="ethtool")
+ op.add_option("--gateway", dest="gateway")
+ op.add_option("--hostname", dest="hostname")
+ op.add_option("--ip", dest="ip")
+ op.add_option("--mtu", dest="mtu")
+ op.add_option("--nameserver", dest="nameserver")
+ op.add_option("--netmask", dest="netmask")
+ op.add_option("--nodns", dest="nodns", action="store_true",
+ default=False)
+ op.add_option("--onboot", dest="onboot", action="store",
+ type="ksboolean")
+ op.add_option("--wepkey", dest="wepkey")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ nd = self.handler.NetworkData()
+ self._setToObj(self.op, opts, nd)
+ nd.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if nd in self.dataList():
+ warnings.warn(_("A network device with the name %s has already been defined.") % nd.device)
+
+ return nd
+
+ def dataList(self):
+ return self.network
+
+class FC4_Network(FC3_Network):
+ removedKeywords = FC3_Network.removedKeywords
+ removedAttrs = FC3_Network.removedAttrs
+
+ def _getParser(self):
+ op = FC3_Network._getParser(self)
+ op.add_option("--notksdevice", dest="notksdevice", action="store_true",
+ default=False)
+ return op
+
+class FC6_Network(FC4_Network):
+ removedKeywords = FC4_Network.removedKeywords
+ removedAttrs = FC4_Network.removedAttrs
+
+ def _getParser(self):
+ op = FC4_Network._getParser(self)
+ op.add_option("--noipv4", dest="noipv4", action="store_true",
+ default=False)
+ op.add_option("--noipv6", dest="noipv6", action="store_true",
+ default=False)
+ return op
+
+class F8_Network(FC6_Network):
+ removedKeywords = FC6_Network.removedKeywords
+ removedAttrs = FC6_Network.removedAttrs
+
+ def _getParser(self):
+ op = FC6_Network._getParser(self)
+ op.add_option("--ipv6", dest="ipv6")
+ return op
+
+class F9_Network(F8_Network):
+ removedKeywords = F8_Network.removedKeywords
+ removedAttrs = F8_Network.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F8_Network.__init__(self, writePriority, *args, **kwargs)
+ self.bootprotoList.append(BOOTPROTO_QUERY)
+
+ def _getParser(self):
+ op = F8_Network._getParser(self)
+ op.add_option("--bootproto", dest="bootProto",
+ default=BOOTPROTO_DHCP,
+ choices=self.bootprotoList)
+ return op
+
+class F16_Network(F9_Network):
+ removedKeywords = F9_Network.removedKeywords
+ removedAttrs = F9_Network.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F9_Network.__init__(self, writePriority, *args, **kwargs)
+ self.bootprotoList.append(BOOTPROTO_IBFT)
+
+ def _getParser(self):
+ op = F9_Network._getParser(self)
+ op.add_option("--activate", dest="activate", action="store_true",
+ default=False)
+ op.add_option("--nodefroute", dest="nodefroute", action="store_true",
+ default=False)
+ op.add_option("--wpakey", dest="wpakey", action="store", default="")
+ return op
+
+class RHEL4_Network(FC3_Network):
+ removedKeywords = FC3_Network.removedKeywords
+ removedAttrs = FC3_Network.removedAttrs
+
+ def _getParser(self):
+ op = FC3_Network._getParser(self)
+ op.add_option("--notksdevice", dest="notksdevice", action="store_true",
+ default=False)
+ return op
+
+class RHEL5_Network(FC6_Network):
+ removedKeywords = FC6_Network.removedKeywords
+ removedAttrs = FC6_Network.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC6_Network.__init__(self, writePriority, *args, **kwargs)
+ self.bootprotoList.append(BOOTPROTO_QUERY)
+
+ def _getParser(self):
+ op = FC6_Network._getParser(self)
+ op.add_option("--bootproto", dest="bootProto",
+ default=BOOTPROTO_DHCP,
+ choices=self.bootprotoList)
+ return op
+
+class RHEL6_Network(F9_Network):
+ removedKeywords = F9_Network.removedKeywords
+ removedAttrs = F9_Network.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F9_Network.__init__(self, writePriority, *args, **kwargs)
+ self.bootprotoList.append(BOOTPROTO_IBFT)
+
+ def _getParser(self):
+ op = F9_Network._getParser(self)
+ op.add_option("--activate", dest="activate", action="store_true",
+ default=False)
+ op.add_option("--nodefroute", dest="nodefroute", action="store_true",
+ default=False)
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/partition.py b/mic/3rdparty/pykickstart/commands/partition.py
new file mode 100644
index 0000000..34e5b7a
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/partition.py
@@ -0,0 +1,348 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_PartData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.active = kwargs.get("active", False)
+ self.primOnly = kwargs.get("primOnly", False)
+ self.end = kwargs.get("end", 0)
+ self.fstype = kwargs.get("fstype", "")
+ self.grow = kwargs.get("grow", False)
+ self.maxSizeMB = kwargs.get("maxSizeMB", 0)
+ self.format = kwargs.get("format", True)
+ self.onbiosdisk = kwargs.get("onbiosdisk", "")
+ self.disk = kwargs.get("disk", "")
+ self.onPart = kwargs.get("onPart", "")
+ self.recommended = kwargs.get("recommended", False)
+ self.size = kwargs.get("size", None)
+ self.start = kwargs.get("start", 0)
+ self.mountpoint = kwargs.get("mountpoint", "")
+
+ def __eq__(self, y):
+ return self.mountpoint == y.mountpoint
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.active:
+ retval += " --active"
+ if self.primOnly:
+ retval += " --asprimary"
+ if hasattr(self, "end") and self.end != 0:
+ retval += " --end=%s" % self.end
+ if self.fstype != "":
+ retval += " --fstype=\"%s\"" % self.fstype
+ if self.grow:
+ retval += " --grow"
+ if self.maxSizeMB > 0:
+ retval += " --maxsize=%d" % self.maxSizeMB
+ if not self.format:
+ retval += " --noformat"
+ if self.onbiosdisk != "":
+ retval += " --onbiosdisk=%s" % self.onbiosdisk
+ if self.disk != "":
+ retval += " --ondisk=%s" % self.disk
+ if self.onPart != "":
+ retval += " --onpart=%s" % self.onPart
+ if self.recommended:
+ retval += " --recommended"
+ if self.size and self.size != 0:
+ retval += " --size=%s" % self.size
+ if hasattr(self, "start") and self.start != 0:
+ retval += " --start=%s" % self.start
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "part %s%s\n" % (self.mountpoint, self._getArgsAsStr())
+ return retval
+
+class FC4_PartData(FC3_PartData):
+ removedKeywords = FC3_PartData.removedKeywords
+ removedAttrs = FC3_PartData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC3_PartData.__init__(self, *args, **kwargs)
+ self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
+ self.fsopts = kwargs.get("fsopts", "")
+ self.label = kwargs.get("label", "")
+
+ def _getArgsAsStr(self):
+ retval = FC3_PartData._getArgsAsStr(self)
+
+ if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
+ retval += " --bytes-per-inode=%d" % self.bytesPerInode
+ if self.fsopts != "":
+ retval += " --fsoptions=\"%s\"" % self.fsopts
+ if self.label != "":
+ retval += " --label=%s" % self.label
+
+ return retval
+
+class RHEL5_PartData(FC4_PartData):
+ removedKeywords = FC4_PartData.removedKeywords
+ removedAttrs = FC4_PartData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC4_PartData.__init__(self, *args, **kwargs)
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC4_PartData._getArgsAsStr(self)
+
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+class F9_PartData(FC4_PartData):
+ removedKeywords = FC4_PartData.removedKeywords + ["bytesPerInode"]
+ removedAttrs = FC4_PartData.removedAttrs + ["bytesPerInode"]
+
+ def __init__(self, *args, **kwargs):
+ FC4_PartData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.fsopts = kwargs.get("fsopts", "")
+ self.label = kwargs.get("label", "")
+ self.fsprofile = kwargs.get("fsprofile", "")
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC4_PartData._getArgsAsStr(self)
+
+ if self.fsprofile != "":
+ retval += " --fsprofile=\"%s\"" % self.fsprofile
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+class F11_PartData(F9_PartData):
+ removedKeywords = F9_PartData.removedKeywords + ["start", "end"]
+ removedAttrs = F9_PartData.removedAttrs + ["start", "end"]
+
+class F12_PartData(F11_PartData):
+ removedKeywords = F11_PartData.removedKeywords
+ removedAttrs = F11_PartData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F11_PartData.__init__(self, *args, **kwargs)
+
+ self.escrowcert = kwargs.get("escrowcert", "")
+ self.backuppassphrase = kwargs.get("backuppassphrase", False)
+
+ def _getArgsAsStr(self):
+ retval = F11_PartData._getArgsAsStr(self)
+
+ if self.encrypted and self.escrowcert != "":
+ retval += " --escrowcert=\"%s\"" % self.escrowcert
+
+ if self.backuppassphrase:
+ retval += " --backuppassphrase"
+
+ return retval
+
+F14_PartData = F12_PartData
+
+class FC3_Partition(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=130, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.partitions = kwargs.get("partitions", [])
+
+ def __str__(self):
+ retval = ""
+
+ for part in self.partitions:
+ retval += part.__str__()
+
+ if retval != "":
+ return "# Disk partitioning information\n" + retval
+ else:
+ return ""
+
+ def _getParser(self):
+ def part_cb (option, opt_str, value, parser):
+ if value.startswith("/dev/"):
+ parser.values.ensure_value(option.dest, value[5:])
+ else:
+ parser.values.ensure_value(option.dest, value)
+
+ op = KSOptionParser()
+ op.add_option("--active", dest="active", action="store_true",
+ default=False)
+ op.add_option("--asprimary", dest="primOnly", action="store_true",
+ default=False)
+ op.add_option("--end", dest="end", action="store", type="int",
+ nargs=1)
+ op.add_option("--fstype", "--type", dest="fstype")
+ op.add_option("--grow", dest="grow", action="store_true", default=False)
+ op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
+ nargs=1)
+ op.add_option("--noformat", dest="format", action="store_false",
+ default=True)
+ op.add_option("--onbiosdisk", dest="onbiosdisk")
+ op.add_option("--ondisk", "--ondrive", dest="disk")
+ op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
+ callback=part_cb, nargs=1, type="string")
+ op.add_option("--recommended", dest="recommended", action="store_true",
+ default=False)
+ op.add_option("--size", dest="size", action="store", type="int",
+ nargs=1)
+ op.add_option("--start", dest="start", action="store", type="int",
+ nargs=1)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "partition")
+
+ pd = self.handler.PartData()
+ self._setToObj(self.op, opts, pd)
+ pd.lineno = self.lineno
+ pd.mountpoint=extra[0]
+
+ # Check for duplicates in the data list.
+ if pd in self.dataList():
+ warnings.warn(_("A partition with the mountpoint %s has already been defined.") % pd.mountpoint)
+
+ return pd
+
+ def dataList(self):
+ return self.partitions
+
+class FC4_Partition(FC3_Partition):
+ removedKeywords = FC3_Partition.removedKeywords
+ removedAttrs = FC3_Partition.removedAttrs
+
+ def __init__(self, writePriority=130, *args, **kwargs):
+ FC3_Partition.__init__(self, writePriority, *args, **kwargs)
+
+ def part_cb (option, opt_str, value, parser):
+ if value.startswith("/dev/"):
+ parser.values.ensure_value(option.dest, value[5:])
+ else:
+ parser.values.ensure_value(option.dest, value)
+
+ def _getParser(self):
+ op = FC3_Partition._getParser(self)
+ op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
+ type="int", nargs=1)
+ op.add_option("--fsoptions", dest="fsopts")
+ op.add_option("--label", dest="label")
+ return op
+
+class RHEL5_Partition(FC4_Partition):
+ removedKeywords = FC4_Partition.removedKeywords
+ removedAttrs = FC4_Partition.removedAttrs
+
+ def __init__(self, writePriority=130, *args, **kwargs):
+ FC4_Partition.__init__(self, writePriority, *args, **kwargs)
+
+ def part_cb (option, opt_str, value, parser):
+ if value.startswith("/dev/"):
+ parser.values.ensure_value(option.dest, value[5:])
+ else:
+ parser.values.ensure_value(option.dest, value)
+
+ def _getParser(self):
+ op = FC4_Partition._getParser(self)
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F9_Partition(FC4_Partition):
+ removedKeywords = FC4_Partition.removedKeywords
+ removedAttrs = FC4_Partition.removedAttrs
+
+ def __init__(self, writePriority=130, *args, **kwargs):
+ FC4_Partition.__init__(self, writePriority, *args, **kwargs)
+
+ def part_cb (option, opt_str, value, parser):
+ if value.startswith("/dev/"):
+ parser.values.ensure_value(option.dest, value[5:])
+ else:
+ parser.values.ensure_value(option.dest, value)
+
+ def _getParser(self):
+ op = FC4_Partition._getParser(self)
+ op.add_option("--bytes-per-inode", deprecated=1)
+ op.add_option("--fsprofile")
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F11_Partition(F9_Partition):
+ removedKeywords = F9_Partition.removedKeywords
+ removedAttrs = F9_Partition.removedAttrs
+
+ def _getParser(self):
+ op = F9_Partition._getParser(self)
+ op.add_option("--start", deprecated=1)
+ op.add_option("--end", deprecated=1)
+ return op
+
+class F12_Partition(F11_Partition):
+ removedKeywords = F11_Partition.removedKeywords
+ removedAttrs = F11_Partition.removedAttrs
+
+ def _getParser(self):
+ op = F11_Partition._getParser(self)
+ op.add_option("--escrowcert")
+ op.add_option("--backuppassphrase", action="store_true", default=False)
+ return op
+
+class F14_Partition(F12_Partition):
+ removedKeywords = F12_Partition.removedKeywords
+ removedAttrs = F12_Partition.removedAttrs
+
+ def _getParser(self):
+ op = F12_Partition._getParser(self)
+ op.remove_option("--bytes-per-inode")
+ op.remove_option("--start")
+ op.remove_option("--end")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/raid.py b/mic/3rdparty/pykickstart/commands/raid.py
new file mode 100644
index 0000000..0f4c92a
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/raid.py
@@ -0,0 +1,365 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_RaidData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.device = kwargs.get("device", None)
+ self.fstype = kwargs.get("fstype", "")
+ self.level = kwargs.get("level", "")
+ self.format = kwargs.get("format", True)
+ self.spares = kwargs.get("spares", 0)
+ self.preexist = kwargs.get("preexist", False)
+ self.mountpoint = kwargs.get("mountpoint", "")
+ self.members = kwargs.get("members", [])
+
+ def __eq__(self, y):
+ return self.device == y.device
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.device != "":
+ retval += " --device=%s" % self.device
+ if self.fstype != "":
+ retval += " --fstype=\"%s\"" % self.fstype
+ if self.level != "":
+ retval += " --level=%s" % self.level
+ if not self.format:
+ retval += " --noformat"
+ if self.spares != 0:
+ retval += " --spares=%d" % self.spares
+ if self.preexist:
+ retval += " --useexisting"
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "raid %s%s %s\n" % (self.mountpoint, self._getArgsAsStr(),
+ " ".join(self.members))
+ return retval
+
+class FC4_RaidData(FC3_RaidData):
+ removedKeywords = FC3_RaidData.removedKeywords
+ removedAttrs = FC3_RaidData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC3_RaidData.__init__(self, *args, **kwargs)
+ self.fsopts = kwargs.get("fsopts", "")
+
+ def _getArgsAsStr(self):
+ retval = FC3_RaidData._getArgsAsStr(self)
+
+ if self.fsopts != "":
+ retval += " --fsoptions=\"%s\"" % self.fsopts
+
+ return retval
+
+class FC5_RaidData(FC4_RaidData):
+ removedKeywords = FC4_RaidData.removedKeywords
+ removedAttrs = FC4_RaidData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC4_RaidData.__init__(self, *args, **kwargs)
+ self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
+
+ def _getArgsAsStr(self):
+ retval = FC4_RaidData._getArgsAsStr(self)
+
+ if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
+ retval += " --bytes-per-inode=%d" % self.bytesPerInode
+
+ return retval
+
+class RHEL5_RaidData(FC5_RaidData):
+ removedKeywords = FC5_RaidData.removedKeywords
+ removedAttrs = FC5_RaidData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC5_RaidData.__init__(self, *args, **kwargs)
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC5_RaidData._getArgsAsStr(self)
+
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+F7_RaidData = FC5_RaidData
+
+class F9_RaidData(FC5_RaidData):
+ removedKeywords = FC5_RaidData.removedKeywords + ["bytesPerInode"]
+ removedAttrs = FC5_RaidData.removedAttrs + ["bytesPerInode"]
+
+ def __init__(self, *args, **kwargs):
+ FC5_RaidData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.fsprofile = kwargs.get("fsprofile", "")
+ self.encrypted = kwargs.get("encrypted", False)
+ self.passphrase = kwargs.get("passphrase", "")
+
+ def _getArgsAsStr(self):
+ retval = FC5_RaidData._getArgsAsStr(self)
+
+ if self.fsprofile != "":
+ retval += " --fsprofile=\"%s\"" % self.fsprofile
+ if self.encrypted:
+ retval += " --encrypted"
+
+ if self.passphrase != "":
+ retval += " --passphrase=\"%s\"" % self.passphrase
+
+ return retval
+
+class F12_RaidData(F9_RaidData):
+ removedKeywords = F9_RaidData.removedKeywords
+ removedAttrs = F9_RaidData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F9_RaidData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.escrowcert = kwargs.get("escrowcert", "")
+ self.backuppassphrase = kwargs.get("backuppassphrase", False)
+
+ def _getArgsAsStr(self):
+ retval = F9_RaidData._getArgsAsStr(self)
+
+ if self.encrypted and self.escrowcert != "":
+ retval += " --escrowcert=\"%s\"" % self.escrowcert
+
+ if self.backuppassphrase:
+ retval += " --backuppassphrase"
+ return retval
+
+F13_RaidData = F12_RaidData
+
+F14_RaidData = F13_RaidData
+
+class F15_RaidData(F14_RaidData):
+ removedKeywords = F14_RaidData.removedKeywords
+ removedAttrs = F14_RaidData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F14_RaidData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.label = kwargs.get("label", "")
+
+ def _getArgsAsStr(self):
+ retval = F14_RaidData._getArgsAsStr(self)
+
+ if self.label != "":
+ retval += " --label=%s" % self.label
+
+ return retval
+
+class FC3_Raid(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=131, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ # A dict of all the RAID levels we support. This means that if we
+ # support more levels in the future, subclasses don't have to
+ # duplicate too much.
+ self.levelMap = { "RAID0": "RAID0", "0": "RAID0",
+ "RAID1": "RAID1", "1": "RAID1",
+ "RAID5": "RAID5", "5": "RAID5",
+ "RAID6": "RAID6", "6": "RAID6" }
+
+ self.raidList = kwargs.get("raidList", [])
+
+ def __str__(self):
+ retval = ""
+
+ for raid in self.raidList:
+ retval += raid.__str__()
+
+ return retval
+
+ def _getParser(self):
+ def raid_cb (option, opt_str, value, parser):
+ parser.values.format = False
+ parser.values.preexist = True
+
+ def device_cb (option, opt_str, value, parser):
+ if value[0:2] == "md":
+ parser.values.ensure_value(option.dest, value[2:])
+ else:
+ parser.values.ensure_value(option.dest, value)
+
+ def level_cb (option, opt_str, value, parser):
+ if self.levelMap.has_key(value):
+ parser.values.ensure_value(option.dest, self.levelMap[value])
+
+ op = KSOptionParser()
+ op.add_option("--device", action="callback", callback=device_cb,
+ dest="device", type="string", nargs=1, required=1)
+ op.add_option("--fstype", dest="fstype")
+ op.add_option("--level", dest="level", action="callback",
+ callback=level_cb, type="string", nargs=1)
+ op.add_option("--noformat", action="callback", callback=raid_cb,
+ dest="format", default=True, nargs=0)
+ op.add_option("--spares", dest="spares", action="store", type="int",
+ nargs=1, default=0)
+ op.add_option("--useexisting", dest="preexist", action="store_true",
+ default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) == 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid")
+ if len(extra) == 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Partitions required for %s") % "raid")
+
+ rd = self.handler.RaidData()
+ self._setToObj(self.op, opts, rd)
+ rd.lineno = self.lineno
+
+ # --device can't just take an int in the callback above, because it
+ # could be specificed as "mdX", which causes optparse to error when
+ # it runs int().
+ rd.device = int(rd.device)
+ rd.mountpoint = extra[0]
+ rd.members = extra[1:]
+
+ # Check for duplicates in the data list.
+ if rd in self.dataList():
+ warnings.warn(_("A RAID device with the name %s has already been defined.") % rd.device)
+
+ return rd
+
+ def dataList(self):
+ return self.raidList
+
+class FC4_Raid(FC3_Raid):
+ removedKeywords = FC3_Raid.removedKeywords
+ removedAttrs = FC3_Raid.removedAttrs
+
+ def _getParser(self):
+ op = FC3_Raid._getParser(self)
+ op.add_option("--fsoptions", dest="fsopts")
+ return op
+
+class FC5_Raid(FC4_Raid):
+ removedKeywords = FC4_Raid.removedKeywords
+ removedAttrs = FC4_Raid.removedAttrs
+
+ def _getParser(self):
+ op = FC4_Raid._getParser(self)
+ op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
+ type="int", nargs=1)
+ return op
+
+class RHEL5_Raid(FC5_Raid):
+ removedKeywords = FC5_Raid.removedKeywords
+ removedAttrs = FC5_Raid.removedAttrs
+
+ def __init__(self, writePriority=131, *args, **kwargs):
+ FC5_Raid.__init__(self, writePriority, *args, **kwargs)
+
+ self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"})
+
+ def _getParser(self):
+ op = FC5_Raid._getParser(self)
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F7_Raid(FC5_Raid):
+ removedKeywords = FC5_Raid.removedKeywords
+ removedAttrs = FC5_Raid.removedAttrs
+
+ def __init__(self, writePriority=131, *args, **kwargs):
+ FC5_Raid.__init__(self, writePriority, *args, **kwargs)
+
+ self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"})
+
+class F9_Raid(F7_Raid):
+ removedKeywords = F7_Raid.removedKeywords
+ removedAttrs = F7_Raid.removedAttrs
+
+ def _getParser(self):
+ op = F7_Raid._getParser(self)
+ op.add_option("--bytes-per-inode", deprecated=1)
+ op.add_option("--fsprofile")
+ op.add_option("--encrypted", action="store_true", default=False)
+ op.add_option("--passphrase")
+ return op
+
+class F12_Raid(F9_Raid):
+ removedKeywords = F9_Raid.removedKeywords
+ removedAttrs = F9_Raid.removedAttrs
+
+ def _getParser(self):
+ op = F9_Raid._getParser(self)
+ op.add_option("--escrowcert")
+ op.add_option("--backuppassphrase", action="store_true", default=False)
+ return op
+
+class F13_Raid(F12_Raid):
+ removedKeywords = F12_Raid.removedKeywords
+ removedAttrs = F12_Raid.removedAttrs
+
+ def __init__(self, writePriority=131, *args, **kwargs):
+ F12_Raid.__init__(self, writePriority, *args, **kwargs)
+
+ self.levelMap.update({"RAID4": "RAID4", "4": "RAID4"})
+
+class F14_Raid(F13_Raid):
+ removedKeywords = F13_Raid.removedKeywords
+ removedAttrs = F13_Raid.removedAttrs
+
+ def _getParser(self):
+ op = F13_Raid._getParser(self)
+ op.remove_option("--bytes-per-inode")
+ return op
+
+class F15_Raid(F14_Raid):
+ removedKeywords = F14_Raid.removedKeywords
+ removedAttrs = F14_Raid.removedAttrs
+
+ def _getParser(self):
+ op = F14_Raid._getParser(self)
+ op.add_option("--label")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/reboot.py b/mic/3rdparty/pykickstart/commands/reboot.py
new file mode 100644
index 0000000..391af14
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/reboot.py
@@ -0,0 +1,79 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+class FC3_Reboot(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.action = kwargs.get("action", None)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.action == KS_REBOOT:
+ retval += "# Reboot after installation\nreboot\n"
+ elif self.action == KS_SHUTDOWN:
+ retval += "# Shutdown after installation\nshutdown\n"
+
+ return retval
+
+ def parse(self, args):
+ if self.currentCmd == "reboot":
+ self.action = KS_REBOOT
+ else:
+ self.action = KS_SHUTDOWN
+
+ return self
+
+class FC6_Reboot(FC3_Reboot):
+ removedKeywords = FC3_Reboot.removedKeywords
+ removedAttrs = FC3_Reboot.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_Reboot.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.eject = kwargs.get("eject", False)
+
+ def __str__(self):
+ retval = FC3_Reboot.__str__(self).rstrip()
+
+ if self.eject:
+ retval += " --eject"
+
+ return retval + "\n"
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--eject", dest="eject", action="store_true",
+ default=False)
+ return op
+
+ def parse(self, args):
+ FC3_Reboot.parse(self, args)
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/repo.py b/mic/3rdparty/pykickstart/commands/repo.py
new file mode 100644
index 0000000..543ef94
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/repo.py
@@ -0,0 +1,249 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007, 2008, 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_RepoData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.baseurl = kwargs.get("baseurl", "")
+ self.mirrorlist = kwargs.get("mirrorlist", None)
+ self.name = kwargs.get("name", "")
+
+ def __eq__(self, y):
+ return self.name == y.name
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.baseurl:
+ retval += "--baseurl=%s" % self.baseurl
+ elif self.mirrorlist:
+ retval += "--mirrorlist=%s" % self.mirrorlist
+
+ return retval
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "repo --name=\"%s\" %s\n" % (self.name, self._getArgsAsStr())
+ return retval
+
+class F8_RepoData(FC6_RepoData):
+ removedKeywords = FC6_RepoData.removedKeywords
+ removedAttrs = FC6_RepoData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC6_RepoData.__init__(self, *args, **kwargs)
+ self.cost = kwargs.get("cost", None)
+ self.includepkgs = kwargs.get("includepkgs", [])
+ self.excludepkgs = kwargs.get("excludepkgs", [])
+
+ def _getArgsAsStr(self):
+ retval = FC6_RepoData._getArgsAsStr(self)
+
+ if self.cost:
+ retval += " --cost=%s" % self.cost
+ if self.includepkgs:
+ retval += " --includepkgs=\"%s\"" % ",".join(self.includepkgs)
+ if self.excludepkgs:
+ retval += " --excludepkgs=\"%s\"" % ",".join(self.excludepkgs)
+
+ return retval
+
+class F11_RepoData(F8_RepoData):
+ removedKeywords = F8_RepoData.removedKeywords
+ removedAttrs = F8_RepoData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F8_RepoData.__init__(self, *args, **kwargs)
+ self.ignoregroups = kwargs.get("ignoregroups", None)
+
+ def _getArgsAsStr(self):
+ retval = F8_RepoData._getArgsAsStr(self)
+
+ if self.ignoregroups:
+ retval += " --ignoregroups=true"
+ return retval
+
+class F13_RepoData(F11_RepoData):
+ removedKeywords = F11_RepoData.removedKeywords
+ removedAttrs = F11_RepoData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F11_RepoData.__init__(self, *args, **kwargs)
+ self.proxy = kwargs.get("proxy", "")
+
+ def _getArgsAsStr(self):
+ retval = F11_RepoData._getArgsAsStr(self)
+
+ if self.proxy:
+ retval += " --proxy=\"%s\"" % self.proxy
+
+ return retval
+
+class F14_RepoData(F13_RepoData):
+ removedKeywords = F13_RepoData.removedKeywords
+ removedAttrs = F13_RepoData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F13_RepoData.__init__(self, *args, **kwargs)
+ self.noverifyssl = kwargs.get("noverifyssl", False)
+
+ def _getArgsAsStr(self):
+ retval = F13_RepoData._getArgsAsStr(self)
+
+ if self.noverifyssl:
+ retval += " --noverifyssl"
+
+ return retval
+
+RHEL6_RepoData = F14_RepoData
+
+F15_RepoData = F14_RepoData
+
+class FC6_Repo(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ urlRequired = True
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.repoList = kwargs.get("repoList", [])
+
+ def __str__(self):
+ retval = ""
+ for repo in self.repoList:
+ retval += repo.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--name", dest="name", required=1)
+ op.add_option("--baseurl")
+ op.add_option("--mirrorlist")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) != 0:
+ mapping = {"command": "repo", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
+
+ # This is lame, but I can't think of a better way to make sure only
+ # one of these two is specified.
+ if opts.baseurl and opts.mirrorlist:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --baseurl and --mirrorlist may be specified for repo command."))
+
+ if self.urlRequired and not opts.baseurl and not opts.mirrorlist:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --baseurl or --mirrorlist must be specified for repo command."))
+
+ rd = self.handler.RepoData()
+ self._setToObj(self.op, opts, rd)
+ rd.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if rd in self.dataList():
+ warnings.warn(_("A repo with the name %s has already been defined.") % rd.name)
+
+ return rd
+
+ def dataList(self):
+ return self.repoList
+
+class F8_Repo(FC6_Repo):
+ removedKeywords = FC6_Repo.removedKeywords
+ removedAttrs = FC6_Repo.removedAttrs
+
+ def __str__(self):
+ retval = ""
+ for repo in self.repoList:
+ retval += repo.__str__()
+
+ return retval
+
+ def _getParser(self):
+ def list_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = FC6_Repo._getParser(self)
+ op.add_option("--cost", action="store", type="int")
+ op.add_option("--excludepkgs", action="callback", callback=list_cb,
+ nargs=1, type="string")
+ op.add_option("--includepkgs", action="callback", callback=list_cb,
+ nargs=1, type="string")
+ return op
+
+ def methodToRepo(self):
+ if not self.handler.method.url:
+ raise KickstartError, formatErrorMsg(self.handler.method.lineno, msg=_("Method must be a url to be added to the repo list."))
+ reponame = "ks-method-url"
+ repourl = self.handler.method.url
+ rd = self.handler.RepoData(name=reponame, baseurl=repourl)
+ return rd
+
+class F11_Repo(F8_Repo):
+ removedKeywords = F8_Repo.removedKeywords
+ removedAttrs = F8_Repo.removedAttrs
+
+ def _getParser(self):
+ op = F8_Repo._getParser(self)
+ op.add_option("--ignoregroups", action="store", type="ksboolean")
+ return op
+
+class F13_Repo(F11_Repo):
+ removedKeywords = F11_Repo.removedKeywords
+ removedAttrs = F11_Repo.removedAttrs
+
+ def _getParser(self):
+ op = F11_Repo._getParser(self)
+ op.add_option("--proxy")
+ return op
+
+class F14_Repo(F13_Repo):
+ removedKeywords = F13_Repo.removedKeywords
+ removedAttrs = F13_Repo.removedAttrs
+
+ def _getParser(self):
+ op = F13_Repo._getParser(self)
+ op.add_option("--noverifyssl", action="store_true", default=False)
+ return op
+
+RHEL6_Repo = F14_Repo
+
+class F15_Repo(F14_Repo):
+ removedKeywords = F14_Repo.removedKeywords
+ removedAttrs = F14_Repo.removedAttrs
+
+ urlRequired = False
diff --git a/mic/3rdparty/pykickstart/commands/rescue.py b/mic/3rdparty/pykickstart/commands/rescue.py
new file mode 100644
index 0000000..1893d4e
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/rescue.py
@@ -0,0 +1,68 @@
+#
+# Alexander Todorov <atodorov@redhat.com>
+#
+# Copyright 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F10_Rescue(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.rescue = False
+ self.nomount = kwargs.get("nomount", False)
+ self.romount = kwargs.get("romount", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.rescue:
+ retval += "rescue"
+
+ if self.nomount:
+ retval += " --nomount"
+ if self.romount:
+ retval += " --romount"
+
+ retval = "# Start rescue mode\n%s\n" % retval
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--nomount", dest="nomount", action="store_true", default=False)
+ op.add_option("--romount", dest="romount", action="store_true", default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if opts.nomount and opts.romount:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --nomount and --romount may be specified for rescue command."))
+
+ self._setToSelf(self.op, opts)
+ self.rescue = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/rootpw.py b/mic/3rdparty/pykickstart/commands/rootpw.py
new file mode 100644
index 0000000..e038b45
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/rootpw.py
@@ -0,0 +1,93 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_RootPw(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.isCrypted = kwargs.get("isCrypted", False)
+ self.password = kwargs.get("password", "")
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if self.isCrypted:
+ retval += " --iscrypted"
+
+ return retval
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.password != "":
+ retval += "# Root password\nrootpw%s %s\n" % (self._getArgsAsStr(), self.password)
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--iscrypted", dest="isCrypted", action="store_true",
+ default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw")
+
+ self.password = extra[0]
+ return self
+
+class F8_RootPw(FC3_RootPw):
+ removedKeywords = FC3_RootPw.removedKeywords
+ removedAttrs = FC3_RootPw.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_RootPw.__init__(self, writePriority, *args, **kwargs)
+ self.lock = kwargs.get("lock", False)
+
+ def _getArgsAsStr(self):
+ retval = FC3_RootPw._getArgsAsStr(self)
+
+ if self.lock:
+ retval += " --lock"
+
+ if not self.isCrypted:
+ retval += " --plaintext"
+
+ return retval
+
+ def _getParser(self):
+ op = FC3_RootPw._getParser(self)
+ op.add_option("--lock", dest="lock", action="store_true", default=False)
+ op.add_option("--plaintext", dest="isCrypted", action="store_false")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/selinux.py b/mic/3rdparty/pykickstart/commands/selinux.py
new file mode 100644
index 0000000..9f8059c
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/selinux.py
@@ -0,0 +1,64 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.options import *
+
+class FC3_SELinux(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.selinux = kwargs.get("selinux", None)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if not retval and self.selinux is None:
+ return ""
+
+ retval += "# SELinux configuration\n"
+
+ if self.selinux == SELINUX_DISABLED:
+ retval += "selinux --disabled\n"
+ elif self.selinux == SELINUX_ENFORCING:
+ retval += "selinux --enforcing\n"
+ elif self.selinux == SELINUX_PERMISSIVE:
+ retval += "selinux --permissive\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--disabled", dest="selinux", action="store_const",
+ const=SELINUX_DISABLED)
+ op.add_option("--enforcing", dest="selinux", action="store_const",
+ const=SELINUX_ENFORCING)
+ op.add_option("--permissive", dest="selinux", action="store_const",
+ const=SELINUX_PERMISSIVE)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/services.py b/mic/3rdparty/pykickstart/commands/services.py
new file mode 100644
index 0000000..2e0eab8
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/services.py
@@ -0,0 +1,71 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_Services(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.disabled = kwargs.get("disabled", [])
+ self.enabled = kwargs.get("enabled", [])
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+ args = ""
+
+ if len(self.disabled) > 0:
+ args += " --disabled=\"%s\"" % ",".join(self.disabled)
+ if len(self.enabled) > 0:
+ args += " --enabled=\"%s\"" % ",".join(self.enabled)
+
+ if args != "":
+ retval += "# System services\nservices%s\n" % args
+
+ return retval
+
+ def _getParser(self):
+ def services_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d.strip())
+
+ op = KSOptionParser()
+ op.add_option("--disabled", dest="disabled", action="callback",
+ callback=services_cb, nargs=1, type="string")
+ op.add_option("--enabled", dest="enabled", action="callback",
+ callback=services_cb, nargs=1, type="string")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if len(self.disabled) == 0 and len(self.enabled) == 0:
+ raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("One of --disabled or --enabled must be provided."))
+
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/skipx.py b/mic/3rdparty/pykickstart/commands/skipx.py
new file mode 100644
index 0000000..36d1a8d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/skipx.py
@@ -0,0 +1,54 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_SkipX(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.skipx = kwargs.get("skipx", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.skipx:
+ retval += "# Do not configure the X Window System\nskipx\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "skipx")
+
+ self.skipx = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/sshpw.py b/mic/3rdparty/pykickstart/commands/sshpw.py
new file mode 100644
index 0000000..e7867eb
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/sshpw.py
@@ -0,0 +1,105 @@
+#
+# Peter Jones <pjones@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F13_SshPwData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.username = kwargs.get("username", None)
+ self.isCrypted = kwargs.get("isCrypted", False)
+ self.password = kwargs.get("password", "")
+ self.lock = kwargs.get("lock", False)
+
+ def __eq__(self, y):
+ return self.username == y.username
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+
+ retval += "sshpw"
+ retval += self._getArgsAsStr() + '\n'
+
+ return retval
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ retval += " --username=%s" % self.username
+ if self.lock:
+ retval += " --lock"
+ if self.isCrypted:
+ retval += " --iscrypted"
+ else:
+ retval += " --plaintext"
+
+ retval += " %s" % self.password
+ return retval
+
+class F13_SshPw(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.sshUserList = kwargs.get("sshUserList", [])
+
+ def __str__(self):
+ retval = ""
+ for user in self.sshUserList:
+ retval += user.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--username", dest="username", required=True)
+ op.add_option("--iscrypted", dest="isCrypted", action="store_true",
+ default=False)
+ op.add_option("--plaintext", dest="isCrypted", action="store_false")
+ op.add_option("--lock", dest="lock", action="store_true", default=False)
+ return op
+
+ def parse(self, args):
+ ud = self.handler.SshPwData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToObj(self.op, opts, ud)
+ ud.lineno = self.lineno
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "sshpw")
+ ud.password = extra[0]
+
+ if ud in self.dataList():
+ warnings.warn(_("An ssh user with the name %s has already been defined.") % ud.name)
+
+ return ud
+
+ def dataList(self):
+ return self.sshUserList
diff --git a/mic/3rdparty/pykickstart/commands/timezone.py b/mic/3rdparty/pykickstart/commands/timezone.py
new file mode 100644
index 0000000..f5441de
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/timezone.py
@@ -0,0 +1,86 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Timezone(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.isUtc = kwargs.get("isUtc", False)
+ self.timezone = kwargs.get("timezone", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.timezone != "":
+ if self.isUtc:
+ utc = "--utc"
+ else:
+ utc = ""
+
+ retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone)
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--utc", dest="isUtc", action="store_true", default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+
+ if len(extra) != 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone")
+
+ self.timezone = extra[0]
+ return self
+
+class FC6_Timezone(FC3_Timezone):
+ removedKeywords = FC3_Timezone.removedKeywords
+ removedAttrs = FC3_Timezone.removedAttrs
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.timezone != "":
+ if self.isUtc:
+ utc = "--isUtc"
+ else:
+ utc = ""
+
+ retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone)
+
+ return retval
+
+ def _getParser(self):
+ op = FC3_Timezone._getParser(self)
+ op.add_option("--utc", "--isUtc", dest="isUtc", action="store_true", default=False)
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/updates.py b/mic/3rdparty/pykickstart/commands/updates.py
new file mode 100644
index 0000000..53ec49f
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/updates.py
@@ -0,0 +1,60 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class F7_Updates(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.url = kwargs.get("url", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.url == "floppy":
+ retval += "updates\n"
+ elif self.url != "":
+ retval += "updates %s\n" % self.url
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 1:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s only takes one argument") % "updates")
+ elif len(extra) == 0:
+ self.url = "floppy"
+ else:
+ self.url = extra[0]
+
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/upgrade.py b/mic/3rdparty/pykickstart/commands/upgrade.py
new file mode 100644
index 0000000..a68a82d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/upgrade.py
@@ -0,0 +1,106 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_Upgrade(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.upgrade = kwargs.get("upgrade", None)
+ self.op = self._getParser()
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.upgrade is None:
+ return retval
+
+ if self.upgrade:
+ retval += "# Upgrade existing installation\nupgrade\n"
+ else:
+ retval += "# Install OS instead of upgrade\ninstall\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade")
+
+ if self.currentCmd == "upgrade":
+ self.upgrade = True
+ else:
+ self.upgrade = False
+
+ return self
+
+class F11_Upgrade(FC3_Upgrade):
+ removedKeywords = FC3_Upgrade.removedKeywords
+ removedAttrs = FC3_Upgrade.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_Upgrade.__init__(self, writePriority, *args, **kwargs)
+
+ self.op = self._getParser()
+ self.root_device = kwargs.get("root_device", None)
+
+ def __str__(self):
+ if self.upgrade and (self.root_device is not None):
+ retval = KickstartCommand.__str__(self)
+ retval += "# Upgrade existing installation\nupgrade --root-device=%s\n" % self.root_device
+ else:
+ retval = FC3_Upgrade.__str__(self)
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--root-device", dest="root_device")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 0:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade")
+
+ if (opts.root_device is not None) and (opts.root_device == ""):
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not accept empty parameter %s") % ("upgrade", "--root-device"))
+ else:
+ self.root_device = opts.root_device
+
+ if self.currentCmd == "upgrade":
+ self.upgrade = True
+ else:
+ self.upgrade = False
+
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/user.py b/mic/3rdparty/pykickstart/commands/user.py
new file mode 100644
index 0000000..189dc75
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/user.py
@@ -0,0 +1,173 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.constants import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC6_UserData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.groups = kwargs.get("groups", [])
+ self.homedir = kwargs.get("homedir", "")
+ self.isCrypted = kwargs.get("isCrypted", False)
+ self.name = kwargs.get("name", "")
+ self.password = kwargs.get("password", "")
+ self.shell = kwargs.get("shell", "")
+ self.uid = kwargs.get("uid", None)
+
+ def __eq__(self, y):
+ return self.name == y.name
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+
+ if self.uid != "":
+ retval += "user"
+ retval += self._getArgsAsStr() + "\n"
+
+ return retval
+
+ def _getArgsAsStr(self):
+ retval = ""
+
+ if len(self.groups) > 0:
+ retval += " --groups=%s" % ",".join(self.groups)
+ if self.homedir:
+ retval += " --homedir=%s" % self.homedir
+ if self.name:
+ retval += " --name=%s" % self.name
+ if self.password:
+ retval += " --password=%s" % self.password
+ if self.isCrypted:
+ retval += " --iscrypted"
+ if self.shell:
+ retval += " --shell=%s" % self.shell
+ if self.uid:
+ retval += " --uid=%s" % self.uid
+
+ return retval
+
+class F8_UserData(FC6_UserData):
+ removedKeywords = FC6_UserData.removedKeywords
+ removedAttrs = FC6_UserData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC6_UserData.__init__(self, *args, **kwargs)
+ self.lock = kwargs.get("lock", False)
+
+ def _getArgsAsStr(self):
+ retval = FC6_UserData._getArgsAsStr(self)
+
+ if self.lock:
+ retval += " --lock"
+
+ return retval
+
+class F12_UserData(F8_UserData):
+ removedKeywords = F8_UserData.removedKeywords
+ removedAttrs = F8_UserData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ F8_UserData.__init__(self, *args, **kwargs)
+ self.gecos = kwargs.get("gecos", "")
+
+ def _getArgsAsStr(self):
+ retval = F8_UserData._getArgsAsStr(self)
+
+ if self.gecos:
+ retval += " --gecos=\"%s\"" % (self.gecos,)
+
+ return retval
+
+class FC6_User(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.userList = kwargs.get("userList", [])
+
+ def __str__(self):
+ retval = ""
+ for user in self.userList:
+ retval += user.__str__()
+
+ return retval
+
+ def _getParser(self):
+ def groups_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = KSOptionParser()
+ op.add_option("--groups", dest="groups", action="callback",
+ callback=groups_cb, nargs=1, type="string")
+ op.add_option("--homedir")
+ op.add_option("--iscrypted", dest="isCrypted", action="store_true",
+ default=False)
+ op.add_option("--name", required=1)
+ op.add_option("--password")
+ op.add_option("--shell")
+ op.add_option("--uid", type="int")
+ return op
+
+ def parse(self, args):
+ ud = self.handler.UserData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToObj(self.op, opts, ud)
+ ud.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if ud in self.dataList():
+ warnings.warn(_("A user with the name %s has already been defined.") % ud.name)
+
+ return ud
+
+ def dataList(self):
+ return self.userList
+
+class F8_User(FC6_User):
+ removedKeywords = FC6_User.removedKeywords
+ removedAttrs = FC6_User.removedAttrs
+
+ def _getParser(self):
+ op = FC6_User._getParser(self)
+ op.add_option("--lock", action="store_true", default=False)
+ op.add_option("--plaintext", dest="isCrypted", action="store_false")
+ return op
+
+class F12_User(F8_User):
+ removedKeywords = F8_User.removedKeywords
+ removedAttrs = F8_User.removedAttrs
+
+ def _getParser(self):
+ op = F8_User._getParser(self)
+ op.add_option("--gecos", type="string")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/vnc.py b/mic/3rdparty/pykickstart/commands/vnc.py
new file mode 100644
index 0000000..200ccfb
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/vnc.py
@@ -0,0 +1,114 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+class FC3_Vnc(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.enabled = kwargs.get("enabled", False)
+ self.password = kwargs.get("password", "")
+ self.connect = kwargs.get("connect", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if not self.enabled:
+ return retval
+
+ retval += "vnc"
+
+ if self.connect != "":
+ retval += " --connect=%s" % self.connect
+ if self.password != "":
+ retval += " --password=%s" % self.password
+
+ return retval + "\n"
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--connect")
+ op.add_option("--password", dest="password")
+ return op
+
+ def parse(self, args):
+ self.enabled = True
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToSelf(self.op, opts)
+ return self
+
+class FC6_Vnc(FC3_Vnc):
+ removedKeywords = FC3_Vnc.removedKeywords + ["connect"]
+ removedAttrs = FC3_Vnc.removedAttrs + ["connect"]
+
+ def __init__(self, writePriority=0, host="", port="", *args, **kwargs):
+ FC3_Vnc.__init__(self, writePriority, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.host = kwargs.get("host", "")
+ self.port = kwargs.get("port", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if not self.enabled:
+ return retval
+
+ retval += "vnc"
+
+ if self.host != "":
+ retval += " --host=%s" % self.host
+
+ if self.port != "":
+ retval += " --port=%s" % self.port
+ if self.password != "":
+ retval += " --password=%s" % self.password
+
+ return retval + "\n"
+
+ def _getParser(self):
+ def connect_cb (option, opt_str, value, parser):
+ cargs = value.split(":")
+ parser.values.ensure_value("host", cargs[0])
+
+ if len(cargs) > 1:
+ parser.values.ensure_value("port", cargs[1])
+
+ op = FC3_Vnc._getParser(self)
+ op.add_option("--connect", action="callback", callback=connect_cb,
+ nargs=1, type="string")
+ op.add_option("--host", dest="host")
+ op.add_option("--port", dest="port")
+ return op
+
+class F9_Vnc(FC6_Vnc):
+ removedKeywords = FC6_Vnc.removedKeywords
+ removedAttrs = FC6_Vnc.removedAttrs
+
+ def _getParser(self):
+ op = FC6_Vnc._getParser(self)
+ op.remove_option("--connect")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/volgroup.py b/mic/3rdparty/pykickstart/commands/volgroup.py
new file mode 100644
index 0000000..255c47f
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/volgroup.py
@@ -0,0 +1,102 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_VolGroupData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.format = kwargs.get("format", True)
+ self.pesize = kwargs.get("pesize", 32768)
+ self.preexist = kwargs.get("preexist", False)
+ self.vgname = kwargs.get("vgname", "")
+ self.physvols = kwargs.get("physvols", [])
+
+ def __eq__(self, y):
+ return self.vgname == y.vgname
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "volgroup %s" % self.vgname
+
+ if not self.format:
+ retval += " --noformat"
+ if self.pesize != 0:
+ retval += " --pesize=%d" % self.pesize
+ if self.preexist:
+ retval += " --useexisting"
+
+ return retval + " " + " ".join(self.physvols) + "\n"
+
+class FC3_VolGroup(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=132, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.vgList = kwargs.get("vgList", [])
+
+ def __str__(self):
+ retval = ""
+ for vg in self.vgList:
+ retval += vg.__str__()
+
+ return retval
+
+ def _getParser(self):
+ # Have to be a little more complicated to set two values.
+ def vg_cb (option, opt_str, value, parser):
+ parser.values.format = False
+ parser.values.preexist = True
+
+ op = KSOptionParser()
+ op.add_option("--noformat", action="callback", callback=vg_cb,
+ dest="format", default=True, nargs=0)
+ op.add_option("--pesize", dest="pesize", type="int", nargs=1,
+ default=32768)
+ op.add_option("--useexisting", dest="preexist", action="store_true",
+ default=False)
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ vg = self.handler.VolGroupData()
+ self._setToObj(self.op, opts, vg)
+ vg.lineno = self.lineno
+ vg.vgname = extra[0]
+ vg.physvols = extra[1:]
+
+ # Check for duplicates in the data list.
+ if vg in self.dataList():
+ warnings.warn(_("A volgroup with the name %s has already been defined.") % vg.vgname)
+
+ return vg
+
+ def dataList(self):
+ return self.vgList
diff --git a/mic/3rdparty/pykickstart/commands/xconfig.py b/mic/3rdparty/pykickstart/commands/xconfig.py
new file mode 100644
index 0000000..644ee86
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/xconfig.py
@@ -0,0 +1,184 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_XConfig(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.card = kwargs.get("card", "")
+ self.defaultdesktop = kwargs.get("defaultdesktop", "")
+ self.depth = kwargs.get("depth", 0)
+ self.hsync = kwargs.get("hsync", "")
+ self.monitor = kwargs.get("monitor", "")
+ self.noProbe = kwargs.get("noProbe", False)
+ self.resolution = kwargs.get("resolution", "")
+ self.server = kwargs.get("server", "")
+ self.startX = kwargs.get("startX", False)
+ self.videoRam = kwargs.get("videoRam", "")
+ self.vsync = kwargs.get("vsync", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.card != "":
+ retval += " --card=%s" % self.card
+ if self.defaultdesktop != "":
+ retval += " --defaultdesktop=%s" % self.defaultdesktop
+ if self.depth != 0:
+ retval += " --depth=%d" % self.depth
+ if self.hsync != "":
+ retval += " --hsync=%s" % self.hsync
+ if self.monitor != "":
+ retval += " --monitor=%s" % self.monitor
+ if self.noProbe:
+ retval += " --noprobe"
+ if self.resolution != "":
+ retval += " --resolution=%s" % self.resolution
+ if self.server != "":
+ retval += " --server=%s" % self.server
+ if self.startX:
+ retval += " --startxonboot"
+ if self.videoRam != "":
+ retval += " --videoram=%s" % self.videoRam
+ if self.vsync != "":
+ retval += " --vsync=%s" % self.vsync
+
+ if retval != "":
+ retval = "# X Window System configuration information\nxconfig %s\n" % retval
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--card")
+ op.add_option("--defaultdesktop")
+ op.add_option("--depth", action="store", type="int", nargs=1)
+ op.add_option("--hsync")
+ op.add_option("--monitor")
+ op.add_option("--noprobe", dest="noProbe", action="store_true",
+ default=False)
+ op.add_option("--resolution")
+ op.add_option("--server")
+ op.add_option("--startxonboot", dest="startX", action="store_true",
+ default=False)
+ op.add_option("--videoram", dest="videoRam")
+ op.add_option("--vsync")
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ if extra:
+ mapping = {"command": "xconfig", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
+
+ self._setToSelf(self.op, opts)
+ return self
+
+class FC6_XConfig(FC3_XConfig):
+ removedKeywords = FC3_XConfig.removedKeywords + ["card", "hsync", "monitor", "noProbe", "vsync"]
+ removedAttrs = FC3_XConfig.removedAttrs + ["card", "hsync", "monitor", "noProbe", "vsync"]
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ FC3_XConfig.__init__(self, writePriority, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ self.driver = kwargs.get("driver", "")
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if hasattr(self, "driver") and self.driver != "":
+ retval += " --driver=%s" % self.driver
+ if self.defaultdesktop != "":
+ retval += " --defaultdesktop=%s" % self.defaultdesktop
+ if self.depth != 0:
+ retval += " --depth=%d" % self.depth
+ if hasattr(self, "resolution") and self.resolution != "":
+ retval += " --resolution=%s" % self.resolution
+ if self.startX:
+ retval += " --startxonboot"
+ if hasattr(self, "videoRam") and self.videoRam != "":
+ retval += " --videoram=%s" % self.videoRam
+
+ if retval != "":
+ retval = "# X Window System configuration information\nxconfig %s\n" % retval
+
+ return retval
+
+ def _getParser(self):
+ op = FC3_XConfig._getParser(self)
+ op.add_option("--card", deprecated=1)
+ op.add_option("--driver", dest="driver")
+ op.add_option("--hsync", deprecated=1)
+ op.add_option("--monitor", deprecated=1)
+ op.add_option("--noprobe", deprecated=1)
+ op.add_option("--vsync", deprecated=1)
+ return op
+
+class F9_XConfig(FC6_XConfig):
+ removedKeywords = FC6_XConfig.removedKeywords
+ removedAttrs = FC6_XConfig.removedAttrs
+
+ def _getParser(self):
+ op = FC6_XConfig._getParser(self)
+ op.remove_option("--card")
+ op.remove_option("--hsync")
+ op.remove_option("--monitor")
+ op.remove_option("--noprobe")
+ op.remove_option("--vsync")
+ return op
+
+class F10_XConfig(F9_XConfig):
+ removedKeywords = F9_XConfig.removedKeywords + ["driver", "resolution", "videoRam"]
+ removedAttrs = F9_XConfig.removedAttrs + ["driver", "resolution", "videoRam"]
+
+ def __init__(self, writePriority=0, *args, **kwargs):
+ F9_XConfig.__init__(self, writePriority, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ def _getParser(self):
+ op = F9_XConfig._getParser(self)
+ op.add_option("--driver", deprecated=1)
+ op.add_option("--depth", deprecated=1)
+ op.add_option("--resolution", deprecated=1)
+ op.add_option("--videoram", deprecated=1)
+ return op
+
+class F14_XConfig(F10_XConfig):
+ removedKeywords = F10_XConfig.removedKeywords
+ removedAttrs = F10_XConfig.removedAttrs
+
+ def _getParser(self):
+ op = F10_XConfig._getParser(self)
+ op.remove_option("--driver")
+ op.remove_option("--depth")
+ op.remove_option("--resolution")
+ op.remove_option("--videoram")
+ return op
diff --git a/mic/3rdparty/pykickstart/commands/zerombr.py b/mic/3rdparty/pykickstart/commands/zerombr.py
new file mode 100644
index 0000000..79555a9
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/zerombr.py
@@ -0,0 +1,69 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+import warnings
+
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_ZeroMbr(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=110, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+ self.zerombr = kwargs.get("zerombr", False)
+
+ def __str__(self):
+ retval = KickstartCommand.__str__(self)
+
+ if self.zerombr:
+ retval += "# Clear the Master Boot Record\nzerombr\n"
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ return op
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 0:
+ warnings.warn(_("Ignoring deprecated option on line %s: The zerombr command no longer takes any options. In future releases, this will result in a fatal error from kickstart. Please modify your kickstart file to remove any options.") % self.lineno, DeprecationWarning)
+
+ self.zerombr = True
+ return self
+
+class F9_ZeroMbr(FC3_ZeroMbr):
+ removedKeywords = FC3_ZeroMbr.removedKeywords
+ removedAttrs = FC3_ZeroMbr.removedAttrs
+
+ def parse(self, args):
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+
+ if len(extra) > 0:
+ raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "zerombr")
+
+ self.zerombr = True
+ return self
diff --git a/mic/3rdparty/pykickstart/commands/zfcp.py b/mic/3rdparty/pykickstart/commands/zfcp.py
new file mode 100644
index 0000000..1ed2694
--- /dev/null
+++ b/mic/3rdparty/pykickstart/commands/zfcp.py
@@ -0,0 +1,134 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.options import *
+
+import gettext
+import warnings
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class FC3_ZFCPData(BaseData):
+ removedKeywords = BaseData.removedKeywords
+ removedAttrs = BaseData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ BaseData.__init__(self, *args, **kwargs)
+ self.devnum = kwargs.get("devnum", "")
+ self.wwpn = kwargs.get("wwpn", "")
+ self.fcplun = kwargs.get("fcplun", "")
+ self.scsiid = kwargs.get("scsiid", "")
+ self.scsilun = kwargs.get("scsilun", "")
+
+ def __eq__(self, y):
+ return self.devnum == y.devnum and self.wwpn == y.wwpn and \
+ self.fcplun == y.fcplun and self.scsiid == y.scsiid and \
+ self.scsilun == y.scsilun
+
+ def __str__(self):
+ retval = BaseData.__str__(self)
+ retval += "zfcp"
+
+ if self.devnum != "":
+ retval += " --devnum=%s" % self.devnum
+ if self.wwpn != "":
+ retval += " --wwpn=%s" % self.wwpn
+ if self.fcplun != "":
+ retval += " --fcplun=%s" % self.fcplun
+ if hasattr(self, "scsiid") and self.scsiid != "":
+ retval += " --scsiid=%s" % self.scsiid
+ if hasattr(self, "scsilun") and self.scsilun != "":
+ retval += " --scsilun=%s" % self.scsilun
+
+ return retval + "\n"
+
+class F12_ZFCPData(FC3_ZFCPData):
+ removedKeywords = FC3_ZFCPData.removedKeywords + ["scsiid", "scsilun"]
+ removedAttrs = FC3_ZFCPData.removedAttrs + ["scsiid", "scsilun"]
+
+ def __init__(self, *args, **kwargs):
+ FC3_ZFCPData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+F14_ZFCPData = F12_ZFCPData
+
+class FC3_ZFCP(KickstartCommand):
+ removedKeywords = KickstartCommand.removedKeywords
+ removedAttrs = KickstartCommand.removedAttrs
+
+ def __init__(self, writePriority=71, *args, **kwargs):
+ KickstartCommand.__init__(self, writePriority, *args, **kwargs)
+ self.op = self._getParser()
+
+ self.zfcp = kwargs.get("zfcp", [])
+
+ def __str__(self):
+ retval = ""
+ for zfcp in self.zfcp:
+ retval += zfcp.__str__()
+
+ return retval
+
+ def _getParser(self):
+ op = KSOptionParser()
+ op.add_option("--devnum", dest="devnum", required=1)
+ op.add_option("--fcplun", dest="fcplun", required=1)
+ op.add_option("--scsiid", dest="scsiid", required=1)
+ op.add_option("--scsilun", dest="scsilun", required=1)
+ op.add_option("--wwpn", dest="wwpn", required=1)
+ return op
+
+ def parse(self, args):
+ zd = self.handler.ZFCPData()
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ self._setToObj(self.op, opts, zd)
+ zd.lineno = self.lineno
+
+ # Check for duplicates in the data list.
+ if zd in self.dataList():
+ warnings.warn(_("A zfcp with this information has already been defined."))
+
+ return zd
+
+ def dataList(self):
+ return self.zfcp
+
+class F12_ZFCP(FC3_ZFCP):
+ removedKeywords = FC3_ZFCP.removedKeywords
+ removedAttrs = FC3_ZFCP.removedAttrs + ["scsiid", "scsilun"]
+
+ def __init__(self, *args, **kwargs):
+ FC3_ZFCP.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+
+ def _getParser(self):
+ op = FC3_ZFCP._getParser(self)
+ op.add_option("--scsiid", deprecated=1)
+ op.add_option("--scsilun", deprecated=1)
+ return op
+
+class F14_ZFCP(F12_ZFCP):
+ removedKeywords = F12_ZFCP.removedKeywords
+ removedAttrs = F12_ZFCP.removedAttrs
+
+ def _getParser(self):
+ op = F12_ZFCP._getParser(self)
+ op.remove_option("--scsiid")
+ op.remove_option("--scsilun")
+ return op
diff --git a/mic/3rdparty/pykickstart/constants.py b/mic/3rdparty/pykickstart/constants.py
new file mode 100644
index 0000000..5e12fc8
--- /dev/null
+++ b/mic/3rdparty/pykickstart/constants.py
@@ -0,0 +1,57 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005-2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+CLEARPART_TYPE_LINUX = 0
+CLEARPART_TYPE_ALL = 1
+CLEARPART_TYPE_NONE = 2
+
+DISPLAY_MODE_CMDLINE = 0
+DISPLAY_MODE_GRAPHICAL = 1
+DISPLAY_MODE_TEXT = 2
+
+FIRSTBOOT_DEFAULT = 0
+FIRSTBOOT_SKIP = 1
+FIRSTBOOT_RECONFIG = 2
+
+KS_MISSING_PROMPT = 0
+KS_MISSING_IGNORE = 1
+
+SELINUX_DISABLED = 0
+SELINUX_ENFORCING = 1
+SELINUX_PERMISSIVE = 2
+
+KS_SCRIPT_PRE = 0
+KS_SCRIPT_POST = 1
+KS_SCRIPT_TRACEBACK = 2
+
+KS_WAIT = 0
+KS_REBOOT = 1
+KS_SHUTDOWN = 2
+
+KS_INSTKEY_SKIP = -99
+
+BOOTPROTO_DHCP = "dhcp"
+BOOTPROTO_BOOTP = "bootp"
+BOOTPROTO_STATIC = "static"
+BOOTPROTO_QUERY = "query"
+BOOTPROTO_IBFT = "ibft"
+
+GROUP_REQUIRED = 0
+GROUP_DEFAULT = 1
+GROUP_ALL = 2
diff --git a/mic/3rdparty/pykickstart/errors.py b/mic/3rdparty/pykickstart/errors.py
new file mode 100644
index 0000000..a234d99
--- /dev/null
+++ b/mic/3rdparty/pykickstart/errors.py
@@ -0,0 +1,103 @@
+#
+# errors.py: Kickstart error handling.
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Error handling classes and functions.
+
+This module exports a single function:
+
+ formatErrorMsg - Properly formats an error message.
+
+It also exports several exception classes:
+
+ KickstartError - A generic exception class.
+
+ KickstartParseError - An exception for errors relating to parsing.
+
+ KickstartValueError - An exception for errors relating to option
+ processing.
+
+ KickstartVersionError - An exception for errors relating to unsupported
+ syntax versions.
+"""
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+def formatErrorMsg(lineno, msg=""):
+ """Properly format the error message msg for inclusion in an exception."""
+ if msg != "":
+ mapping = {"lineno": lineno, "msg": msg}
+ return _("The following problem occurred on line %(lineno)s of the kickstart file:\n\n%(msg)s\n") % mapping
+ else:
+ return _("There was a problem reading from line %s of the kickstart file") % lineno
+
+class KickstartError(Exception):
+ """A generic exception class for unspecific error conditions."""
+ def __init__(self, val = ""):
+ """Create a new KickstartError exception instance with the descriptive
+ message val. val should be the return value of formatErrorMsg.
+ """
+ Exception.__init__(self)
+ self.value = val
+
+ def __str__ (self):
+ return self.value
+
+class KickstartParseError(KickstartError):
+ """An exception class for errors when processing the input file, such as
+ unknown options, commands, or sections.
+ """
+ def __init__(self, msg):
+ """Create a new KickstartParseError exception instance with the
+ descriptive message val. val should be the return value of
+ formatErrorMsg.
+ """
+ KickstartError.__init__(self, msg)
+
+ def __str__(self):
+ return self.value
+
+class KickstartValueError(KickstartError):
+ """An exception class for errors when processing arguments to commands,
+ such as too many arguments, too few arguments, or missing required
+ arguments.
+ """
+ def __init__(self, msg):
+ """Create a new KickstartValueError exception instance with the
+ descriptive message val. val should be the return value of
+ formatErrorMsg.
+ """
+ KickstartError.__init__(self, msg)
+
+ def __str__ (self):
+ return self.value
+
+class KickstartVersionError(KickstartError):
+ """An exception class for errors related to using an incorrect version of
+ kickstart syntax.
+ """
+ def __init__(self, msg):
+ """Create a new KickstartVersionError exception instance with the
+ descriptive message val. val should be the return value of
+ formatErrorMsg.
+ """
+ KickstartError.__init__(self, msg)
+
+ def __str__ (self):
+ return self.value
diff --git a/mic/3rdparty/pykickstart/handlers/__init__.py b/mic/3rdparty/pykickstart/handlers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/__init__.py
diff --git a/mic/3rdparty/pykickstart/handlers/control.py b/mic/3rdparty/pykickstart/handlers/control.py
new file mode 100644
index 0000000..d8c8f2b
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/control.py
@@ -0,0 +1,1307 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007, 2008, 2009, 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.version import *
+from pykickstart.commands import *
+
+# This map is keyed on kickstart syntax version as provided by
+# pykickstart.version. Within each sub-dict is a mapping from command name
+# to the class that handles it. This is an onto mapping - that is, multiple
+# command names can map to the same class. However, the Handler will ensure
+# that only one instance of each class ever exists.
+commandMap = {
+ FC3: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC3_Bootloader,
+ "cdrom": method.FC3_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "driverdisk": driverdisk.FC3_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC3_Reboot,
+ "harddrive": method.FC3_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC3_LangSupport,
+ "lilo": bootloader.FC3_Bootloader,
+ "lilocheck": lilocheck.FC3_LiloCheck,
+ "logvol": logvol.FC3_LogVol,
+ "monitor": monitor.FC3_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "network": network.FC3_Network,
+ "nfs": method.FC3_Method,
+ "part": partition.FC3_Partition,
+ "partition": partition.FC3_Partition,
+ "poweroff": reboot.FC3_Reboot,
+ "raid": raid.FC3_Raid,
+ "reboot": reboot.FC3_Reboot,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "shutdown": reboot.FC3_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC3_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC3_Method,
+ "vnc": vnc.FC3_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC3_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on fc3
+ FC4: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC4_Bootloader,
+ "cdrom": method.FC3_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC3_Reboot,
+ "harddrive": method.FC3_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC3_LangSupport,
+ "logvol": logvol.FC4_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC3_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "network": network.FC4_Network,
+ "nfs": method.FC3_Method,
+ "part": partition.FC4_Partition,
+ "partition": partition.FC4_Partition,
+ "poweroff": reboot.FC3_Reboot,
+ "raid": raid.FC4_Raid,
+ "reboot": reboot.FC3_Reboot,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "shutdown": reboot.FC3_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC3_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC3_Method,
+ "vnc": vnc.FC3_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC3_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on fc4
+ FC5: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC4_Bootloader,
+ "cdrom": method.FC3_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC3_Reboot,
+ "harddrive": method.FC3_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC5_LangSupport,
+ "logvol": logvol.FC4_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC3_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "network": network.FC4_Network,
+ "nfs": method.FC3_Method,
+ "part": partition.FC4_Partition,
+ "partition": partition.FC4_Partition,
+ "poweroff": reboot.FC3_Reboot,
+ "raid": raid.FC5_Raid,
+ "reboot": reboot.FC3_Reboot,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "shutdown": reboot.FC3_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC3_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC3_Method,
+ "vnc": vnc.FC3_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC3_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on fc5
+ FC6: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC4_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.FC6_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC5_LangSupport,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.FC4_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC6_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.FC6_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.FC4_Partition,
+ "partition": partition.FC4_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.FC5_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.FC6_Repo,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "user": user.FC6_User,
+ "url": method.FC6_Method,
+ "vnc": vnc.FC6_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC6_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on fc6
+ F7: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC4_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.FC6_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.FC4_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC6_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.FC6_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.FC4_Partition,
+ "partition": partition.FC4_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F7_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.FC6_Repo,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.FC6_User,
+ "vnc": vnc.FC6_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC6_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f7
+ F8: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F8_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.FC6_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.FC4_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC6_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F8_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.FC4_Partition,
+ "partition": partition.FC4_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F7_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F8_Repo,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.F8_User,
+ "vnc": vnc.FC6_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC6_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f8
+ F9: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F9_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F8_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.F9_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.FC6_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F9_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC6_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.F9_Partition,
+ "partition": partition.F9_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F9_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F8_Repo,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.F8_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F9_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f9
+ F10: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F9_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F8_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.F10_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F9_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.F9_Partition,
+ "partition": partition.F9_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F9_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F8_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.F8_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F10_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f10
+ F11: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F9_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F8_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.F10_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F9_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.F11_Partition,
+ "partition": partition.F11_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F9_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F11_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.F8_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F10_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f11
+ F12: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F12_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F12_DriverDisk,
+ "fcoe": fcoe.F12_Fcoe,
+ "firewall": firewall.F10_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F12_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.F12_Partition,
+ "partition": partition.F12_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F12_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F11_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.FC6_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F10_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F12_ZFCP,
+ },
+
+ # based on f12
+ F13: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F12_Bootloader,
+ "cdrom": method.F13_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F12_DriverDisk,
+ "fcoe": fcoe.F13_Fcoe,
+ "firewall": firewall.F10_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.F13_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F12_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.F13_Method,
+ "part": partition.F12_Partition,
+ "partition": partition.F12_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F13_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F13_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "sshpw": sshpw.F13_SshPw,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.F13_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F10_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F12_ZFCP,
+ },
+
+ # based on f13
+ F14: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F14_Bootloader,
+ "cdrom": method.F14_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F14_DriverDisk,
+ "fcoe": fcoe.F13_Fcoe,
+ "firewall": firewall.F14_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.F14_Method,
+ "ignoredisk": ignoredisk.F14_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "interactive": interactive.F14_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F14_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.F14_Method,
+ "part": partition.F14_Partition,
+ "partition": partition.F14_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F14_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F14_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "sshpw": sshpw.F13_SshPw,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.F14_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F14_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F14_ZFCP,
+ },
+
+ # based on f14
+ F15: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F15_Bootloader,
+ "cdrom": method.F14_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F14_DriverDisk,
+ "fcoe": fcoe.F13_Fcoe,
+ "firewall": firewall.F14_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.F14_Method,
+ "ignoredisk": ignoredisk.F14_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F15_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F9_Network,
+ "nfs": method.F14_Method,
+ "part": partition.F14_Partition,
+ "partition": partition.F14_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F15_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F15_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "sshpw": sshpw.F13_SshPw,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.F14_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F14_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F14_ZFCP,
+ },
+
+ # based on f15
+ F16: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.F15_Bootloader,
+ "cdrom": method.F14_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F14_DriverDisk,
+ "fcoe": fcoe.F13_Fcoe,
+ "firewall": firewall.F14_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.F14_Method,
+ "ignoredisk": ignoredisk.F14_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F15_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.F16_Network,
+ "nfs": method.F14_Method,
+ "part": partition.F14_Partition,
+ "partition": partition.F14_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F15_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.F15_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "sshpw": sshpw.F13_SshPw,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.F14_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F14_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F14_ZFCP,
+ },
+
+ # based on fc1
+ RHEL3: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC3_Bootloader,
+ "cdrom": method.FC3_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "driverdisk": driverdisk.FC3_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC3_Reboot,
+ "harddrive": method.FC3_Method,
+ "ignoredisk": ignoredisk.FC3_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC3_LangSupport,
+ "lilo": bootloader.FC3_Bootloader,
+ "lilocheck": lilocheck.FC3_LiloCheck,
+ "logvol": logvol.FC3_LogVol,
+ "monitor": monitor.FC3_Monitor,
+ "mouse": mouse.RHEL3_Mouse,
+ "network": network.FC3_Network,
+ "nfs": method.FC3_Method,
+ "part": partition.FC3_Partition,
+ "partition": partition.FC3_Partition,
+ "poweroff": reboot.FC3_Reboot,
+ "raid": raid.FC3_Raid,
+ "reboot": reboot.FC3_Reboot,
+ "rootpw": rootpw.FC3_RootPw,
+ "shutdown": reboot.FC3_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC3_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC3_Method,
+ "vnc": vnc.FC3_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC3_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ },
+
+ # based on fc3
+ RHEL4: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.FC3_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.FC3_Bootloader,
+ "cdrom": method.FC3_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "driverdisk": driverdisk.FC4_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC3_Reboot,
+ "harddrive": method.FC3_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC3_LangSupport,
+ "lilo": bootloader.FC3_Bootloader,
+ "lilocheck": lilocheck.FC3_LiloCheck,
+ "logvol": logvol.FC3_LogVol,
+ "monitor": monitor.FC3_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "network": network.RHEL4_Network,
+ "nfs": method.FC3_Method,
+ "part": partition.FC3_Partition,
+ "partition": partition.FC3_Partition,
+ "poweroff": reboot.FC3_Reboot,
+ "raid": raid.FC3_Raid,
+ "reboot": reboot.FC3_Reboot,
+ "rootpw": rootpw.FC3_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "shutdown": reboot.FC3_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC3_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "url": method.FC3_Method,
+ "vnc": vnc.FC3_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC3_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on fc6
+ RHEL5: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F9_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.RHEL5_Bootloader,
+ "cdrom": method.FC6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.FC3_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F12_DriverDisk,
+ "firewall": firewall.FC3_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.FC6_Method,
+ "ignoredisk": ignoredisk.F8_IgnoreDisk,
+ "install": upgrade.FC3_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.FC6_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "key": key.RHEL5_Key,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "langsupport": langsupport.FC5_LangSupport,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.RHEL5_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.FC6_Monitor,
+ "mouse": mouse.FC3_Mouse,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.RHEL5_Network,
+ "nfs": method.FC6_Method,
+ "part": partition.RHEL5_Partition,
+ "partition": partition.RHEL5_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.RHEL5_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.FC6_Repo,
+ "rootpw": rootpw.FC3_RootPw,
+ "services": services.FC6_Services,
+ "selinux": selinux.FC3_SELinux,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "upgrade": upgrade.FC3_Upgrade,
+ "user": user.FC6_User,
+ "url": method.FC6_Method,
+ "vnc": vnc.FC6_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.FC6_XConfig,
+ "zerombr": zerombr.FC3_ZeroMbr,
+ "zfcp": zfcp.FC3_ZFCP,
+ },
+
+ # based on f13ish
+ RHEL6: {
+ "auth": authconfig.FC3_Authconfig,
+ "authconfig": authconfig.FC3_Authconfig,
+ "autopart": autopart.F12_AutoPart,
+ "autostep": autostep.FC3_AutoStep,
+ "bootloader": bootloader.RHEL6_Bootloader,
+ "cdrom": method.RHEL6_Method,
+ "clearpart": clearpart.FC3_ClearPart,
+ "cmdline": displaymode.FC3_DisplayMode,
+ "device": device.F8_Device,
+ "deviceprobe": deviceprobe.FC3_DeviceProbe,
+ "dmraid": dmraid.FC6_DmRaid,
+ "driverdisk": driverdisk.F12_DriverDisk,
+ "fcoe": fcoe.F13_Fcoe,
+ "firewall": firewall.F10_Firewall,
+ "firstboot": firstboot.FC3_Firstboot,
+ "graphical": displaymode.FC3_DisplayMode,
+ "group": group.F12_Group,
+ "halt": reboot.FC6_Reboot,
+ "harddrive": method.RHEL6_Method,
+ "ignoredisk": ignoredisk.RHEL6_IgnoreDisk,
+ "install": upgrade.F11_Upgrade,
+ "interactive": interactive.FC3_Interactive,
+ "iscsi": iscsi.F10_Iscsi,
+ "iscsiname": iscsiname.FC6_IscsiName,
+ "keyboard": keyboard.FC3_Keyboard,
+ "lang": lang.FC3_Lang,
+ "logging": logging.FC6_Logging,
+ "logvol": logvol.F12_LogVol,
+ "mediacheck": mediacheck.FC4_MediaCheck,
+ "monitor": monitor.F10_Monitor,
+ "multipath": multipath.FC6_MultiPath,
+ "network": network.RHEL6_Network,
+ "nfs": method.RHEL6_Method,
+ "part": partition.F12_Partition,
+ "partition": partition.F12_Partition,
+ "poweroff": reboot.FC6_Reboot,
+ "raid": raid.F13_Raid,
+ "reboot": reboot.FC6_Reboot,
+ "repo": repo.RHEL6_Repo,
+ "rescue": rescue.F10_Rescue,
+ "rootpw": rootpw.F8_RootPw,
+ "selinux": selinux.FC3_SELinux,
+ "services": services.FC6_Services,
+ "shutdown": reboot.FC6_Reboot,
+ "skipx": skipx.FC3_SkipX,
+ "sshpw": sshpw.F13_SshPw,
+ "text": displaymode.FC3_DisplayMode,
+ "timezone": timezone.FC6_Timezone,
+ "updates": updates.F7_Updates,
+ "upgrade": upgrade.F11_Upgrade,
+ "url": method.RHEL6_Method,
+ "user": user.F12_User,
+ "vnc": vnc.F9_Vnc,
+ "volgroup": volgroup.FC3_VolGroup,
+ "xconfig": xconfig.F10_XConfig,
+ "zerombr": zerombr.F9_ZeroMbr,
+ "zfcp": zfcp.F12_ZFCP,
+ }
+}
+
+# This map is keyed on kickstart syntax version as provided by
+# pykickstart.version. Within each sub-dict is a mapping from a data object
+# name to the class that provides it. This is a bijective mapping - that is,
+# each name maps to exactly one data class and all data classes have a name.
+# More than one instance of each class is allowed to exist, however.
+dataMap = {
+ FC3: {
+ "DriverDiskData": driverdisk.FC3_DriverDiskData,
+ "LogVolData": logvol.FC3_LogVolData,
+ "NetworkData": network.FC3_NetworkData,
+ "PartData": partition.FC3_PartData,
+ "RaidData": raid.FC3_RaidData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ FC4: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "LogVolData": logvol.FC4_LogVolData,
+ "NetworkData": network.FC4_NetworkData,
+ "PartData": partition.FC4_PartData,
+ "RaidData": raid.FC4_RaidData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ FC5: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "LogVolData": logvol.FC4_LogVolData,
+ "NetworkData": network.FC4_NetworkData,
+ "PartData": partition.FC4_PartData,
+ "RaidData": raid.FC5_RaidData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ FC6: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.FC6_IscsiData,
+ "LogVolData": logvol.FC4_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.FC6_NetworkData,
+ "PartData": partition.FC4_PartData,
+ "RaidData": raid.FC5_RaidData,
+ "RepoData": repo.FC6_RepoData,
+ "UserData": user.FC6_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F7: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.FC6_IscsiData,
+ "LogVolData": logvol.FC4_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.FC6_NetworkData,
+ "PartData": partition.FC4_PartData,
+ "RaidData": raid.F7_RaidData,
+ "RepoData": repo.FC6_RepoData,
+ "UserData": user.FC6_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F8: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.FC6_IscsiData,
+ "LogVolData": logvol.FC4_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.FC4_PartData,
+ "RaidData": raid.F7_RaidData,
+ "RepoData": repo.F8_RepoData,
+ "UserData": user.F8_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F9: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.FC6_IscsiData,
+ "LogVolData": logvol.F9_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F9_PartData,
+ "RaidData": raid.F9_RaidData,
+ "RepoData": repo.F8_RepoData,
+ "UserData": user.F8_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F10: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F9_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F9_PartData,
+ "RaidData": raid.F9_RaidData,
+ "RepoData": repo.F8_RepoData,
+ "UserData": user.F8_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F11: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F9_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F11_PartData,
+ "RaidData": raid.F9_RaidData,
+ "RepoData": repo.F11_RepoData,
+ "UserData": user.F8_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ F12: {
+ "DriverDiskData": driverdisk.F12_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F12_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F12_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F12_PartData,
+ "RaidData": raid.F12_RaidData,
+ "RepoData": repo.F11_RepoData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F12_ZFCPData,
+ },
+ F13: {
+ "DriverDiskData": driverdisk.F12_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F13_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F12_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F12_PartData,
+ "RaidData": raid.F13_RaidData,
+ "RepoData": repo.F13_RepoData,
+ "SshPwData": sshpw.F13_SshPwData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F12_ZFCPData,
+ },
+ F14: {
+ "DriverDiskData": driverdisk.F14_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F13_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F14_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F14_PartData,
+ "RaidData": raid.F14_RaidData,
+ "RepoData": repo.F14_RepoData,
+ "SshPwData": sshpw.F13_SshPwData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F14_ZFCPData,
+ },
+ F15: {
+ "DriverDiskData": driverdisk.F14_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F13_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F15_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F8_NetworkData,
+ "PartData": partition.F14_PartData,
+ "RaidData": raid.F15_RaidData,
+ "RepoData": repo.F15_RepoData,
+ "SshPwData": sshpw.F13_SshPwData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F14_ZFCPData,
+ },
+ F16: {
+ "DriverDiskData": driverdisk.F14_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F13_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F15_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.F16_NetworkData,
+ "PartData": partition.F14_PartData,
+ "RaidData": raid.F15_RaidData,
+ "RepoData": repo.F15_RepoData,
+ "SshPwData": sshpw.F13_SshPwData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F14_ZFCPData,
+ },
+ RHEL3: {
+ "DriverDiskData": driverdisk.FC3_DriverDiskData,
+ "LogVolData": logvol.FC3_LogVolData,
+ "NetworkData": network.RHEL4_NetworkData,
+ "PartData": partition.FC3_PartData,
+ "RaidData": raid.FC3_RaidData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ RHEL4: {
+ "DriverDiskData": driverdisk.FC4_DriverDiskData,
+ "LogVolData": logvol.FC3_LogVolData,
+ "NetworkData": network.RHEL4_NetworkData,
+ "PartData": partition.FC3_PartData,
+ "RaidData": raid.FC3_RaidData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ RHEL5: {
+ "DriverDiskData": driverdisk.F12_DriverDiskData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "IscsiData": iscsi.FC6_IscsiData,
+ "LogVolData": logvol.RHEL5_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.FC6_NetworkData,
+ "PartData": partition.RHEL5_PartData,
+ "RaidData": raid.RHEL5_RaidData,
+ "RepoData": repo.FC6_RepoData,
+ "UserData": user.FC6_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.FC3_ZFCPData,
+ },
+ RHEL6: {
+ "DriverDiskData": driverdisk.F12_DriverDiskData,
+ "DeviceData": device.F8_DeviceData,
+ "DmRaidData": dmraid.FC6_DmRaidData,
+ "FcoeData": fcoe.F13_FcoeData,
+ "GroupData": group.F12_GroupData,
+ "IscsiData": iscsi.F10_IscsiData,
+ "LogVolData": logvol.F12_LogVolData,
+ "MultiPathData": multipath.FC6_MultiPathData,
+ "NetworkData": network.RHEL6_NetworkData,
+ "PartData": partition.F12_PartData,
+ "RaidData": raid.F13_RaidData,
+ "RepoData": repo.RHEL6_RepoData,
+ "SshPwData": sshpw.F13_SshPwData,
+ "UserData": user.F12_UserData,
+ "VolGroupData": volgroup.FC3_VolGroupData,
+ "ZFCPData": zfcp.F12_ZFCPData,
+ }
+}
diff --git a/mic/3rdparty/pykickstart/handlers/f10.py b/mic/3rdparty/pykickstart/handlers/f10.py
new file mode 100644
index 0000000..17c8211
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f10.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F10Handler(BaseHandler):
+ version = F10
diff --git a/mic/3rdparty/pykickstart/handlers/f11.py b/mic/3rdparty/pykickstart/handlers/f11.py
new file mode 100644
index 0000000..d21aee3
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f11.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2008 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F11Handler(BaseHandler):
+ version = F11
diff --git a/mic/3rdparty/pykickstart/handlers/f12.py b/mic/3rdparty/pykickstart/handlers/f12.py
new file mode 100644
index 0000000..cea3ece
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f12.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F12Handler(BaseHandler):
+ version = F12
diff --git a/mic/3rdparty/pykickstart/handlers/f13.py b/mic/3rdparty/pykickstart/handlers/f13.py
new file mode 100644
index 0000000..b94c738
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f13.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F13Handler(BaseHandler):
+ version = F13
diff --git a/mic/3rdparty/pykickstart/handlers/f14.py b/mic/3rdparty/pykickstart/handlers/f14.py
new file mode 100644
index 0000000..478f75d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f14.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F14Handler(BaseHandler):
+ version = F14
diff --git a/mic/3rdparty/pykickstart/handlers/f15.py b/mic/3rdparty/pykickstart/handlers/f15.py
new file mode 100644
index 0000000..12aecb4
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f15.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F15Handler(BaseHandler):
+ version = F15
diff --git a/mic/3rdparty/pykickstart/handlers/f16.py b/mic/3rdparty/pykickstart/handlers/f16.py
new file mode 100644
index 0000000..3c52f8d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f16.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F16Handler(BaseHandler):
+ version = F16
diff --git a/mic/3rdparty/pykickstart/handlers/f7.py b/mic/3rdparty/pykickstart/handlers/f7.py
new file mode 100644
index 0000000..5e856ea
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f7.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F7Handler(BaseHandler):
+ version = F7
diff --git a/mic/3rdparty/pykickstart/handlers/f8.py b/mic/3rdparty/pykickstart/handlers/f8.py
new file mode 100644
index 0000000..1a97881
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f8.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F8Handler(BaseHandler):
+ version = F8
diff --git a/mic/3rdparty/pykickstart/handlers/f9.py b/mic/3rdparty/pykickstart/handlers/f9.py
new file mode 100644
index 0000000..116f1b5
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/f9.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class F9Handler(BaseHandler):
+ version = F9
diff --git a/mic/3rdparty/pykickstart/handlers/fc3.py b/mic/3rdparty/pykickstart/handlers/fc3.py
new file mode 100644
index 0000000..a115dc2
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/fc3.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class FC3Handler(BaseHandler):
+ version = FC3
diff --git a/mic/3rdparty/pykickstart/handlers/fc4.py b/mic/3rdparty/pykickstart/handlers/fc4.py
new file mode 100644
index 0000000..fd47b73
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/fc4.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class FC4Handler(BaseHandler):
+ version = FC4
diff --git a/mic/3rdparty/pykickstart/handlers/fc5.py b/mic/3rdparty/pykickstart/handlers/fc5.py
new file mode 100644
index 0000000..bcdc29d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/fc5.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class FC5Handler(BaseHandler):
+ version = FC5
diff --git a/mic/3rdparty/pykickstart/handlers/fc6.py b/mic/3rdparty/pykickstart/handlers/fc6.py
new file mode 100644
index 0000000..c83a929
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/fc6.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class FC6Handler(BaseHandler):
+ version = FC6
diff --git a/mic/3rdparty/pykickstart/handlers/rhel3.py b/mic/3rdparty/pykickstart/handlers/rhel3.py
new file mode 100644
index 0000000..131763c
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/rhel3.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class RHEL3Handler(BaseHandler):
+ version = RHEL3
diff --git a/mic/3rdparty/pykickstart/handlers/rhel4.py b/mic/3rdparty/pykickstart/handlers/rhel4.py
new file mode 100644
index 0000000..3496c43
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/rhel4.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class RHEL4Handler(BaseHandler):
+ version = RHEL4
diff --git a/mic/3rdparty/pykickstart/handlers/rhel5.py b/mic/3rdparty/pykickstart/handlers/rhel5.py
new file mode 100644
index 0000000..abb7a8d
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/rhel5.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class RHEL5Handler(BaseHandler):
+ version = RHEL5
diff --git a/mic/3rdparty/pykickstart/handlers/rhel6.py b/mic/3rdparty/pykickstart/handlers/rhel6.py
new file mode 100644
index 0000000..7202419
--- /dev/null
+++ b/mic/3rdparty/pykickstart/handlers/rhel6.py
@@ -0,0 +1,24 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+from pykickstart.base import *
+from pykickstart.version import *
+
+class RHEL6Handler(BaseHandler):
+ version = RHEL6
diff --git a/mic/3rdparty/pykickstart/ko.py b/mic/3rdparty/pykickstart/ko.py
new file mode 100644
index 0000000..1350d19
--- /dev/null
+++ b/mic/3rdparty/pykickstart/ko.py
@@ -0,0 +1,37 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2009 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Base classes for internal pykickstart use.
+
+The module exports the following important classes:
+
+ KickstartObject - The base class for all classes in pykickstart
+"""
+
+class KickstartObject(object):
+ """The base class for all other classes in pykickstart."""
+ def __init__(self, *args, **kwargs):
+ """Create a new KickstartObject instance. All other classes in
+ pykickstart should be derived from this one. Instance attributes:
+ """
+ pass
+
+ def __str__(self):
+ return ""
diff --git a/mic/3rdparty/pykickstart/options.py b/mic/3rdparty/pykickstart/options.py
new file mode 100644
index 0000000..341c5d7
--- /dev/null
+++ b/mic/3rdparty/pykickstart/options.py
@@ -0,0 +1,204 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Specialized option handling.
+
+This module exports two classes:
+
+ KSOptionParser - A specialized subclass of OptionParser to be used
+ in BaseHandler subclasses.
+
+ KSOption - A specialized subclass of Option.
+"""
+import warnings
+from copy import copy
+from optparse import *
+
+from constants import *
+from errors import *
+from version import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+class KSOptionParser(OptionParser):
+ """A specialized subclass of optparse.OptionParser to handle extra option
+ attribute checking, work error reporting into the KickstartParseError
+ framework, and to turn off the default help.
+ """
+ def exit(self, status=0, msg=None):
+ pass
+
+ def error(self, msg):
+ if self.lineno != None:
+ raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
+ else:
+ raise KickstartParseError, msg
+
+ def keys(self):
+ retval = []
+
+ for opt in self.option_list:
+ if opt not in retval:
+ retval.append(opt.dest)
+
+ return retval
+
+ def _init_parsing_state (self):
+ OptionParser._init_parsing_state(self)
+ self.option_seen = {}
+
+ def check_values (self, values, args):
+ def seen(self, option):
+ return self.option_seen.has_key(option)
+
+ def usedTooNew(self, option):
+ return option.introduced and option.introduced > self.version
+
+ def usedDeprecated(self, option):
+ return option.deprecated
+
+ def usedRemoved(self, option):
+ return option.removed and option.removed <= self.version
+
+ for option in filter(lambda o: isinstance(o, Option), self.option_list):
+ if option.required and not seen(self, option):
+ raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option)
+ elif seen(self, option) and usedTooNew(self, option):
+ mapping = {"option": option, "intro": versionToString(option.introduced),
+ "version": versionToString(self.version)}
+ self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping)
+ elif seen(self, option) and usedRemoved(self, option):
+ mapping = {"option": option, "removed": versionToString(option.removed),
+ "version": versionToString(self.version)}
+
+ if option.removed == self.version:
+ self.error(_("The %(option)s option is no longer supported.") % mapping)
+ else:
+ self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping)
+ elif seen(self, option) and usedDeprecated(self, option):
+ mapping = {"lineno": self.lineno, "option": option}
+ warnings.warn(_("Ignoring deprecated option on line %(lineno)s: The %(option)s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % mapping, DeprecationWarning)
+
+ return (values, args)
+
+ def parse_args(self, *args, **kwargs):
+ if kwargs.has_key("lineno"):
+ self.lineno = kwargs.pop("lineno")
+
+ return OptionParser.parse_args(self, **kwargs)
+
+ def __init__(self, mapping=None, version=None):
+ """Create a new KSOptionParser instance. Each KickstartCommand
+ subclass should create one instance of KSOptionParser, providing
+ at least the lineno attribute. mapping and version are not required.
+ Instance attributes:
+
+ mapping -- A mapping from option strings to different values.
+ version -- The version of the kickstart syntax we are checking
+ against.
+ """
+ OptionParser.__init__(self, option_class=KSOption,
+ add_help_option=False,
+ conflict_handler="resolve")
+ if mapping is None:
+ self.map = {}
+ else:
+ self.map = mapping
+
+ self.lineno = None
+ self.option_seen = {}
+ self.version = version
+
+def _check_ksboolean(option, opt, value):
+ if value.lower() in ("on", "yes", "true", "1"):
+ return True
+ elif value.lower() in ("off", "no", "false", "0"):
+ return False
+ else:
+ mapping = {"opt": opt, "value": value}
+ raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping)
+
+def _check_string(option, opt, value):
+ if len(value) > 2 and value.startswith("--"):
+ mapping = {"opt": opt, "value": value}
+ raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping)
+ else:
+ return value
+
+# Creates a new Option class that supports several new attributes:
+# - required: any option with this attribute must be supplied or an exception
+# is thrown
+# - introduced: the kickstart syntax version that this option first appeared
+# in - an exception will be raised if the option is used and
+# the specified syntax version is less than the value of this
+# attribute
+# - deprecated: the kickstart syntax version that this option was deprecated
+# in - a DeprecationWarning will be thrown if the option is
+# used and the specified syntax version is greater than the
+# value of this attribute
+# - removed: the kickstart syntax version that this option was removed in - an
+# exception will be raised if the option is used and the specified
+# syntax version is greated than the value of this attribute
+# Also creates a new type:
+# - ksboolean: support various kinds of boolean values on an option
+# And two new actions:
+# - map : allows you to define an opt -> val mapping such that dest gets val
+# when opt is seen
+# - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
+# that dest gets a list of vals built up when opt is seen
+class KSOption (Option):
+ ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required']
+ ACTIONS = Option.ACTIONS + ("map", "map_extend",)
+ STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
+
+ TYPES = Option.TYPES + ("ksboolean", "string")
+ TYPE_CHECKER = copy(Option.TYPE_CHECKER)
+ TYPE_CHECKER["ksboolean"] = _check_ksboolean
+ TYPE_CHECKER["string"] = _check_string
+
+ def _check_required(self):
+ if self.required and not self.takes_value():
+ raise OptionError(_("Required flag set for option that doesn't take a value"), self)
+
+ # Make sure _check_required() is called from the constructor!
+ CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
+
+ def process (self, opt, value, values, parser):
+ Option.process(self, opt, value, values, parser)
+ parser.option_seen[self] = 1
+
+ # Override default take_action method to handle our custom actions.
+ def take_action(self, action, dest, opt, value, values, parser):
+ if action == "map":
+ values.ensure_value(dest, parser.map[opt.lstrip('-')])
+ elif action == "map_extend":
+ values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
+ else:
+ Option.take_action(self, action, dest, opt, value, values, parser)
+
+ def takes_value(self):
+ # Deprecated options don't take a value.
+ return Option.takes_value(self) and not self.deprecated
+
+ def __init__(self, *args, **kwargs):
+ self.deprecated = False
+ self.required = False
+ Option.__init__(self, *args, **kwargs)
diff --git a/mic/3rdparty/pykickstart/parser.py b/mic/3rdparty/pykickstart/parser.py
new file mode 100644
index 0000000..840a448
--- /dev/null
+++ b/mic/3rdparty/pykickstart/parser.py
@@ -0,0 +1,702 @@
+#
+# parser.py: Kickstart file parser.
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Main kickstart file processing module.
+
+This module exports several important classes:
+
+ Script - Representation of a single %pre, %post, or %traceback script.
+
+ Packages - Representation of the %packages section.
+
+ KickstartParser - The kickstart file parser state machine.
+"""
+
+from collections import Iterator
+import os
+import shlex
+import sys
+import tempfile
+from copy import copy
+from optparse import *
+from urlgrabber import urlread
+import urlgrabber.grabber as grabber
+
+import constants
+from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg
+from ko import KickstartObject
+from sections import *
+import version
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+STATE_END = "end"
+STATE_COMMANDS = "commands"
+
+ver = version.DEVEL
+
+def _preprocessStateMachine (lineIter):
+ l = None
+ lineno = 0
+
+ # Now open an output kickstart file that we are going to write to one
+ # line at a time.
+ (outF, outName) = tempfile.mkstemp("-ks.cfg", "", "/tmp")
+
+ while True:
+ try:
+ l = lineIter.next()
+ except StopIteration:
+ break
+
+ # At the end of the file?
+ if l == "":
+ break
+
+ lineno += 1
+ url = None
+
+ ll = l.strip()
+ if not ll.startswith("%ksappend"):
+ os.write(outF, l)
+ continue
+
+ # Try to pull down the remote file.
+ try:
+ ksurl = ll.split(' ')[1]
+ except:
+ raise KickstartParseError, formatErrorMsg(lineno, msg=_("Illegal url for %%ksappend: %s") % ll)
+
+ try:
+ url = grabber.urlopen(ksurl)
+ except grabber.URLGrabError, e:
+ raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file: %s") % e.strerror)
+ else:
+ # Sanity check result. Sometimes FTP doesn't catch a file
+ # is missing.
+ try:
+ if url.size < 1:
+ raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file"))
+ except:
+ raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file"))
+
+ # If that worked, write the remote file to the output kickstart
+ # file in one burst. Then close everything up to get ready to
+ # read ahead in the input file. This allows multiple %ksappend
+ # lines to exist.
+ if url is not None:
+ os.write(outF, url.read())
+ url.close()
+
+ # All done - close the temp file and return its location.
+ os.close(outF)
+ return outName
+
+def preprocessFromString (s):
+ """Preprocess the kickstart file, provided as the string str. This
+ method is currently only useful for handling %ksappend lines,
+ which need to be fetched before the real kickstart parser can be
+ run. Returns the location of the complete kickstart file.
+ """
+ i = iter(s.splitlines(True) + [""])
+ rc = _preprocessStateMachine (i.next)
+ return rc
+
+def preprocessKickstart (f):
+ """Preprocess the kickstart file, given by the filename file. This
+ method is currently only useful for handling %ksappend lines,
+ which need to be fetched before the real kickstart parser can be
+ run. Returns the location of the complete kickstart file.
+ """
+ try:
+ fh = urlopen(f)
+ except grabber.URLGrabError, e:
+ raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
+
+ rc = _preprocessStateMachine (iter(fh.readlines()))
+ fh.close()
+ return rc
+
+class PutBackIterator(Iterator):
+ def __init__(self, iterable):
+ self._iterable = iter(iterable)
+ self._buf = None
+
+ def __iter__(self):
+ return self
+
+ def put(self, s):
+ self._buf = s
+
+ def next(self):
+ if self._buf:
+ retval = self._buf
+ self._buf = None
+ return retval
+ else:
+ return self._iterable.next()
+
+###
+### SCRIPT HANDLING
+###
+class Script(KickstartObject):
+ """A class representing a single kickstart script. If functionality beyond
+ just a data representation is needed (for example, a run method in
+ anaconda), Script may be subclassed. Although a run method is not
+ provided, most of the attributes of Script have to do with running the
+ script. Instances of Script are held in a list by the Version object.
+ """
+ def __init__(self, script, *args , **kwargs):
+ """Create a new Script instance. Instance attributes:
+
+ errorOnFail -- If execution of the script fails, should anaconda
+ stop, display an error, and then reboot without
+ running any other scripts?
+ inChroot -- Does the script execute in anaconda's chroot
+ environment or not?
+ interp -- The program that should be used to interpret this
+ script.
+ lineno -- The line number this script starts on.
+ logfile -- Where all messages from the script should be logged.
+ script -- A string containing all the lines of the script.
+ type -- The type of the script, which can be KS_SCRIPT_* from
+ pykickstart.constants.
+ """
+ KickstartObject.__init__(self, *args, **kwargs)
+ self.script = "".join(script)
+
+ self.interp = kwargs.get("interp", "/bin/sh")
+ self.inChroot = kwargs.get("inChroot", False)
+ self.lineno = kwargs.get("lineno", None)
+ self.logfile = kwargs.get("logfile", None)
+ self.errorOnFail = kwargs.get("errorOnFail", False)
+ self.type = kwargs.get("type", constants.KS_SCRIPT_PRE)
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file."""
+ retval = ""
+
+ if self.type == constants.KS_SCRIPT_PRE:
+ retval += '\n%pre'
+ elif self.type == constants.KS_SCRIPT_POST:
+ retval += '\n%post'
+ elif self.type == constants.KS_SCRIPT_TRACEBACK:
+ retval += '\n%traceback'
+
+ if self.interp != "/bin/sh" and self.interp != "":
+ retval += " --interpreter=%s" % self.interp
+ if self.type == constants.KS_SCRIPT_POST and not self.inChroot:
+ retval += " --nochroot"
+ if self.logfile != None:
+ retval += " --logfile %s" % self.logfile
+ if self.errorOnFail:
+ retval += " --erroronfail"
+
+ if self.script.endswith("\n"):
+ if ver >= version.F8:
+ return retval + "\n%s%%end\n" % self.script
+ else:
+ return retval + "\n%s\n" % self.script
+ else:
+ if ver >= version.F8:
+ return retval + "\n%s\n%%end\n" % self.script
+ else:
+ return retval + "\n%s\n" % self.script
+
+
+##
+## PACKAGE HANDLING
+##
+class Group:
+ """A class representing a single group in the %packages section."""
+ def __init__(self, name="", include=constants.GROUP_DEFAULT):
+ """Create a new Group instance. Instance attributes:
+
+ name -- The group's identifier
+ include -- The level of how much of the group should be included.
+ Values can be GROUP_* from pykickstart.constants.
+ """
+ self.name = name
+ self.include = include
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file."""
+ if self.include == constants.GROUP_REQUIRED:
+ return "@%s --nodefaults" % self.name
+ elif self.include == constants.GROUP_ALL:
+ return "@%s --optional" % self.name
+ else:
+ return "@%s" % self.name
+
+ def __cmp__(self, other):
+ if self.name < other.name:
+ return -1
+ elif self.name > other.name:
+ return 1
+ return 0
+
+class Packages(KickstartObject):
+ """A class representing the %packages section of the kickstart file."""
+ def __init__(self, *args, **kwargs):
+ """Create a new Packages instance. Instance attributes:
+
+ addBase -- Should the Base group be installed even if it is
+ not specified?
+ default -- Should the default package set be selected?
+ excludedList -- A list of all the packages marked for exclusion in
+ the %packages section, without the leading minus
+ symbol.
+ excludeDocs -- Should documentation in each package be excluded?
+ groupList -- A list of Group objects representing all the groups
+ specified in the %packages section. Names will be
+ stripped of the leading @ symbol.
+ excludedGroupList -- A list of Group objects representing all the
+ groups specified for removal in the %packages
+ section. Names will be stripped of the leading
+ -@ symbols.
+ handleMissing -- If unknown packages are specified in the %packages
+ section, should it be ignored or not? Values can
+ be KS_MISSING_* from pykickstart.constants.
+ packageList -- A list of all the packages specified in the
+ %packages section.
+ instLangs -- A list of languages to install.
+ """
+ KickstartObject.__init__(self, *args, **kwargs)
+
+ self.addBase = True
+ self.default = False
+ self.excludedList = []
+ self.excludedGroupList = []
+ self.excludeDocs = False
+ self.groupList = []
+ self.handleMissing = constants.KS_MISSING_PROMPT
+ self.packageList = []
+ self.instLangs = None
+
+ def __str__(self):
+ """Return a string formatted for output to a kickstart file."""
+ pkgs = ""
+
+ if not self.default:
+ grps = self.groupList
+ grps.sort()
+ for grp in grps:
+ pkgs += "%s\n" % grp.__str__()
+
+ p = self.packageList
+ p.sort()
+ for pkg in p:
+ pkgs += "%s\n" % pkg
+
+ grps = self.excludedGroupList
+ grps.sort()
+ for grp in grps:
+ pkgs += "-%s\n" % grp.__str__()
+
+ p = self.excludedList
+ p.sort()
+ for pkg in p:
+ pkgs += "-%s\n" % pkg
+
+ if pkgs == "":
+ return ""
+
+ retval = "\n%packages"
+
+ if self.default:
+ retval += " --default"
+ if self.excludeDocs:
+ retval += " --excludedocs"
+ if not self.addBase:
+ retval += " --nobase"
+ if self.handleMissing == constants.KS_MISSING_IGNORE:
+ retval += " --ignoremissing"
+ if self.instLangs:
+ retval += " --instLangs=%s" % self.instLangs
+
+ if ver >= version.F8:
+ return retval + "\n" + pkgs + "\n%end\n"
+ else:
+ return retval + "\n" + pkgs + "\n"
+
+ def _processGroup (self, line):
+ op = OptionParser()
+ op.add_option("--nodefaults", action="store_true", default=False)
+ op.add_option("--optional", action="store_true", default=False)
+
+ (opts, extra) = op.parse_args(args=line.split())
+
+ if opts.nodefaults and opts.optional:
+ raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional")
+
+ # If the group name has spaces in it, we have to put it back together
+ # now.
+ grp = " ".join(extra)
+
+ if opts.nodefaults:
+ self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED))
+ elif opts.optional:
+ self.groupList.append(Group(name=grp, include=constants.GROUP_ALL))
+ else:
+ self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT))
+
+ def add (self, pkgList):
+ """Given a list of lines from the input file, strip off any leading
+ symbols and add the result to the appropriate list.
+ """
+ existingExcludedSet = set(self.excludedList)
+ existingPackageSet = set(self.packageList)
+ newExcludedSet = set()
+ newPackageSet = set()
+
+ excludedGroupList = []
+
+ for pkg in pkgList:
+ stripped = pkg.strip()
+
+ if stripped[0] == "@":
+ self._processGroup(stripped[1:])
+ elif stripped[0] == "-":
+ if stripped[1] == "@":
+ excludedGroupList.append(Group(name=stripped[2:]))
+ else:
+ newExcludedSet.add(stripped[1:])
+ else:
+ newPackageSet.add(stripped)
+
+ # Groups have to be excluded in two different ways (note: can't use
+ # sets here because we have to store objects):
+ excludedGroupNames = map(lambda g: g.name, excludedGroupList)
+
+ # First, an excluded group may be cancelling out a previously given
+ # one. This is often the case when using %include. So there we should
+ # just remove the group from the list.
+ self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList)
+
+ # Second, the package list could have included globs which are not
+ # processed by pykickstart. In that case we need to preserve a list of
+ # excluded groups so whatever tool doing package/group installation can
+ # take appropriate action.
+ self.excludedGroupList.extend(excludedGroupList)
+
+ existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet
+ existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet
+
+ self.packageList = list(existingPackageSet)
+ self.excludedList = list(existingExcludedSet)
+
+
+###
+### PARSER
+###
+class KickstartParser:
+ """The kickstart file parser class as represented by a basic state
+ machine. To create a specialized parser, make a subclass and override
+ any of the methods you care about. Methods that don't need to do
+ anything may just pass. However, _stateMachine should never be
+ overridden.
+ """
+ def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
+ missingIncludeIsFatal=True):
+ """Create a new KickstartParser instance. Instance attributes:
+
+ errorsAreFatal -- Should errors cause processing to halt, or
+ just print a message to the screen? This
+ is most useful for writing syntax checkers
+ that may want to continue after an error is
+ encountered.
+ followIncludes -- If %include is seen, should the included
+ file be checked as well or skipped?
+ handler -- An instance of a BaseHandler subclass. If
+ None, the input file will still be parsed
+ but no data will be saved and no commands
+ will be executed.
+ missingIncludeIsFatal -- Should missing include files be fatal, even
+ if errorsAreFatal is False?
+ """
+ self.errorsAreFatal = errorsAreFatal
+ self.followIncludes = followIncludes
+ self.handler = handler
+ self.currentdir = {}
+ self.missingIncludeIsFatal = missingIncludeIsFatal
+
+ self._state = STATE_COMMANDS
+ self._includeDepth = 0
+ self._line = ""
+
+ self.version = self.handler.version
+
+ global ver
+ ver = self.version
+
+ self._sections = {}
+ self.setupSections()
+
+ def _reset(self):
+ """Reset the internal variables of the state machine for a new kickstart file."""
+ self._state = STATE_COMMANDS
+ self._includeDepth = 0
+
+ def getSection(self, s):
+ """Return a reference to the requested section (s must start with '%'s),
+ or raise KeyError if not found.
+ """
+ return self._sections[s]
+
+ def handleCommand (self, lineno, args):
+ """Given the list of command and arguments, call the Version's
+ dispatcher method to handle the command. Returns the command or
+ data object returned by the dispatcher. This method may be
+ overridden in a subclass if necessary.
+ """
+ if self.handler:
+ self.handler.currentCmd = args[0]
+ self.handler.currentLine = self._line
+ retval = self.handler.dispatcher(args, lineno)
+
+ return retval
+
+ def registerSection(self, obj):
+ """Given an instance of a Section subclass, register the new section
+ with the parser. Calling this method means the parser will
+ recognize your new section and dispatch into the given object to
+ handle it.
+ """
+ if not obj.sectionOpen:
+ raise TypeError, "no sectionOpen given for section %s" % obj
+
+ if not obj.sectionOpen.startswith("%"):
+ raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen
+
+ self._sections[obj.sectionOpen] = obj
+
+ def _finalize(self, obj):
+ """Called at the close of a kickstart section to take any required
+ actions. Internally, this is used to add scripts once we have the
+ whole body read.
+ """
+ obj.finalize()
+ self._state = STATE_COMMANDS
+
+ def _handleSpecialComments(self, line):
+ """Kickstart recognizes a couple special comments."""
+ if self._state != STATE_COMMANDS:
+ return
+
+ # Save the platform for s-c-kickstart.
+ if line[:10] == "#platform=":
+ self.handler.platform = self._line[11:]
+
+ def _readSection(self, lineIter, lineno):
+ obj = self._sections[self._state]
+
+ while True:
+ try:
+ line = lineIter.next()
+ if line == "":
+ # This section ends at the end of the file.
+ if self.version >= version.F8:
+ raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
+
+ self._finalize(obj)
+ except StopIteration:
+ break
+
+ lineno += 1
+
+ # Throw away blank lines and comments, unless the section wants all
+ # lines.
+ if self._isBlankOrComment(line) and not obj.allLines:
+ continue
+
+ if line.startswith("%"):
+ args = shlex.split(line)
+
+ if args and args[0] == "%end":
+ # This is a properly terminated section.
+ self._finalize(obj)
+ break
+ elif args and args[0] == "%ksappend":
+ continue
+ elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]):
+ # This is an unterminated section.
+ if self.version >= version.F8:
+ raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
+
+ # Finish up. We do not process the header here because
+ # kicking back out to STATE_COMMANDS will ensure that happens.
+ lineIter.put(line)
+ lineno -= 1
+ self._finalize(obj)
+ break
+ else:
+ # This is just a line within a section. Pass it off to whatever
+ # section handles it.
+ obj.handleLine(line)
+
+ return lineno
+
+ def _validState(self, st):
+ """Is the given section tag one that has been registered with the parser?"""
+ return st in self._sections.keys()
+
+ def _tryFunc(self, fn):
+ """Call the provided function (which doesn't take any arguments) and
+ do the appropriate error handling. If errorsAreFatal is False, this
+ function will just print the exception and keep going.
+ """
+ try:
+ fn()
+ except Exception, msg:
+ if self.errorsAreFatal:
+ raise
+ else:
+ print msg
+
+ def _isBlankOrComment(self, line):
+ return line.isspace() or line == "" or line.lstrip()[0] == '#'
+
+ def _stateMachine(self, lineIter):
+ # For error reporting.
+ lineno = 0
+
+ while True:
+ # Get the next line out of the file, quitting if this is the last line.
+ try:
+ self._line = lineIter.next()
+ if self._line == "":
+ break
+ except StopIteration:
+ break
+
+ lineno += 1
+
+ # Eliminate blank lines, whitespace-only lines, and comments.
+ if self._isBlankOrComment(self._line):
+ self._handleSpecialComments(self._line)
+ continue
+
+ # Remove any end-of-line comments.
+ sanitized = self._line.split("#")[0]
+
+ # Then split the line.
+ args = shlex.split(sanitized.rstrip())
+
+ if args[0] == "%include":
+ # This case comes up primarily in ksvalidator.
+ if not self.followIncludes:
+ continue
+
+ if len(args) == 1 or not args[1]:
+ raise KickstartParseError, formatErrorMsg(lineno)
+
+ self._includeDepth += 1
+
+ try:
+ self.readKickstart(args[1], reset=False)
+ except KickstartError:
+ # Handle the include file being provided over the
+ # network in a %pre script. This case comes up in the
+ # early parsing in anaconda.
+ if self.missingIncludeIsFatal:
+ raise
+
+ self._includeDepth -= 1
+ continue
+
+ # Now on to the main event.
+ if self._state == STATE_COMMANDS:
+ if args[0] == "%ksappend":
+ # This is handled by the preprocess* functions, so continue.
+ continue
+ elif args[0][0] == '%':
+ # This is the beginning of a new section. Handle its header
+ # here.
+ newSection = args[0]
+ if not self._validState(newSection):
+ raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection))
+
+ self._state = newSection
+ obj = self._sections[self._state]
+ self._tryFunc(lambda: obj.handleHeader(lineno, args))
+
+ # This will handle all section processing, kicking us back
+ # out to STATE_COMMANDS at the end with the current line
+ # being the next section header, etc.
+ lineno = self._readSection(lineIter, lineno)
+ else:
+ # This is a command in the command section. Dispatch to it.
+ self._tryFunc(lambda: self.handleCommand(lineno, args))
+ elif self._state == STATE_END:
+ break
+
+ def readKickstartFromString (self, s, reset=True):
+ """Process a kickstart file, provided as the string str."""
+ if reset:
+ self._reset()
+
+ # Add a "" to the end of the list so the string reader acts like the
+ # file reader and we only get StopIteration when we're after the final
+ # line of input.
+ i = PutBackIterator(s.splitlines(True) + [""])
+ self._stateMachine (i)
+
+ def readKickstart(self, f, reset=True):
+ """Process a kickstart file, given by the filename f."""
+ if reset:
+ self._reset()
+
+ # an %include might not specify a full path. if we don't try to figure
+ # out what the path should have been, then we're unable to find it
+ # requiring full path specification, though, sucks. so let's make
+ # the reading "smart" by keeping track of what the path is at each
+ # include depth.
+ if not os.path.exists(f):
+ if self.currentdir.has_key(self._includeDepth - 1):
+ if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)):
+ f = os.path.join(self.currentdir[self._includeDepth - 1], f)
+
+ cd = os.path.dirname(f)
+ if not cd.startswith("/"):
+ cd = os.path.abspath(cd)
+ self.currentdir[self._includeDepth] = cd
+
+ try:
+ s = urlread(f)
+ except grabber.URLGrabError, e:
+ raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
+
+ self.readKickstartFromString(s, reset=False)
+
+ def setupSections(self):
+ """Install the sections all kickstart files support. You may override
+ this method in a subclass, but should avoid doing so unless you know
+ what you're doing.
+ """
+ self._sections = {}
+
+ # Install the sections all kickstart files support.
+ self.registerSection(PreScriptSection(self.handler, dataObj=Script))
+ self.registerSection(PostScriptSection(self.handler, dataObj=Script))
+ self.registerSection(TracebackScriptSection(self.handler, dataObj=Script))
+ self.registerSection(PackageSection(self.handler))
diff --git a/mic/3rdparty/pykickstart/sections.py b/mic/3rdparty/pykickstart/sections.py
new file mode 100644
index 0000000..44df856
--- /dev/null
+++ b/mic/3rdparty/pykickstart/sections.py
@@ -0,0 +1,244 @@
+#
+# sections.py: Kickstart file sections.
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+This module exports the classes that define a section of a kickstart file. A
+section is a chunk of the file starting with a %tag and ending with a %end.
+Examples of sections include %packages, %pre, and %post.
+
+You may use this module to define your own custom sections which will be
+treated just the same as a predefined one by the kickstart parser. All that
+is necessary is to create a new subclass of Section and call
+parser.registerSection with an instance of your new class.
+"""
+from constants import *
+from options import KSOptionParser
+from version import *
+
+class Section(object):
+ """The base class for defining kickstart sections. You are free to
+ subclass this as appropriate.
+
+ Class attributes:
+
+ allLines -- Does this section require the parser to call handleLine
+ for every line in the section, even blanks and comments?
+ sectionOpen -- The string that denotes the start of this section. You
+ must start your tag with a percent sign.
+ timesSeen -- This attribute is for informational purposes only. It is
+ incremented every time handleHeader is called to keep
+ track of the number of times a section of this type is
+ seen.
+ """
+ allLines = False
+ sectionOpen = ""
+ timesSeen = 0
+
+ def __init__(self, handler, **kwargs):
+ """Create a new Script instance. At the least, you must pass in an
+ instance of a baseHandler subclass.
+
+ Valid kwargs:
+
+ dataObj --
+ """
+ self.handler = handler
+
+ self.version = self.handler.version
+
+ self.dataObj = kwargs.get("dataObj", None)
+
+ def finalize(self):
+ """This method is called when the %end tag for a section is seen. It
+ is not required to be provided.
+ """
+ pass
+
+ def handleLine(self, line):
+ """This method is called for every line of a section. Take whatever
+ action is appropriate. While this method is not required to be
+ provided, not providing it does not make a whole lot of sense.
+
+ Arguments:
+
+ line -- The complete line, with any trailing newline.
+ """
+ pass
+
+ def handleHeader(self, lineno, args):
+ """This method is called when the opening tag for a section is seen.
+ Not all sections will need this method, though all provided with
+ kickstart include one.
+
+ Arguments:
+
+ args -- A list of all strings passed as arguments to the section
+ opening tag.
+ """
+ self.timesSeen += 1
+
+class NullSection(Section):
+ """This defines a section that pykickstart will recognize but do nothing
+ with. If the parser runs across a %section that has no object registered,
+ it will raise an error. Sometimes, you may want to simply ignore those
+ sections instead. This class is useful for that purpose.
+ """
+ def __init__(self, *args, **kwargs):
+ """Create a new NullSection instance. You must pass a sectionOpen
+ parameter (including a leading '%') for the section you wish to
+ ignore.
+ """
+ Section.__init__(self, *args, **kwargs)
+ self.sectionOpen = kwargs.get("sectionOpen")
+
+class ScriptSection(Section):
+ allLines = True
+
+ def __init__(self, *args, **kwargs):
+ Section.__init__(self, *args, **kwargs)
+ self._script = {}
+ self._resetScript()
+
+ def _getParser(self):
+ op = KSOptionParser(self.version)
+ op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
+ default=False)
+ op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
+ op.add_option("--log", "--logfile", dest="log")
+ return op
+
+ def _resetScript(self):
+ self._script = {"interp": "/bin/sh", "log": None, "errorOnFail": False,
+ "lineno": None, "chroot": False, "body": []}
+
+ def handleLine(self, line):
+ self._script["body"].append(line)
+
+ def finalize(self):
+ if " ".join(self._script["body"]).strip() == "":
+ return
+
+ kwargs = {"interp": self._script["interp"],
+ "inChroot": self._script["chroot"],
+ "lineno": self._script["lineno"],
+ "logfile": self._script["log"],
+ "errorOnFail": self._script["errorOnFail"],
+ "type": self._script["type"]}
+
+ s = self.dataObj (self._script["body"], **kwargs)
+ self._resetScript()
+
+ if self.handler:
+ self.handler.scripts.append(s)
+
+ def handleHeader(self, lineno, args):
+ """Process the arguments to a %pre/%post/%traceback header for later
+ setting on a Script instance once the end of the script is found.
+ This method may be overridden in a subclass if necessary.
+ """
+ Section.handleHeader(self, lineno, args)
+ op = self._getParser()
+
+ (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
+
+ self._script["interp"] = opts.interpreter
+ self._script["lineno"] = lineno
+ self._script["log"] = opts.log
+ self._script["errorOnFail"] = opts.errorOnFail
+ if hasattr(opts, "nochroot"):
+ self._script["chroot"] = not opts.nochroot
+
+class PreScriptSection(ScriptSection):
+ sectionOpen = "%pre"
+
+ def _resetScript(self):
+ ScriptSection._resetScript(self)
+ self._script["type"] = KS_SCRIPT_PRE
+
+class PostScriptSection(ScriptSection):
+ sectionOpen = "%post"
+
+ def _getParser(self):
+ op = ScriptSection._getParser(self)
+ op.add_option("--nochroot", dest="nochroot", action="store_true",
+ default=False)
+ return op
+
+ def _resetScript(self):
+ ScriptSection._resetScript(self)
+ self._script["chroot"] = True
+ self._script["type"] = KS_SCRIPT_POST
+
+class TracebackScriptSection(ScriptSection):
+ sectionOpen = "%traceback"
+
+ def _resetScript(self):
+ ScriptSection._resetScript(self)
+ self._script["type"] = KS_SCRIPT_TRACEBACK
+
+class PackageSection(Section):
+ sectionOpen = "%packages"
+
+ def handleLine(self, line):
+ if not self.handler:
+ return
+
+ (h, s, t) = line.partition('#')
+ line = h.rstrip()
+
+ self.handler.packages.add([line])
+
+ def handleHeader(self, lineno, args):
+ """Process the arguments to the %packages header and set attributes
+ on the Version's Packages instance appropriate. This method may be
+ overridden in a subclass if necessary.
+ """
+ Section.handleHeader(self, lineno, args)
+ op = KSOptionParser(version=self.version)
+ op.add_option("--excludedocs", dest="excludedocs", action="store_true",
+ default=False)
+ op.add_option("--ignoremissing", dest="ignoremissing",
+ action="store_true", default=False)
+ op.add_option("--nobase", dest="nobase", action="store_true",
+ default=False)
+ op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
+ deprecated=FC4, removed=F9)
+ op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
+ deprecated=FC4, removed=F9)
+ op.add_option("--default", dest="defaultPackages", action="store_true",
+ default=False, introduced=F7)
+ op.add_option("--instLangs", dest="instLangs", type="string",
+ default="", introduced=F9)
+
+ (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
+
+ self.handler.packages.excludeDocs = opts.excludedocs
+ self.handler.packages.addBase = not opts.nobase
+ if opts.ignoremissing:
+ self.handler.packages.handleMissing = KS_MISSING_IGNORE
+ else:
+ self.handler.packages.handleMissing = KS_MISSING_PROMPT
+
+ if opts.defaultPackages:
+ self.handler.packages.default = True
+
+ if opts.instLangs:
+ self.handler.packages.instLangs = opts.instLangs
diff --git a/mic/3rdparty/pykickstart/urlgrabber/__init__.py b/mic/3rdparty/pykickstart/urlgrabber/__init__.py
new file mode 100644
index 0000000..7bcd9d5
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/__init__.py
@@ -0,0 +1,53 @@
+# 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# Copyright 2002-2006 Michael D. Stenner, Ryan Tomayko
+
+# $Id: __init__.py,v 1.20 2006/09/22 00:58:55 mstenner Exp $
+
+"""A high-level cross-protocol url-grabber.
+
+Using urlgrabber, data can be fetched in three basic ways:
+
+ urlgrab(url) copy the file to the local filesystem
+ urlopen(url) open the remote file and return a file object
+ (like urllib2.urlopen)
+ urlread(url) return the contents of the file as a string
+
+When using these functions (or methods), urlgrabber supports the
+following features:
+
+ * identical behavior for http://, ftp://, and file:// urls
+ * http keepalive - faster downloads of many files by using
+ only a single connection
+ * byte ranges - fetch only a portion of the file
+ * reget - for a urlgrab, resume a partial download
+ * progress meters - the ability to report download progress
+ automatically, even when using urlopen!
+ * throttling - restrict bandwidth usage
+ * retries - automatically retry a download if it fails. The
+ number of retries and failure types are configurable.
+ * authenticated server access for http and ftp
+ * proxy support - support for authenticated http and ftp proxies
+ * mirror groups - treat a list of mirrors as a single source,
+ automatically switching mirrors if there is a failure.
+"""
+
+__version__ = '3.1.0'
+__date__ = '2006/09/21'
+__author__ = 'Michael D. Stenner <mstenner@linux.duke.edu>, ' \
+ 'Ryan Tomayko <rtomayko@naeblis.cx>'
+__url__ = 'http://linux.duke.edu/projects/urlgrabber/'
+
+from grabber import urlgrab, urlopen, urlread
diff --git a/mic/3rdparty/pykickstart/urlgrabber/byterange.py b/mic/3rdparty/pykickstart/urlgrabber/byterange.py
new file mode 100644
index 0000000..001b4e3
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/byterange.py
@@ -0,0 +1,463 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
+
+# $Id: byterange.py,v 1.12 2006/07/20 20:15:58 mstenner Exp $
+
+import os
+import stat
+import urllib
+import urllib2
+import rfc822
+
+DEBUG = None
+
+try:
+ from cStringIO import StringIO
+except ImportError, msg:
+ from StringIO import StringIO
+
+class RangeError(IOError):
+ """Error raised when an unsatisfiable range is requested."""
+ pass
+
+class HTTPRangeHandler(urllib2.BaseHandler):
+ """Handler that enables HTTP Range headers.
+
+ This was extremely simple. The Range header is a HTTP feature to
+ begin with so all this class does is tell urllib2 that the
+ "206 Partial Content" reponse from the HTTP server is what we
+ expected.
+
+ Example:
+ import urllib2
+ import byterange
+
+ range_handler = range.HTTPRangeHandler()
+ opener = urllib2.build_opener(range_handler)
+
+ # install it
+ urllib2.install_opener(opener)
+
+ # create Request and set Range header
+ req = urllib2.Request('http://www.python.org/')
+ req.header['Range'] = 'bytes=30-50'
+ f = urllib2.urlopen(req)
+ """
+
+ def http_error_206(self, req, fp, code, msg, hdrs):
+ # 206 Partial Content Response
+ r = urllib.addinfourl(fp, hdrs, req.get_full_url())
+ r.code = code
+ r.msg = msg
+ return r
+
+ def http_error_416(self, req, fp, code, msg, hdrs):
+ # HTTP's Range Not Satisfiable error
+ raise RangeError('Requested Range Not Satisfiable')
+
+class HTTPSRangeHandler(HTTPRangeHandler):
+ """ Range Header support for HTTPS. """
+
+ def https_error_206(self, req, fp, code, msg, hdrs):
+ return self.http_error_206(req, fp, code, msg, hdrs)
+
+ def https_error_416(self, req, fp, code, msg, hdrs):
+ self.https_error_416(req, fp, code, msg, hdrs)
+
+class RangeableFileObject:
+ """File object wrapper to enable raw range handling.
+ This was implemented primarilary for handling range
+ specifications for file:// urls. This object effectively makes
+ a file object look like it consists only of a range of bytes in
+ the stream.
+
+ Examples:
+ # expose 10 bytes, starting at byte position 20, from
+ # /etc/aliases.
+ >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30))
+ # seek seeks within the range (to position 23 in this case)
+ >>> fo.seek(3)
+ # tell tells where your at _within the range_ (position 3 in
+ # this case)
+ >>> fo.tell()
+ # read EOFs if an attempt is made to read past the last
+ # byte in the range. the following will return only 7 bytes.
+ >>> fo.read(30)
+ """
+
+ def __init__(self, fo, rangetup):
+ """Create a RangeableFileObject.
+ fo -- a file like object. only the read() method need be
+ supported but supporting an optimized seek() is
+ preferable.
+ rangetup -- a (firstbyte,lastbyte) tuple specifying the range
+ to work over.
+ The file object provided is assumed to be at byte offset 0.
+ """
+ self.fo = fo
+ (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup)
+ self.realpos = 0
+ self._do_seek(self.firstbyte)
+
+ def __getattr__(self, name):
+ """This effectively allows us to wrap at the instance level.
+ Any attribute not found in _this_ object will be searched for
+ in self.fo. This includes methods."""
+ if hasattr(self.fo, name):
+ return getattr(self.fo, name)
+ raise AttributeError, name
+
+ def tell(self):
+ """Return the position within the range.
+ This is different from fo.seek in that position 0 is the
+ first byte position of the range tuple. For example, if
+ this object was created with a range tuple of (500,899),
+ tell() will return 0 when at byte position 500 of the file.
+ """
+ return (self.realpos - self.firstbyte)
+
+ def seek(self,offset,whence=0):
+ """Seek within the byte range.
+ Positioning is identical to that described under tell().
+ """
+ assert whence in (0, 1, 2)
+ if whence == 0: # absolute seek
+ realoffset = self.firstbyte + offset
+ elif whence == 1: # relative seek
+ realoffset = self.realpos + offset
+ elif whence == 2: # absolute from end of file
+ # XXX: are we raising the right Error here?
+ raise IOError('seek from end of file not supported.')
+
+ # do not allow seek past lastbyte in range
+ if self.lastbyte and (realoffset >= self.lastbyte):
+ realoffset = self.lastbyte
+
+ self._do_seek(realoffset - self.realpos)
+
+ def read(self, size=-1):
+ """Read within the range.
+ This method will limit the size read based on the range.
+ """
+ size = self._calc_read_size(size)
+ rslt = self.fo.read(size)
+ self.realpos += len(rslt)
+ return rslt
+
+ def readline(self, size=-1):
+ """Read lines within the range.
+ This method will limit the size read based on the range.
+ """
+ size = self._calc_read_size(size)
+ rslt = self.fo.readline(size)
+ self.realpos += len(rslt)
+ return rslt
+
+ def _calc_read_size(self, size):
+ """Handles calculating the amount of data to read based on
+ the range.
+ """
+ if self.lastbyte:
+ if size > -1:
+ if ((self.realpos + size) >= self.lastbyte):
+ size = (self.lastbyte - self.realpos)
+ else:
+ size = (self.lastbyte - self.realpos)
+ return size
+
+ def _do_seek(self,offset):
+ """Seek based on whether wrapped object supports seek().
+ offset is relative to the current position (self.realpos).
+ """
+ assert offset >= 0
+ if not hasattr(self.fo, 'seek'):
+ self._poor_mans_seek(offset)
+ else:
+ self.fo.seek(self.realpos + offset)
+ self.realpos+= offset
+
+ def _poor_mans_seek(self,offset):
+ """Seek by calling the wrapped file objects read() method.
+ This is used for file like objects that do not have native
+ seek support. The wrapped objects read() method is called
+ to manually seek to the desired position.
+ offset -- read this number of bytes from the wrapped
+ file object.
+ raise RangeError if we encounter EOF before reaching the
+ specified offset.
+ """
+ pos = 0
+ bufsize = 1024
+ while pos < offset:
+ if (pos + bufsize) > offset:
+ bufsize = offset - pos
+ buf = self.fo.read(bufsize)
+ if len(buf) != bufsize:
+ raise RangeError('Requested Range Not Satisfiable')
+ pos+= bufsize
+
+class FileRangeHandler(urllib2.FileHandler):
+ """FileHandler subclass that adds Range support.
+ This class handles Range headers exactly like an HTTP
+ server would.
+ """
+ def open_local_file(self, req):
+ import mimetypes
+ import mimetools
+ host = req.get_host()
+ file = req.get_selector()
+ localfile = urllib.url2pathname(file)
+ stats = os.stat(localfile)
+ size = stats[stat.ST_SIZE]
+ modified = rfc822.formatdate(stats[stat.ST_MTIME])
+ mtype = mimetypes.guess_type(file)[0]
+ if host:
+ host, port = urllib.splitport(host)
+ if port or socket.gethostbyname(host) not in self.get_names():
+ raise urllib2.URLError('file not on local host')
+ fo = open(localfile,'rb')
+ brange = req.headers.get('Range',None)
+ brange = range_header_to_tuple(brange)
+ assert brange != ()
+ if brange:
+ (fb,lb) = brange
+ if lb == '': lb = size
+ if fb < 0 or fb > size or lb > size:
+ raise RangeError('Requested Range Not Satisfiable')
+ size = (lb - fb)
+ fo = RangeableFileObject(fo, (fb,lb))
+ headers = mimetools.Message(StringIO(
+ 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
+ (mtype or 'text/plain', size, modified)))
+ return urllib.addinfourl(fo, headers, 'file:'+file)
+
+
+# FTP Range Support
+# Unfortunately, a large amount of base FTP code had to be copied
+# from urllib and urllib2 in order to insert the FTP REST command.
+# Code modifications for range support have been commented as
+# follows:
+# -- range support modifications start/end here
+
+from urllib import splitport, splituser, splitpasswd, splitattr, \
+ unquote, addclosehook, addinfourl
+import ftplib
+import socket
+import sys
+import ftplib
+import mimetypes
+import mimetools
+
+class FTPRangeHandler(urllib2.FTPHandler):
+ def ftp_open(self, req):
+ host = req.get_host()
+ if not host:
+ raise IOError, ('ftp error', 'no host given')
+ host, port = splitport(host)
+ if port is None:
+ port = ftplib.FTP_PORT
+
+ # username/password handling
+ user, host = splituser(host)
+ if user:
+ user, passwd = splitpasswd(user)
+ else:
+ passwd = None
+ host = unquote(host)
+ user = unquote(user or '')
+ passwd = unquote(passwd or '')
+
+ try:
+ host = socket.gethostbyname(host)
+ except socket.error, msg:
+ raise urllib2.URLError(msg)
+ path, attrs = splitattr(req.get_selector())
+ dirs = path.split('/')
+ dirs = map(unquote, dirs)
+ dirs, file = dirs[:-1], dirs[-1]
+ if dirs and not dirs[0]:
+ dirs = dirs[1:]
+ try:
+ fw = self.connect_ftp(user, passwd, host, port, dirs)
+ type = file and 'I' or 'D'
+ for attr in attrs:
+ attr, value = splitattr(attr)
+ if attr.lower() == 'type' and \
+ value in ('a', 'A', 'i', 'I', 'd', 'D'):
+ type = value.upper()
+
+ # -- range support modifications start here
+ rest = None
+ range_tup = range_header_to_tuple(req.headers.get('Range',None))
+ assert range_tup != ()
+ if range_tup:
+ (fb,lb) = range_tup
+ if fb > 0: rest = fb
+ # -- range support modifications end here
+
+ fp, retrlen = fw.retrfile(file, type, rest)
+
+ # -- range support modifications start here
+ if range_tup:
+ (fb,lb) = range_tup
+ if lb == '':
+ if retrlen is None or retrlen == 0:
+ raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.')
+ lb = retrlen
+ retrlen = lb - fb
+ if retrlen < 0:
+ # beginning of range is larger than file
+ raise RangeError('Requested Range Not Satisfiable')
+ else:
+ retrlen = lb - fb
+ fp = RangeableFileObject(fp, (0,retrlen))
+ # -- range support modifications end here
+
+ headers = ""
+ mtype = mimetypes.guess_type(req.get_full_url())[0]
+ if mtype:
+ headers += "Content-Type: %s\n" % mtype
+ if retrlen is not None and retrlen >= 0:
+ headers += "Content-Length: %d\n" % retrlen
+ sf = StringIO(headers)
+ headers = mimetools.Message(sf)
+ return addinfourl(fp, headers, req.get_full_url())
+ except ftplib.all_errors, msg:
+ raise IOError, ('ftp error', msg), sys.exc_info()[2]
+
+ def connect_ftp(self, user, passwd, host, port, dirs):
+ fw = ftpwrapper(user, passwd, host, port, dirs)
+ return fw
+
+class ftpwrapper(urllib.ftpwrapper):
+ # range support note:
+ # this ftpwrapper code is copied directly from
+ # urllib. The only enhancement is to add the rest
+ # argument and pass it on to ftp.ntransfercmd
+ def retrfile(self, file, type, rest=None):
+ self.endtransfer()
+ if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
+ else: cmd = 'TYPE ' + type; isdir = 0
+ try:
+ self.ftp.voidcmd(cmd)
+ except ftplib.all_errors:
+ self.init()
+ self.ftp.voidcmd(cmd)
+ conn = None
+ if file and not isdir:
+ # Use nlst to see if the file exists at all
+ try:
+ self.ftp.nlst(file)
+ except ftplib.error_perm, reason:
+ raise IOError, ('ftp error', reason), sys.exc_info()[2]
+ # Restore the transfer mode!
+ self.ftp.voidcmd(cmd)
+ # Try to retrieve as a file
+ try:
+ cmd = 'RETR ' + file
+ conn = self.ftp.ntransfercmd(cmd, rest)
+ except ftplib.error_perm, reason:
+ if str(reason)[:3] == '501':
+ # workaround for REST not supported error
+ fp, retrlen = self.retrfile(file, type)
+ fp = RangeableFileObject(fp, (rest,''))
+ return (fp, retrlen)
+ elif str(reason)[:3] != '550':
+ raise IOError, ('ftp error', reason), sys.exc_info()[2]
+ if not conn:
+ # Set transfer mode to ASCII!
+ self.ftp.voidcmd('TYPE A')
+ # Try a directory listing
+ if file: cmd = 'LIST ' + file
+ else: cmd = 'LIST'
+ conn = self.ftp.ntransfercmd(cmd)
+ self.busy = 1
+ # Pass back both a suitably decorated object and a retrieval length
+ return (addclosehook(conn[0].makefile('rb'),
+ self.endtransfer), conn[1])
+
+
+####################################################################
+# Range Tuple Functions
+# XXX: These range tuple functions might go better in a class.
+
+_rangere = None
+def range_header_to_tuple(range_header):
+ """Get a (firstbyte,lastbyte) tuple from a Range header value.
+
+ Range headers have the form "bytes=<firstbyte>-<lastbyte>". This
+ function pulls the firstbyte and lastbyte values and returns
+ a (firstbyte,lastbyte) tuple. If lastbyte is not specified in
+ the header value, it is returned as an empty string in the
+ tuple.
+
+ Return None if range_header is None
+ Return () if range_header does not conform to the range spec
+ pattern.
+
+ """
+ global _rangere
+ if range_header is None: return None
+ if _rangere is None:
+ import re
+ _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)')
+ match = _rangere.match(range_header)
+ if match:
+ tup = range_tuple_normalize(match.group(1,2))
+ if tup and tup[1]:
+ tup = (tup[0],tup[1]+1)
+ return tup
+ return ()
+
+def range_tuple_to_header(range_tup):
+ """Convert a range tuple to a Range header value.
+ Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None
+ if no range is needed.
+ """
+ if range_tup is None: return None
+ range_tup = range_tuple_normalize(range_tup)
+ if range_tup:
+ if range_tup[1]:
+ range_tup = (range_tup[0],range_tup[1] - 1)
+ return 'bytes=%s-%s' % range_tup
+
+def range_tuple_normalize(range_tup):
+ """Normalize a (first_byte,last_byte) range tuple.
+ Return a tuple whose first element is guaranteed to be an int
+ and whose second element will be '' (meaning: the last byte) or
+ an int. Finally, return None if the normalized tuple == (0,'')
+ as that is equivelant to retrieving the entire file.
+ """
+ if range_tup is None: return None
+ # handle first byte
+ fb = range_tup[0]
+ if fb in (None,''): fb = 0
+ else: fb = int(fb)
+ # handle last byte
+ try: lb = range_tup[1]
+ except IndexError: lb = ''
+ else:
+ if lb is None: lb = ''
+ elif lb != '': lb = int(lb)
+ # check if range is over the entire file
+ if (fb,lb) == (0,''): return None
+ # check that the range is valid
+ if lb < fb: raise RangeError('Invalid byte range: %s-%s' % (fb,lb))
+ return (fb,lb)
+
diff --git a/mic/3rdparty/pykickstart/urlgrabber/grabber.py b/mic/3rdparty/pykickstart/urlgrabber/grabber.py
new file mode 100644
index 0000000..20e7899
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/grabber.py
@@ -0,0 +1,1477 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
+
+"""A high-level cross-protocol url-grabber.
+
+GENERAL ARGUMENTS (kwargs)
+
+ Where possible, the module-level default is indicated, and legal
+ values are provided.
+
+ copy_local = 0 [0|1]
+
+ ignored except for file:// urls, in which case it specifies
+ whether urlgrab should still make a copy of the file, or simply
+ point to the existing copy. The module level default for this
+ option is 0.
+
+ close_connection = 0 [0|1]
+
+ tells URLGrabber to close the connection after a file has been
+ transfered. This is ignored unless the download happens with the
+ http keepalive handler (keepalive=1). Otherwise, the connection
+ is left open for further use. The module level default for this
+ option is 0 (keepalive connections will not be closed).
+
+ keepalive = 1 [0|1]
+
+ specifies whether keepalive should be used for HTTP/1.1 servers
+ that support it. The module level default for this option is 1
+ (keepalive is enabled).
+
+ progress_obj = None
+
+ a class instance that supports the following methods:
+ po.start(filename, url, basename, length, text)
+ # length will be None if unknown
+ po.update(read) # read == bytes read so far
+ po.end()
+
+ text = None
+
+ specifies an alternativ text item in the beginning of the progress
+ bar line. If not given, the basename of the file is used.
+
+ throttle = 1.0
+
+ a number - if it's an int, it's the bytes/second throttle limit.
+ If it's a float, it is first multiplied by bandwidth. If throttle
+ == 0, throttling is disabled. If None, the module-level default
+ (which can be set on default_grabber.throttle) is used. See
+ BANDWIDTH THROTTLING for more information.
+
+ timeout = None
+
+ a positive float expressing the number of seconds to wait for socket
+ operations. If the value is None or 0.0, socket operations will block
+ forever. Setting this option causes urlgrabber to call the settimeout
+ method on the Socket object used for the request. See the Python
+ documentation on settimeout for more information.
+ http://www.python.org/doc/current/lib/socket-objects.html
+
+ bandwidth = 0
+
+ the nominal max bandwidth in bytes/second. If throttle is a float
+ and bandwidth == 0, throttling is disabled. If None, the
+ module-level default (which can be set on
+ default_grabber.bandwidth) is used. See BANDWIDTH THROTTLING for
+ more information.
+
+ range = None
+
+ a tuple of the form (first_byte, last_byte) describing a byte
+ range to retrieve. Either or both of the values may set to
+ None. If first_byte is None, byte offset 0 is assumed. If
+ last_byte is None, the last byte available is assumed. Note that
+ the range specification is python-like in that (0,10) will yeild
+ the first 10 bytes of the file.
+
+ If set to None, no range will be used.
+
+ reget = None [None|'simple'|'check_timestamp']
+
+ whether to attempt to reget a partially-downloaded file. Reget
+ only applies to .urlgrab and (obviously) only if there is a
+ partially downloaded file. Reget has two modes:
+
+ 'simple' -- the local file will always be trusted. If there
+ are 100 bytes in the local file, then the download will always
+ begin 100 bytes into the requested file.
+
+ 'check_timestamp' -- the timestamp of the server file will be
+ compared to the timestamp of the local file. ONLY if the
+ local file is newer than or the same age as the server file
+ will reget be used. If the server file is newer, or the
+ timestamp is not returned, the entire file will be fetched.
+
+ NOTE: urlgrabber can do very little to verify that the partial
+ file on disk is identical to the beginning of the remote file.
+ You may want to either employ a custom "checkfunc" or simply avoid
+ using reget in situations where corruption is a concern.
+
+ user_agent = 'urlgrabber/VERSION'
+
+ a string, usually of the form 'AGENT/VERSION' that is provided to
+ HTTP servers in the User-agent header. The module level default
+ for this option is "urlgrabber/VERSION".
+
+ http_headers = None
+
+ a tuple of 2-tuples, each containing a header and value. These
+ will be used for http and https requests only. For example, you
+ can do
+ http_headers = (('Pragma', 'no-cache'),)
+
+ ftp_headers = None
+
+ this is just like http_headers, but will be used for ftp requests.
+
+ proxies = None
+
+ a dictionary that maps protocol schemes to proxy hosts. For
+ example, to use a proxy server on host "foo" port 3128 for http
+ and https URLs:
+ proxies={ 'http' : 'http://foo:3128', 'https' : 'http://foo:3128' }
+ note that proxy authentication information may be provided using
+ normal URL constructs:
+ proxies={ 'http' : 'http://user:host@foo:3128' }
+ Lastly, if proxies is None, the default environment settings will
+ be used.
+
+ prefix = None
+
+ a url prefix that will be prepended to all requested urls. For
+ example:
+ g = URLGrabber(prefix='http://foo.com/mirror/')
+ g.urlgrab('some/file.txt')
+ ## this will fetch 'http://foo.com/mirror/some/file.txt'
+ This option exists primarily to allow identical behavior to
+ MirrorGroup (and derived) instances. Note: a '/' will be inserted
+ if necessary, so you cannot specify a prefix that ends with a
+ partial file or directory name.
+
+ opener = None
+
+ Overrides the default urllib2.OpenerDirector provided to urllib2
+ when making requests. This option exists so that the urllib2
+ handler chain may be customized. Note that the range, reget,
+ proxy, and keepalive features require that custom handlers be
+ provided to urllib2 in order to function properly. If an opener
+ option is provided, no attempt is made by urlgrabber to ensure
+ chain integrity. You are responsible for ensuring that any
+ extension handlers are present if said features are required.
+
+ data = None
+
+ Only relevant for the HTTP family (and ignored for other
+ protocols), this allows HTTP POSTs. When the data kwarg is
+ present (and not None), an HTTP request will automatically become
+ a POST rather than GET. This is done by direct passthrough to
+ urllib2. If you use this, you may also want to set the
+ 'Content-length' and 'Content-type' headers with the http_headers
+ option. Note that python 2.2 handles the case of these
+ badly and if you do not use the proper case (shown here), your
+ values will be overridden with the defaults.
+
+
+RETRY RELATED ARGUMENTS
+
+ retry = None
+
+ the number of times to retry the grab before bailing. If this is
+ zero, it will retry forever. This was intentional... really, it
+ was :). If this value is not supplied or is supplied but is None
+ retrying does not occur.
+
+ retrycodes = [-1,2,4,5,6,7]
+
+ a sequence of errorcodes (values of e.errno) for which it should
+ retry. See the doc on URLGrabError for more details on this. You
+ might consider modifying a copy of the default codes rather than
+ building yours from scratch so that if the list is extended in the
+ future (or one code is split into two) you can still enjoy the
+ benefits of the default list. You can do that with something like
+ this:
+
+ retrycodes = urlgrabber.grabber.URLGrabberOptions().retrycodes
+ if 12 not in retrycodes:
+ retrycodes.append(12)
+
+ checkfunc = None
+
+ a function to do additional checks. This defaults to None, which
+ means no additional checking. The function should simply return
+ on a successful check. It should raise URLGrabError on an
+ unsuccessful check. Raising of any other exception will be
+ considered immediate failure and no retries will occur.
+
+ If it raises URLGrabError, the error code will determine the retry
+ behavior. Negative error numbers are reserved for use by these
+ passed in functions, so you can use many negative numbers for
+ different types of failure. By default, -1 results in a retry,
+ but this can be customized with retrycodes.
+
+ If you simply pass in a function, it will be given exactly one
+ argument: a CallbackObject instance with the .url attribute
+ defined and either .filename (for urlgrab) or .data (for urlread).
+ For urlgrab, .filename is the name of the local file. For
+ urlread, .data is the actual string data. If you need other
+ arguments passed to the callback (program state of some sort), you
+ can do so like this:
+
+ checkfunc=(function, ('arg1', 2), {'kwarg': 3})
+
+ if the downloaded file has filename /tmp/stuff, then this will
+ result in this call (for urlgrab):
+
+ function(obj, 'arg1', 2, kwarg=3)
+ # obj.filename = '/tmp/stuff'
+ # obj.url = 'http://foo.com/stuff'
+
+ NOTE: both the "args" tuple and "kwargs" dict must be present if
+ you use this syntax, but either (or both) can be empty.
+
+ failure_callback = None
+
+ The callback that gets called during retries when an attempt to
+ fetch a file fails. The syntax for specifying the callback is
+ identical to checkfunc, except for the attributes defined in the
+ CallbackObject instance. The attributes for failure_callback are:
+
+ exception = the raised exception
+ url = the url we're trying to fetch
+ tries = the number of tries so far (including this one)
+ retry = the value of the retry option
+
+ The callback is present primarily to inform the calling program of
+ the failure, but if it raises an exception (including the one it's
+ passed) that exception will NOT be caught and will therefore cause
+ future retries to be aborted.
+
+ The callback is called for EVERY failure, including the last one.
+ On the last try, the callback can raise an alternate exception,
+ but it cannot (without severe trickiness) prevent the exception
+ from being raised.
+
+ interrupt_callback = None
+
+ This callback is called if KeyboardInterrupt is received at any
+ point in the transfer. Basically, this callback can have three
+ impacts on the fetch process based on the way it exits:
+
+ 1) raise no exception: the current fetch will be aborted, but
+ any further retries will still take place
+
+ 2) raise a URLGrabError: if you're using a MirrorGroup, then
+ this will prompt a failover to the next mirror according to
+ the behavior of the MirrorGroup subclass. It is recommended
+ that you raise URLGrabError with code 15, 'user abort'. If
+ you are NOT using a MirrorGroup subclass, then this is the
+ same as (3).
+
+ 3) raise some other exception (such as KeyboardInterrupt), which
+ will not be caught at either the grabber or mirror levels.
+ That is, it will be raised up all the way to the caller.
+
+ This callback is very similar to failure_callback. They are
+ passed the same arguments, so you could use the same function for
+ both.
+
+ urlparser = URLParser()
+
+ The URLParser class handles pre-processing of URLs, including
+ auth-handling for user/pass encoded in http urls, file handing
+ (that is, filenames not sent as a URL), and URL quoting. If you
+ want to override any of this behavior, you can pass in a
+ replacement instance. See also the 'quote' option.
+
+ quote = None
+
+ Whether or not to quote the path portion of a url.
+ quote = 1 -> quote the URLs (they're not quoted yet)
+ quote = 0 -> do not quote them (they're already quoted)
+ quote = None -> guess what to do
+
+ This option only affects proper urls like 'file:///etc/passwd'; it
+ does not affect 'raw' filenames like '/etc/passwd'. The latter
+ will always be quoted as they are converted to URLs. Also, only
+ the path part of a url is quoted. If you need more fine-grained
+ control, you should probably subclass URLParser and pass it in via
+ the 'urlparser' option.
+
+BANDWIDTH THROTTLING
+
+ urlgrabber supports throttling via two values: throttle and
+ bandwidth Between the two, you can either specify and absolute
+ throttle threshold or specify a theshold as a fraction of maximum
+ available bandwidth.
+
+ throttle is a number - if it's an int, it's the bytes/second
+ throttle limit. If it's a float, it is first multiplied by
+ bandwidth. If throttle == 0, throttling is disabled. If None, the
+ module-level default (which can be set with set_throttle) is used.
+
+ bandwidth is the nominal max bandwidth in bytes/second. If throttle
+ is a float and bandwidth == 0, throttling is disabled. If None, the
+ module-level default (which can be set with set_bandwidth) is used.
+
+ THROTTLING EXAMPLES:
+
+ Lets say you have a 100 Mbps connection. This is (about) 10^8 bits
+ per second, or 12,500,000 Bytes per second. You have a number of
+ throttling options:
+
+ *) set_bandwidth(12500000); set_throttle(0.5) # throttle is a float
+
+ This will limit urlgrab to use half of your available bandwidth.
+
+ *) set_throttle(6250000) # throttle is an int
+
+ This will also limit urlgrab to use half of your available
+ bandwidth, regardless of what bandwidth is set to.
+
+ *) set_throttle(6250000); set_throttle(1.0) # float
+
+ Use half your bandwidth
+
+ *) set_throttle(6250000); set_throttle(2.0) # float
+
+ Use up to 12,500,000 Bytes per second (your nominal max bandwidth)
+
+ *) set_throttle(6250000); set_throttle(0) # throttle = 0
+
+ Disable throttling - this is more efficient than a very large
+ throttle setting.
+
+ *) set_throttle(0); set_throttle(1.0) # throttle is float, bandwidth = 0
+
+ Disable throttling - this is the default when the module is loaded.
+
+ SUGGESTED AUTHOR IMPLEMENTATION (THROTTLING)
+
+ While this is flexible, it's not extremely obvious to the user. I
+ suggest you implement a float throttle as a percent to make the
+ distinction between absolute and relative throttling very explicit.
+
+ Also, you may want to convert the units to something more convenient
+ than bytes/second, such as kbps or kB/s, etc.
+
+"""
+
+# $Id: grabber.py,v 1.48 2006/09/22 00:58:05 mstenner Exp $
+
+import os
+import os.path
+import sys
+import urlparse
+import rfc822
+import time
+import string
+import urllib
+import urllib2
+from stat import * # S_* and ST_*
+
+########################################################################
+# MODULE INITIALIZATION
+########################################################################
+try:
+ exec('from ' + (__name__.split('.'))[0] + ' import __version__')
+except:
+ __version__ = '???'
+
+import sslfactory
+
+auth_handler = urllib2.HTTPBasicAuthHandler( \
+ urllib2.HTTPPasswordMgrWithDefaultRealm())
+
+try:
+ from i18n import _
+except ImportError, msg:
+ def _(st): return st
+
+try:
+ from httplib import HTTPException
+except ImportError, msg:
+ HTTPException = None
+
+try:
+ # This is a convenient way to make keepalive optional.
+ # Just rename the module so it can't be imported.
+ import keepalive
+ from keepalive import HTTPHandler, HTTPSHandler
+ have_keepalive = True
+except ImportError, msg:
+ have_keepalive = False
+
+try:
+ # add in range support conditionally too
+ import byterange
+ from byterange import HTTPRangeHandler, HTTPSRangeHandler, \
+ FileRangeHandler, FTPRangeHandler, range_tuple_normalize, \
+ range_tuple_to_header, RangeError
+except ImportError, msg:
+ range_handlers = ()
+ RangeError = None
+ have_range = 0
+else:
+ range_handlers = (HTTPRangeHandler(), HTTPSRangeHandler(),
+ FileRangeHandler(), FTPRangeHandler())
+ have_range = 1
+
+
+# check whether socket timeout support is available (Python >= 2.3)
+import socket
+try:
+ TimeoutError = socket.timeout
+ have_socket_timeout = True
+except AttributeError:
+ TimeoutError = None
+ have_socket_timeout = False
+
+########################################################################
+# functions for debugging output. These functions are here because they
+# are also part of the module initialization.
+DEBUG = None
+def set_logger(DBOBJ):
+ """Set the DEBUG object. This is called by _init_default_logger when
+ the environment variable URLGRABBER_DEBUG is set, but can also be
+ called by a calling program. Basically, if the calling program uses
+ the logging module and would like to incorporate urlgrabber logging,
+ then it can do so this way. It's probably not necessary as most
+ internal logging is only for debugging purposes.
+
+ The passed-in object should be a logging.Logger instance. It will
+ be pushed into the keepalive and byterange modules if they're
+ being used. The mirror module pulls this object in on import, so
+ you will need to manually push into it. In fact, you may find it
+ tidier to simply push your logging object (or objects) into each
+ of these modules independently.
+ """
+
+ global DEBUG
+ DEBUG = DBOBJ
+ if have_keepalive and keepalive.DEBUG is None:
+ keepalive.DEBUG = DBOBJ
+ if have_range and byterange.DEBUG is None:
+ byterange.DEBUG = DBOBJ
+ if sslfactory.DEBUG is None:
+ sslfactory.DEBUG = DBOBJ
+
+def _init_default_logger():
+ '''Examines the environment variable URLGRABBER_DEBUG and creates
+ a logging object (logging.logger) based on the contents. It takes
+ the form
+
+ URLGRABBER_DEBUG=level,filename
+
+ where "level" can be either an integer or a log level from the
+ logging module (DEBUG, INFO, etc). If the integer is zero or
+ less, logging will be disabled. Filename is the filename where
+ logs will be sent. If it is "-", then stdout will be used. If
+ the filename is empty or missing, stderr will be used. If the
+ variable cannot be processed or the logging module cannot be
+ imported (python < 2.3) then logging will be disabled. Here are
+ some examples:
+
+ URLGRABBER_DEBUG=1,debug.txt # log everything to debug.txt
+ URLGRABBER_DEBUG=WARNING,- # log warning and higher to stdout
+ URLGRABBER_DEBUG=INFO # log info and higher to stderr
+
+ This funtion is called during module initialization. It is not
+ intended to be called from outside. The only reason it is a
+ function at all is to keep the module-level namespace tidy and to
+ collect the code into a nice block.'''
+
+ try:
+ dbinfo = os.environ['URLGRABBER_DEBUG'].split(',')
+ import logging
+ level = logging._levelNames.get(dbinfo[0], int(dbinfo[0]))
+ if level < 1: raise ValueError()
+
+ formatter = logging.Formatter('%(asctime)s %(message)s')
+ if len(dbinfo) > 1: filename = dbinfo[1]
+ else: filename = ''
+ if filename == '': handler = logging.StreamHandler(sys.stderr)
+ elif filename == '-': handler = logging.StreamHandler(sys.stdout)
+ else: handler = logging.FileHandler(filename)
+ handler.setFormatter(formatter)
+ DBOBJ = logging.getLogger('urlgrabber')
+ DBOBJ.addHandler(handler)
+ DBOBJ.setLevel(level)
+ except (KeyError, ImportError, ValueError):
+ DBOBJ = None
+ set_logger(DBOBJ)
+
+_init_default_logger()
+########################################################################
+# END MODULE INITIALIZATION
+########################################################################
+
+
+
+class URLGrabError(IOError):
+ """
+ URLGrabError error codes:
+
+ URLGrabber error codes (0 -- 255)
+ 0 - everything looks good (you should never see this)
+ 1 - malformed url
+ 2 - local file doesn't exist
+ 3 - request for non-file local file (dir, etc)
+ 4 - IOError on fetch
+ 5 - OSError on fetch
+ 6 - no content length header when we expected one
+ 7 - HTTPException
+ 8 - Exceeded read limit (for urlread)
+ 9 - Requested byte range not satisfiable.
+ 10 - Byte range requested, but range support unavailable
+ 11 - Illegal reget mode
+ 12 - Socket timeout
+ 13 - malformed proxy url
+ 14 - HTTPError (includes .code and .exception attributes)
+ 15 - user abort
+
+ MirrorGroup error codes (256 -- 511)
+ 256 - No more mirrors left to try
+
+ Custom (non-builtin) classes derived from MirrorGroup (512 -- 767)
+ [ this range reserved for application-specific error codes ]
+
+ Retry codes (< 0)
+ -1 - retry the download, unknown reason
+
+ Note: to test which group a code is in, you can simply do integer
+ division by 256: e.errno / 256
+
+ Negative codes are reserved for use by functions passed in to
+ retrygrab with checkfunc. The value -1 is built in as a generic
+ retry code and is already included in the retrycodes list.
+ Therefore, you can create a custom check function that simply
+ returns -1 and the fetch will be re-tried. For more customized
+ retries, you can use other negative number and include them in
+ retry-codes. This is nice for outputting useful messages about
+ what failed.
+
+ You can use these error codes like so:
+ try: urlgrab(url)
+ except URLGrabError, e:
+ if e.errno == 3: ...
+ # or
+ print e.strerror
+ # or simply
+ print e #### print '[Errno %i] %s' % (e.errno, e.strerror)
+ """
+ pass
+
+class CallbackObject:
+ """Container for returned callback data.
+
+ This is currently a dummy class into which urlgrabber can stuff
+ information for passing to callbacks. This way, the prototype for
+ all callbacks is the same, regardless of the data that will be
+ passed back. Any function that accepts a callback function as an
+ argument SHOULD document what it will define in this object.
+
+ It is possible that this class will have some greater
+ functionality in the future.
+ """
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+def urlgrab(url, filename=None, **kwargs):
+ """grab the file at <url> and make a local copy at <filename>
+ If filename is none, the basename of the url is used.
+ urlgrab returns the filename of the local file, which may be different
+ from the passed-in filename if the copy_local kwarg == 0.
+
+ See module documentation for a description of possible kwargs.
+ """
+ return default_grabber.urlgrab(url, filename, **kwargs)
+
+def urlopen(url, **kwargs):
+ """open the url and return a file object
+ If a progress object or throttle specifications exist, then
+ a special file object will be returned that supports them.
+ The file object can be treated like any other file object.
+
+ See module documentation for a description of possible kwargs.
+ """
+ return default_grabber.urlopen(url, **kwargs)
+
+def urlread(url, limit=None, **kwargs):
+ """read the url into a string, up to 'limit' bytes
+ If the limit is exceeded, an exception will be thrown. Note that urlread
+ is NOT intended to be used as a way of saying "I want the first N bytes"
+ but rather 'read the whole file into memory, but don't use too much'
+
+ See module documentation for a description of possible kwargs.
+ """
+ return default_grabber.urlread(url, limit, **kwargs)
+
+
+class URLParser:
+ """Process the URLs before passing them to urllib2.
+
+ This class does several things:
+
+ * add any prefix
+ * translate a "raw" file to a proper file: url
+ * handle any http or https auth that's encoded within the url
+ * quote the url
+
+ Only the "parse" method is called directly, and it calls sub-methods.
+
+ An instance of this class is held in the options object, which
+ means that it's easy to change the behavior by sub-classing and
+ passing the replacement in. It need only have a method like:
+
+ url, parts = urlparser.parse(url, opts)
+ """
+
+ def parse(self, url, opts):
+ """parse the url and return the (modified) url and its parts
+
+ Note: a raw file WILL be quoted when it's converted to a URL.
+ However, other urls (ones which come with a proper scheme) may
+ or may not be quoted according to opts.quote
+
+ opts.quote = 1 --> quote it
+ opts.quote = 0 --> do not quote it
+ opts.quote = None --> guess
+ """
+ quote = opts.quote
+
+ if opts.prefix:
+ url = self.add_prefix(url, opts.prefix)
+
+ parts = urlparse.urlparse(url)
+ (scheme, host, path, parm, query, frag) = parts
+
+ if not scheme or (len(scheme) == 1 and scheme in string.letters):
+ # if a scheme isn't specified, we guess that it's "file:"
+ if url[0] not in '/\\': url = os.path.abspath(url)
+ url = 'file:' + urllib.pathname2url(url)
+ parts = urlparse.urlparse(url)
+ quote = 0 # pathname2url quotes, so we won't do it again
+
+ if scheme in ['http', 'https']:
+ parts = self.process_http(parts)
+
+ if quote is None:
+ quote = self.guess_should_quote(parts)
+ if quote:
+ parts = self.quote(parts)
+
+ url = urlparse.urlunparse(parts)
+ return url, parts
+
+ def add_prefix(self, url, prefix):
+ if prefix[-1] == '/' or url[0] == '/':
+ url = prefix + url
+ else:
+ url = prefix + '/' + url
+ return url
+
+ def process_http(self, parts):
+ (scheme, host, path, parm, query, frag) = parts
+
+ if '@' in host and auth_handler:
+ try:
+ user_pass, host = host.split('@', 1)
+ if ':' in user_pass:
+ user, password = user_pass.split(':', 1)
+ except ValueError, e:
+ raise URLGrabError(1, _('Bad URL: %s') % url)
+ if DEBUG: DEBUG.info('adding HTTP auth: %s, %s', user, password)
+ auth_handler.add_password(None, host, user, password)
+
+ return (scheme, host, path, parm, query, frag)
+
+ def quote(self, parts):
+ """quote the URL
+
+ This method quotes ONLY the path part. If you need to quote
+ other parts, you should override this and pass in your derived
+ class. The other alternative is to quote other parts before
+ passing into urlgrabber.
+ """
+ (scheme, host, path, parm, query, frag) = parts
+ path = urllib.quote(path)
+ return (scheme, host, path, parm, query, frag)
+
+ hexvals = '0123456789ABCDEF'
+ def guess_should_quote(self, parts):
+ """
+ Guess whether we should quote a path. This amounts to
+ guessing whether it's already quoted.
+
+ find ' ' -> 1
+ find '%' -> 1
+ find '%XX' -> 0
+ else -> 1
+ """
+ (scheme, host, path, parm, query, frag) = parts
+ if ' ' in path:
+ return 1
+ ind = string.find(path, '%')
+ if ind > -1:
+ while ind > -1:
+ if len(path) < ind+3:
+ return 1
+ code = path[ind+1:ind+3].upper()
+ if code[0] not in self.hexvals or \
+ code[1] not in self.hexvals:
+ return 1
+ ind = string.find(path, '%', ind+1)
+ return 0
+ return 1
+
+class URLGrabberOptions:
+ """Class to ease kwargs handling."""
+
+ def __init__(self, delegate=None, **kwargs):
+ """Initialize URLGrabberOptions object.
+ Set default values for all options and then update options specified
+ in kwargs.
+ """
+ self.delegate = delegate
+ if delegate is None:
+ self._set_defaults()
+ self._set_attributes(**kwargs)
+
+ def __getattr__(self, name):
+ if self.delegate and hasattr(self.delegate, name):
+ return getattr(self.delegate, name)
+ raise AttributeError, name
+
+ def raw_throttle(self):
+ """Calculate raw throttle value from throttle and bandwidth
+ values.
+ """
+ if self.throttle <= 0:
+ return 0
+ elif type(self.throttle) == type(0):
+ return float(self.throttle)
+ else: # throttle is a float
+ return self.bandwidth * self.throttle
+
+ def derive(self, **kwargs):
+ """Create a derived URLGrabberOptions instance.
+ This method creates a new instance and overrides the
+ options specified in kwargs.
+ """
+ return URLGrabberOptions(delegate=self, **kwargs)
+
+ def _set_attributes(self, **kwargs):
+ """Update object attributes with those provided in kwargs."""
+ self.__dict__.update(kwargs)
+ if have_range and kwargs.has_key('range'):
+ # normalize the supplied range value
+ self.range = range_tuple_normalize(self.range)
+ if not self.reget in [None, 'simple', 'check_timestamp']:
+ raise URLGrabError(11, _('Illegal reget mode: %s') \
+ % (self.reget, ))
+
+ def _set_defaults(self):
+ """Set all options to their default values.
+ When adding new options, make sure a default is
+ provided here.
+ """
+ self.progress_obj = None
+ self.throttle = 1.0
+ self.bandwidth = 0
+ self.retry = None
+ self.retrycodes = [-1,2,4,5,6,7]
+ self.checkfunc = None
+ self.copy_local = 0
+ self.close_connection = 0
+ self.range = None
+ self.user_agent = 'urlgrabber/%s' % __version__
+ self.keepalive = 1
+ self.proxies = None
+ self.reget = None
+ self.failure_callback = None
+ self.interrupt_callback = None
+ self.prefix = None
+ self.opener = None
+ self.cache_openers = True
+ self.timeout = None
+ self.text = None
+ self.http_headers = None
+ self.ftp_headers = None
+ self.data = None
+ self.urlparser = URLParser()
+ self.quote = None
+ self.ssl_ca_cert = None
+ self.ssl_context = None
+
+class URLGrabber:
+ """Provides easy opening of URLs with a variety of options.
+
+ All options are specified as kwargs. Options may be specified when
+ the class is created and may be overridden on a per request basis.
+
+ New objects inherit default values from default_grabber.
+ """
+
+ def __init__(self, **kwargs):
+ self.opts = URLGrabberOptions(**kwargs)
+
+ def _retry(self, opts, func, *args):
+ tries = 0
+ while 1:
+ # there are only two ways out of this loop. The second has
+ # several "sub-ways"
+ # 1) via the return in the "try" block
+ # 2) by some exception being raised
+ # a) an excepton is raised that we don't "except"
+ # b) a callback raises ANY exception
+ # c) we're not retry-ing or have run out of retries
+ # d) the URLGrabError code is not in retrycodes
+ # beware of infinite loops :)
+ tries = tries + 1
+ exception = None
+ retrycode = None
+ callback = None
+ if DEBUG: DEBUG.info('attempt %i/%s: %s',
+ tries, opts.retry, args[0])
+ try:
+ r = apply(func, (opts,) + args, {})
+ if DEBUG: DEBUG.info('success')
+ return r
+ except URLGrabError, e:
+ exception = e
+ callback = opts.failure_callback
+ retrycode = e.errno
+ except KeyboardInterrupt, e:
+ exception = e
+ callback = opts.interrupt_callback
+
+ if DEBUG: DEBUG.info('exception: %s', exception)
+ if callback:
+ if DEBUG: DEBUG.info('calling callback: %s', callback)
+ cb_func, cb_args, cb_kwargs = self._make_callback(callback)
+ obj = CallbackObject(exception=exception, url=args[0],
+ tries=tries, retry=opts.retry)
+ cb_func(obj, *cb_args, **cb_kwargs)
+
+ if (opts.retry is None) or (tries == opts.retry):
+ if DEBUG: DEBUG.info('retries exceeded, re-raising')
+ raise
+
+ if (retrycode is not None) and (retrycode not in opts.retrycodes):
+ if DEBUG: DEBUG.info('retrycode (%i) not in list %s, re-raising',
+ retrycode, opts.retrycodes)
+ raise
+
+ def urlopen(self, url, **kwargs):
+ """open the url and return a file object
+ If a progress object or throttle value specified when this
+ object was created, then a special file object will be
+ returned that supports them. The file object can be treated
+ like any other file object.
+ """
+ opts = self.opts.derive(**kwargs)
+ (url,parts) = opts.urlparser.parse(url, opts)
+ def retryfunc(opts, url):
+ return URLGrabberFileObject(url, filename=None, opts=opts)
+ return self._retry(opts, retryfunc, url)
+
+ def urlgrab(self, url, filename=None, **kwargs):
+ """grab the file at <url> and make a local copy at <filename>
+ If filename is none, the basename of the url is used.
+ urlgrab returns the filename of the local file, which may be
+ different from the passed-in filename if copy_local == 0.
+ """
+ opts = self.opts.derive(**kwargs)
+ (url,parts) = opts.urlparser.parse(url, opts)
+ (scheme, host, path, parm, query, frag) = parts
+ if filename is None:
+ filename = os.path.basename( urllib.unquote(path) )
+ if scheme == 'file' and not opts.copy_local:
+ # just return the name of the local file - don't make a
+ # copy currently
+ path = urllib.url2pathname(path)
+ if host:
+ path = os.path.normpath('//' + host + path)
+ if not os.path.exists(path):
+ raise URLGrabError(2,
+ _('Local file does not exist: %s') % (path, ))
+ elif not os.path.isfile(path):
+ raise URLGrabError(3,
+ _('Not a normal file: %s') % (path, ))
+ elif not opts.range:
+ return path
+
+ def retryfunc(opts, url, filename):
+ fo = URLGrabberFileObject(url, filename, opts)
+ try:
+ fo._do_grab()
+ if not opts.checkfunc is None:
+ cb_func, cb_args, cb_kwargs = \
+ self._make_callback(opts.checkfunc)
+ obj = CallbackObject()
+ obj.filename = filename
+ obj.url = url
+ apply(cb_func, (obj, )+cb_args, cb_kwargs)
+ finally:
+ fo.close()
+ return filename
+
+ return self._retry(opts, retryfunc, url, filename)
+
+ def urlread(self, url, limit=None, **kwargs):
+ """read the url into a string, up to 'limit' bytes
+ If the limit is exceeded, an exception will be thrown. Note
+ that urlread is NOT intended to be used as a way of saying
+ "I want the first N bytes" but rather 'read the whole file
+ into memory, but don't use too much'
+ """
+ opts = self.opts.derive(**kwargs)
+ (url,parts) = opts.urlparser.parse(url, opts)
+ if limit is not None:
+ limit = limit + 1
+
+ def retryfunc(opts, url, limit):
+ fo = URLGrabberFileObject(url, filename=None, opts=opts)
+ s = ''
+ try:
+ # this is an unfortunate thing. Some file-like objects
+ # have a default "limit" of None, while the built-in (real)
+ # file objects have -1. They each break the other, so for
+ # now, we just force the default if necessary.
+ if limit is None: s = fo.read()
+ else: s = fo.read(limit)
+
+ if not opts.checkfunc is None:
+ cb_func, cb_args, cb_kwargs = \
+ self._make_callback(opts.checkfunc)
+ obj = CallbackObject()
+ obj.data = s
+ obj.url = url
+ apply(cb_func, (obj, )+cb_args, cb_kwargs)
+ finally:
+ fo.close()
+ return s
+
+ s = self._retry(opts, retryfunc, url, limit)
+ if limit and len(s) > limit:
+ raise URLGrabError(8,
+ _('Exceeded limit (%i): %s') % (limit, url))
+ return s
+
+ def _make_callback(self, callback_obj):
+ if callable(callback_obj):
+ return callback_obj, (), {}
+ else:
+ return callback_obj
+
+# create the default URLGrabber used by urlXXX functions.
+# NOTE: actual defaults are set in URLGrabberOptions
+default_grabber = URLGrabber()
+
+class URLGrabberFileObject:
+ """This is a file-object wrapper that supports progress objects
+ and throttling.
+
+ This exists to solve the following problem: lets say you want to
+ drop-in replace a normal open with urlopen. You want to use a
+ progress meter and/or throttling, but how do you do that without
+ rewriting your code? Answer: urlopen will return a wrapped file
+ object that does the progress meter and-or throttling internally.
+ """
+
+ def __init__(self, url, filename, opts):
+ self.url = url
+ self.filename = filename
+ self.opts = opts
+ self.fo = None
+ self._rbuf = ''
+ self._rbufsize = 1024*8
+ self._ttime = time.time()
+ self._tsize = 0
+ self._amount_read = 0
+ self._opener = None
+ self._do_open()
+
+ def __getattr__(self, name):
+ """This effectively allows us to wrap at the instance level.
+ Any attribute not found in _this_ object will be searched for
+ in self.fo. This includes methods."""
+ if hasattr(self.fo, name):
+ return getattr(self.fo, name)
+ raise AttributeError, name
+
+ def _get_opener(self):
+ """Build a urllib2 OpenerDirector based on request options."""
+ if self.opts.opener:
+ return self.opts.opener
+ elif self._opener is None:
+ handlers = []
+ need_keepalive_handler = (have_keepalive and self.opts.keepalive)
+ need_range_handler = (range_handlers and \
+ (self.opts.range or self.opts.reget))
+ # if you specify a ProxyHandler when creating the opener
+ # it _must_ come before all other handlers in the list or urllib2
+ # chokes.
+ if self.opts.proxies:
+ handlers.append( CachedProxyHandler(self.opts.proxies) )
+
+ # -------------------------------------------------------
+ # OK, these next few lines are a serious kludge to get
+ # around what I think is a bug in python 2.2's
+ # urllib2. The basic idea is that default handlers
+ # get applied first. If you override one (like a
+ # proxy handler), then the default gets pulled, but
+ # the replacement goes on the end. In the case of
+ # proxies, this means the normal handler picks it up
+ # first and the proxy isn't used. Now, this probably
+ # only happened with ftp or non-keepalive http, so not
+ # many folks saw it. The simple approach to fixing it
+ # is just to make sure you override the other
+ # conflicting defaults as well. I would LOVE to see
+ # these go way or be dealt with more elegantly. The
+ # problem isn't there after 2.2. -MDS 2005/02/24
+ if not need_keepalive_handler:
+ handlers.append( urllib2.HTTPHandler() )
+ if not need_range_handler:
+ handlers.append( urllib2.FTPHandler() )
+ # -------------------------------------------------------
+
+ ssl_factory = sslfactory.get_factory(self.opts.ssl_ca_cert,
+ self.opts.ssl_context)
+
+ if need_keepalive_handler:
+ handlers.append(HTTPHandler())
+ handlers.append(HTTPSHandler(ssl_factory))
+ if need_range_handler:
+ handlers.extend( range_handlers )
+ handlers.append( auth_handler )
+ if self.opts.cache_openers:
+ self._opener = CachedOpenerDirector(ssl_factory, *handlers)
+ else:
+ self._opener = ssl_factory.create_opener(*handlers)
+ # OK, I don't like to do this, but otherwise, we end up with
+ # TWO user-agent headers.
+ self._opener.addheaders = []
+ return self._opener
+
+ def _do_open(self):
+ opener = self._get_opener()
+
+ req = urllib2.Request(self.url, self.opts.data) # build request object
+ self._add_headers(req) # add misc headers that we need
+ self._build_range(req) # take care of reget and byterange stuff
+
+ fo, hdr = self._make_request(req, opener)
+ if self.reget_time and self.opts.reget == 'check_timestamp':
+ # do this if we have a local file with known timestamp AND
+ # we're in check_timestamp reget mode.
+ fetch_again = 0
+ try:
+ modified_tuple = hdr.getdate_tz('last-modified')
+ modified_stamp = rfc822.mktime_tz(modified_tuple)
+ if modified_stamp > self.reget_time: fetch_again = 1
+ except (TypeError,):
+ fetch_again = 1
+
+ if fetch_again:
+ # the server version is newer than the (incomplete) local
+ # version, so we should abandon the version we're getting
+ # and fetch the whole thing again.
+ fo.close()
+ self.opts.reget = None
+ del req.headers['Range']
+ self._build_range(req)
+ fo, hdr = self._make_request(req, opener)
+
+ (scheme, host, path, parm, query, frag) = urlparse.urlparse(self.url)
+ path = urllib.unquote(path)
+ if not (self.opts.progress_obj or self.opts.raw_throttle() \
+ or self.opts.timeout):
+ # if we're not using the progress_obj, throttling, or timeout
+ # we can get a performance boost by going directly to
+ # the underlying fileobject for reads.
+ self.read = fo.read
+ if hasattr(fo, 'readline'):
+ self.readline = fo.readline
+ elif self.opts.progress_obj:
+ try:
+ length = int(hdr['Content-Length'])
+ length = length + self._amount_read # Account for regets
+ except (KeyError, ValueError, TypeError):
+ length = None
+
+ self.opts.progress_obj.start(str(self.filename),
+ urllib.unquote(self.url),
+ os.path.basename(path),
+ length, text=self.opts.text)
+ self.opts.progress_obj.update(0)
+ (self.fo, self.hdr) = (fo, hdr)
+
+ def _add_headers(self, req):
+ if self.opts.user_agent:
+ req.add_header('User-agent', self.opts.user_agent)
+ try: req_type = req.get_type()
+ except ValueError: req_type = None
+ if self.opts.http_headers and req_type in ('http', 'https'):
+ for h, v in self.opts.http_headers:
+ req.add_header(h, v)
+ if self.opts.ftp_headers and req_type == 'ftp':
+ for h, v in self.opts.ftp_headers:
+ req.add_header(h, v)
+
+ def _build_range(self, req):
+ self.reget_time = None
+ self.append = 0
+ reget_length = 0
+ rt = None
+ if have_range and self.opts.reget and type(self.filename) == type(''):
+ # we have reget turned on and we're dumping to a file
+ try:
+ s = os.stat(self.filename)
+ except OSError:
+ pass
+ else:
+ self.reget_time = s[ST_MTIME]
+ reget_length = s[ST_SIZE]
+
+ # Set initial length when regetting
+ self._amount_read = reget_length
+
+ rt = reget_length, ''
+ self.append = 1
+
+ if self.opts.range:
+ if not have_range:
+ raise URLGrabError(10, _('Byte range requested but range '\
+ 'support unavailable'))
+ rt = self.opts.range
+ if rt[0]: rt = (rt[0] + reget_length, rt[1])
+
+ if rt:
+ header = range_tuple_to_header(rt)
+ if header: req.add_header('Range', header)
+
+ def _make_request(self, req, opener):
+ try:
+ if have_socket_timeout and self.opts.timeout:
+ old_to = socket.getdefaulttimeout()
+ socket.setdefaulttimeout(self.opts.timeout)
+ try:
+ fo = opener.open(req)
+ finally:
+ socket.setdefaulttimeout(old_to)
+ else:
+ fo = opener.open(req)
+ hdr = fo.info()
+ except ValueError, e:
+ raise URLGrabError(1, _('Bad URL: %s') % (e, ))
+ except RangeError, e:
+ raise URLGrabError(9, str(e))
+ except urllib2.HTTPError, e:
+ new_e = URLGrabError(14, str(e))
+ new_e.code = e.code
+ new_e.exception = e
+ raise new_e
+ except IOError, e:
+ if hasattr(e, 'reason') and have_socket_timeout and \
+ isinstance(e.reason, TimeoutError):
+ raise URLGrabError(12, _('Timeout: %s') % (e, ))
+ else:
+ raise URLGrabError(4, _('IOError: %s') % (e, ))
+ except OSError, e:
+ raise URLGrabError(5, _('OSError: %s') % (e, ))
+ except HTTPException, e:
+ raise URLGrabError(7, _('HTTP Exception (%s): %s') % \
+ (e.__class__.__name__, e))
+ else:
+ return (fo, hdr)
+
+ def _do_grab(self):
+ """dump the file to self.filename."""
+ if self.append: new_fo = open(self.filename, 'ab')
+ else: new_fo = open(self.filename, 'wb')
+ bs = 1024*8
+ size = 0
+
+ block = self.read(bs)
+ size = size + len(block)
+ while block:
+ new_fo.write(block)
+ block = self.read(bs)
+ size = size + len(block)
+
+ new_fo.close()
+ try:
+ modified_tuple = self.hdr.getdate_tz('last-modified')
+ modified_stamp = rfc822.mktime_tz(modified_tuple)
+ os.utime(self.filename, (modified_stamp, modified_stamp))
+ except (TypeError,), e: pass
+
+ return size
+
+ def _fill_buffer(self, amt=None):
+ """fill the buffer to contain at least 'amt' bytes by reading
+ from the underlying file object. If amt is None, then it will
+ read until it gets nothing more. It updates the progress meter
+ and throttles after every self._rbufsize bytes."""
+ # the _rbuf test is only in this first 'if' for speed. It's not
+ # logically necessary
+ if self._rbuf and not amt is None:
+ L = len(self._rbuf)
+ if amt > L:
+ amt = amt - L
+ else:
+ return
+
+ # if we've made it here, then we don't have enough in the buffer
+ # and we need to read more.
+
+ buf = [self._rbuf]
+ bufsize = len(self._rbuf)
+ while amt is None or amt:
+ # first, delay if necessary for throttling reasons
+ if self.opts.raw_throttle():
+ diff = self._tsize/self.opts.raw_throttle() - \
+ (time.time() - self._ttime)
+ if diff > 0: time.sleep(diff)
+ self._ttime = time.time()
+
+ # now read some data, up to self._rbufsize
+ if amt is None: readamount = self._rbufsize
+ else: readamount = min(amt, self._rbufsize)
+ try:
+ new = self.fo.read(readamount)
+ except socket.error, e:
+ raise URLGrabError(4, _('Socket Error: %s') % (e, ))
+ except TimeoutError, e:
+ raise URLGrabError(12, _('Timeout: %s') % (e, ))
+ except IOError, e:
+ raise URLGrabError(4, _('IOError: %s') %(e,))
+ newsize = len(new)
+ if not newsize: break # no more to read
+
+ if amt: amt = amt - newsize
+ buf.append(new)
+ bufsize = bufsize + newsize
+ self._tsize = newsize
+ self._amount_read = self._amount_read + newsize
+ if self.opts.progress_obj:
+ self.opts.progress_obj.update(self._amount_read)
+
+ self._rbuf = string.join(buf, '')
+ return
+
+ def read(self, amt=None):
+ self._fill_buffer(amt)
+ if amt is None:
+ s, self._rbuf = self._rbuf, ''
+ else:
+ s, self._rbuf = self._rbuf[:amt], self._rbuf[amt:]
+ return s
+
+ def readline(self, limit=-1):
+ i = string.find(self._rbuf, '\n')
+ while i < 0 and not (0 < limit <= len(self._rbuf)):
+ L = len(self._rbuf)
+ self._fill_buffer(L + self._rbufsize)
+ if not len(self._rbuf) > L: break
+ i = string.find(self._rbuf, '\n', L)
+
+ if i < 0: i = len(self._rbuf)
+ else: i = i+1
+ if 0 <= limit < len(self._rbuf): i = limit
+
+ s, self._rbuf = self._rbuf[:i], self._rbuf[i:]
+ return s
+
+ def close(self):
+ if self.opts.progress_obj:
+ self.opts.progress_obj.end(self._amount_read)
+ self.fo.close()
+ if self.opts.close_connection:
+ try: self.fo.close_connection()
+ except: pass
+
+_handler_cache = []
+def CachedOpenerDirector(ssl_factory = None, *handlers):
+ for (cached_handlers, opener) in _handler_cache:
+ if cached_handlers == handlers:
+ for handler in opener.handlers:
+ handler.add_parent(opener)
+ return opener
+ if not ssl_factory:
+ ssl_factory = sslfactory.get_factory()
+ opener = ssl_factory.create_opener(*handlers)
+ _handler_cache.append( (handlers, opener) )
+ return opener
+
+_proxy_cache = []
+def CachedProxyHandler(proxies):
+ for (pdict, handler) in _proxy_cache:
+ if pdict == proxies:
+ if DEBUG: DEBUG.debug('re-using proxy settings: %s', proxies)
+ break
+ else:
+ for k, v in proxies.items():
+ utype, url = urllib.splittype(v)
+ host, other = urllib.splithost(url)
+ if (utype is None) or (host is None):
+ raise URLGrabError(13, _('Bad proxy URL: %s') % v)
+
+ if DEBUG: DEBUG.info('creating new proxy handler: %s', proxies)
+ handler = urllib2.ProxyHandler(proxies)
+ _proxy_cache.append( (proxies, handler) )
+ return handler
+
+#####################################################################
+# DEPRECATED FUNCTIONS
+def set_throttle(new_throttle):
+ """Deprecated. Use: default_grabber.throttle = new_throttle"""
+ default_grabber.throttle = new_throttle
+
+def set_bandwidth(new_bandwidth):
+ """Deprecated. Use: default_grabber.bandwidth = new_bandwidth"""
+ default_grabber.bandwidth = new_bandwidth
+
+def set_progress_obj(new_progress_obj):
+ """Deprecated. Use: default_grabber.progress_obj = new_progress_obj"""
+ default_grabber.progress_obj = new_progress_obj
+
+def set_user_agent(new_user_agent):
+ """Deprecated. Use: default_grabber.user_agent = new_user_agent"""
+ default_grabber.user_agent = new_user_agent
+
+def retrygrab(url, filename=None, copy_local=0, close_connection=0,
+ progress_obj=None, throttle=None, bandwidth=None,
+ numtries=3, retrycodes=[-1,2,4,5,6,7], checkfunc=None):
+ """Deprecated. Use: urlgrab() with the retry arg instead"""
+ kwargs = {'copy_local' : copy_local,
+ 'close_connection' : close_connection,
+ 'progress_obj' : progress_obj,
+ 'throttle' : throttle,
+ 'bandwidth' : bandwidth,
+ 'retry' : numtries,
+ 'retrycodes' : retrycodes,
+ 'checkfunc' : checkfunc
+ }
+ return urlgrab(url, filename, **kwargs)
+
+
+#####################################################################
+# TESTING
+def _main_test():
+ import sys
+ try: url, filename = sys.argv[1:3]
+ except ValueError:
+ print 'usage:', sys.argv[0], \
+ '<url> <filename> [copy_local=0|1] [close_connection=0|1]'
+ sys.exit()
+
+ kwargs = {}
+ for a in sys.argv[3:]:
+ k, v = string.split(a, '=', 1)
+ kwargs[k] = int(v)
+
+ set_throttle(1.0)
+ set_bandwidth(32 * 1024)
+ print "throttle: %s, throttle bandwidth: %s B/s" % (default_grabber.throttle,
+ default_grabber.bandwidth)
+
+ try: from progress import text_progress_meter
+ except ImportError, e: pass
+ else: kwargs['progress_obj'] = text_progress_meter()
+
+ try: name = apply(urlgrab, (url, filename), kwargs)
+ except URLGrabError, e: print e
+ else: print 'LOCAL FILE:', name
+
+
+def _retry_test():
+ import sys
+ try: url, filename = sys.argv[1:3]
+ except ValueError:
+ print 'usage:', sys.argv[0], \
+ '<url> <filename> [copy_local=0|1] [close_connection=0|1]'
+ sys.exit()
+
+ kwargs = {}
+ for a in sys.argv[3:]:
+ k, v = string.split(a, '=', 1)
+ kwargs[k] = int(v)
+
+ try: from progress import text_progress_meter
+ except ImportError, e: pass
+ else: kwargs['progress_obj'] = text_progress_meter()
+
+ def cfunc(filename, hello, there='foo'):
+ print hello, there
+ import random
+ rnum = random.random()
+ if rnum < .5:
+ print 'forcing retry'
+ raise URLGrabError(-1, 'forcing retry')
+ if rnum < .75:
+ print 'forcing failure'
+ raise URLGrabError(-2, 'forcing immediate failure')
+ print 'success'
+ return
+
+ kwargs['checkfunc'] = (cfunc, ('hello',), {'there':'there'})
+ try: name = apply(retrygrab, (url, filename), kwargs)
+ except URLGrabError, e: print e
+ else: print 'LOCAL FILE:', name
+
+def _file_object_test(filename=None):
+ import random, cStringIO, sys
+ if filename is None:
+ filename = __file__
+ print 'using file "%s" for comparisons' % filename
+ fo = open(filename)
+ s_input = fo.read()
+ fo.close()
+
+ for testfunc in [_test_file_object_smallread,
+ _test_file_object_readall,
+ _test_file_object_readline,
+ _test_file_object_readlines]:
+ fo_input = cStringIO.StringIO(s_input)
+ fo_output = cStringIO.StringIO()
+ wrapper = URLGrabberFileObject(fo_input, None, 0)
+ print 'testing %-30s ' % testfunc.__name__,
+ testfunc(wrapper, fo_output)
+ s_output = fo_output.getvalue()
+ if s_output == s_input: print 'passed'
+ else: print 'FAILED'
+
+def _test_file_object_smallread(wrapper, fo_output):
+ while 1:
+ s = wrapper.read(23)
+ fo_output.write(s)
+ if not s: return
+
+def _test_file_object_readall(wrapper, fo_output):
+ s = wrapper.read()
+ fo_output.write(s)
+
+def _test_file_object_readline(wrapper, fo_output):
+ while 1:
+ s = wrapper.readline()
+ fo_output.write(s)
+ if not s: return
+
+def _test_file_object_readlines(wrapper, fo_output):
+ li = wrapper.readlines()
+ fo_output.write(string.join(li, ''))
+
+if __name__ == '__main__':
+ _main_test()
+ _retry_test()
+ _file_object_test('test')
diff --git a/mic/3rdparty/pykickstart/urlgrabber/keepalive.py b/mic/3rdparty/pykickstart/urlgrabber/keepalive.py
new file mode 100644
index 0000000..71393e2
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/keepalive.py
@@ -0,0 +1,617 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
+
+"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
+
+>>> import urllib2
+>>> from keepalive import HTTPHandler
+>>> keepalive_handler = HTTPHandler()
+>>> opener = urllib2.build_opener(keepalive_handler)
+>>> urllib2.install_opener(opener)
+>>>
+>>> fo = urllib2.urlopen('http://www.python.org')
+
+If a connection to a given host is requested, and all of the existing
+connections are still in use, another connection will be opened. If
+the handler tries to use an existing connection but it fails in some
+way, it will be closed and removed from the pool.
+
+To remove the handler, simply re-run build_opener with no arguments, and
+install that opener.
+
+You can explicitly close connections by using the close_connection()
+method of the returned file-like object (described below) or you can
+use the handler methods:
+
+ close_connection(host)
+ close_all()
+ open_connections()
+
+NOTE: using the close_connection and close_all methods of the handler
+should be done with care when using multiple threads.
+ * there is nothing that prevents another thread from creating new
+ connections immediately after connections are closed
+ * no checks are done to prevent in-use connections from being closed
+
+>>> keepalive_handler.close_all()
+
+EXTRA ATTRIBUTES AND METHODS
+
+ Upon a status of 200, the object returned has a few additional
+ attributes and methods, which should not be used if you want to
+ remain consistent with the normal urllib2-returned objects:
+
+ close_connection() - close the connection to the host
+ readlines() - you know, readlines()
+ status - the return status (ie 404)
+ reason - english translation of status (ie 'File not found')
+
+ If you want the best of both worlds, use this inside an
+ AttributeError-catching try:
+
+ >>> try: status = fo.status
+ >>> except AttributeError: status = None
+
+ Unfortunately, these are ONLY there if status == 200, so it's not
+ easy to distinguish between non-200 responses. The reason is that
+ urllib2 tries to do clever things with error codes 301, 302, 401,
+ and 407, and it wraps the object upon return.
+
+ For python versions earlier than 2.4, you can avoid this fancy error
+ handling by setting the module-level global HANDLE_ERRORS to zero.
+ You see, prior to 2.4, it's the HTTP Handler's job to determine what
+ to handle specially, and what to just pass up. HANDLE_ERRORS == 0
+ means "pass everything up". In python 2.4, however, this job no
+ longer belongs to the HTTP Handler and is now done by a NEW handler,
+ HTTPErrorProcessor. Here's the bottom line:
+
+ python version < 2.4
+ HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as
+ errors
+ HANDLE_ERRORS == 0 pass everything up, error processing is
+ left to the calling code
+ python version >= 2.4
+ HANDLE_ERRORS == 1 pass up 200, treat the rest as errors
+ HANDLE_ERRORS == 0 (default) pass everything up, let the
+ other handlers (specifically,
+ HTTPErrorProcessor) decide what to do
+
+ In practice, setting the variable either way makes little difference
+ in python 2.4, so for the most consistent behavior across versions,
+ you probably just want to use the defaults, which will give you
+ exceptions on errors.
+
+"""
+
+# $Id: keepalive.py,v 1.16 2006/09/22 00:58:05 mstenner Exp $
+
+import urllib2
+import httplib
+import socket
+import thread
+
+DEBUG = None
+
+import sslfactory
+
+import sys
+if sys.version_info < (2, 4): HANDLE_ERRORS = 1
+else: HANDLE_ERRORS = 0
+
+class ConnectionManager:
+ """
+ The connection manager must be able to:
+ * keep track of all existing
+ """
+ def __init__(self):
+ self._lock = thread.allocate_lock()
+ self._hostmap = {} # map hosts to a list of connections
+ self._connmap = {} # map connections to host
+ self._readymap = {} # map connection to ready state
+
+ def add(self, host, connection, ready):
+ self._lock.acquire()
+ try:
+ if not self._hostmap.has_key(host): self._hostmap[host] = []
+ self._hostmap[host].append(connection)
+ self._connmap[connection] = host
+ self._readymap[connection] = ready
+ finally:
+ self._lock.release()
+
+ def remove(self, connection):
+ self._lock.acquire()
+ try:
+ try:
+ host = self._connmap[connection]
+ except KeyError:
+ pass
+ else:
+ del self._connmap[connection]
+ del self._readymap[connection]
+ self._hostmap[host].remove(connection)
+ if not self._hostmap[host]: del self._hostmap[host]
+ finally:
+ self._lock.release()
+
+ def set_ready(self, connection, ready):
+ try: self._readymap[connection] = ready
+ except KeyError: pass
+
+ def get_ready_conn(self, host):
+ conn = None
+ self._lock.acquire()
+ try:
+ if self._hostmap.has_key(host):
+ for c in self._hostmap[host]:
+ if self._readymap[c]:
+ self._readymap[c] = 0
+ conn = c
+ break
+ finally:
+ self._lock.release()
+ return conn
+
+ def get_all(self, host=None):
+ if host:
+ return list(self._hostmap.get(host, []))
+ else:
+ return dict(self._hostmap)
+
+class KeepAliveHandler:
+ def __init__(self):
+ self._cm = ConnectionManager()
+
+ #### Connection Management
+ def open_connections(self):
+ """return a list of connected hosts and the number of connections
+ to each. [('foo.com:80', 2), ('bar.org', 1)]"""
+ return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
+
+ def close_connection(self, host):
+ """close connection(s) to <host>
+ host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
+ no error occurs if there is no connection to that host."""
+ for h in self._cm.get_all(host):
+ self._cm.remove(h)
+ h.close()
+
+ def close_all(self):
+ """close all open connections"""
+ for host, conns in self._cm.get_all().items():
+ for h in conns:
+ self._cm.remove(h)
+ h.close()
+
+ def _request_closed(self, request, host, connection):
+ """tells us that this request is now closed and the the
+ connection is ready for another request"""
+ self._cm.set_ready(connection, 1)
+
+ def _remove_connection(self, host, connection, close=0):
+ if close: connection.close()
+ self._cm.remove(connection)
+
+ #### Transaction Execution
+ def do_open(self, req):
+ host = req.get_host()
+ if not host:
+ raise urllib2.URLError('no host given')
+
+ try:
+ h = self._cm.get_ready_conn(host)
+ while h:
+ r = self._reuse_connection(h, req, host)
+
+ # if this response is non-None, then it worked and we're
+ # done. Break out, skipping the else block.
+ if r: break
+
+ # connection is bad - possibly closed by server
+ # discard it and ask for the next free connection
+ h.close()
+ self._cm.remove(h)
+ h = self._cm.get_ready_conn(host)
+ else:
+ # no (working) free connections were found. Create a new one.
+ h = self._get_connection(host)
+ if DEBUG: DEBUG.info("creating new connection to %s (%d)",
+ host, id(h))
+ self._cm.add(host, h, 0)
+ self._start_transaction(h, req)
+ r = h.getresponse()
+ except (socket.error, httplib.HTTPException), err:
+ raise urllib2.URLError(err)
+
+ # if not a persistent connection, don't try to reuse it
+ if r.will_close: self._cm.remove(h)
+
+ if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason)
+ r._handler = self
+ r._host = host
+ r._url = req.get_full_url()
+ r._connection = h
+ r.code = r.status
+ r.headers = r.msg
+ r.msg = r.reason
+
+ if r.status == 200 or not HANDLE_ERRORS:
+ return r
+ else:
+ return self.parent.error('http', req, r,
+ r.status, r.msg, r.headers)
+
+ def _reuse_connection(self, h, req, host):
+ """start the transaction with a re-used connection
+ return a response object (r) upon success or None on failure.
+ This DOES not close or remove bad connections in cases where
+ it returns. However, if an unexpected exception occurs, it
+ will close and remove the connection before re-raising.
+ """
+ try:
+ self._start_transaction(h, req)
+ r = h.getresponse()
+ # note: just because we got something back doesn't mean it
+ # worked. We'll check the version below, too.
+ except (socket.error, httplib.HTTPException):
+ r = None
+ except:
+ # adding this block just in case we've missed
+ # something we will still raise the exception, but
+ # lets try and close the connection and remove it
+ # first. We previously got into a nasty loop
+ # where an exception was uncaught, and so the
+ # connection stayed open. On the next try, the
+ # same exception was raised, etc. The tradeoff is
+ # that it's now possible this call will raise
+ # a DIFFERENT exception
+ if DEBUG: DEBUG.error("unexpected exception - closing " + \
+ "connection to %s (%d)", host, id(h))
+ self._cm.remove(h)
+ h.close()
+ raise
+
+ if r is None or r.version == 9:
+ # httplib falls back to assuming HTTP 0.9 if it gets a
+ # bad header back. This is most likely to happen if
+ # the socket has been closed by the server since we
+ # last used the connection.
+ if DEBUG: DEBUG.info("failed to re-use connection to %s (%d)",
+ host, id(h))
+ r = None
+ else:
+ if DEBUG: DEBUG.info("re-using connection to %s (%d)", host, id(h))
+
+ return r
+
+ def _start_transaction(self, h, req):
+ try:
+ if req.has_data():
+ data = req.get_data()
+ h.putrequest('POST', req.get_selector())
+ if not req.headers.has_key('Content-type'):
+ h.putheader('Content-type',
+ 'application/x-www-form-urlencoded')
+ if not req.headers.has_key('Content-length'):
+ h.putheader('Content-length', '%d' % len(data))
+ else:
+ h.putrequest('GET', req.get_selector())
+ except (socket.error, httplib.HTTPException), err:
+ raise urllib2.URLError(err)
+
+ for args in self.parent.addheaders:
+ h.putheader(*args)
+ for k, v in req.headers.items():
+ h.putheader(k, v)
+ h.endheaders()
+ if req.has_data():
+ h.send(data)
+
+ def _get_connection(self, host):
+ return NotImplementedError
+
+class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
+ def __init__(self):
+ KeepAliveHandler.__init__(self)
+
+ def http_open(self, req):
+ return self.do_open(req)
+
+ def _get_connection(self, host):
+ return HTTPConnection(host)
+
+class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler):
+ def __init__(self, ssl_factory=None):
+ KeepAliveHandler.__init__(self)
+ if not ssl_factory:
+ ssl_factory = sslfactory.get_factory()
+ self._ssl_factory = ssl_factory
+
+ def https_open(self, req):
+ return self.do_open(req)
+
+ def _get_connection(self, host):
+ return self._ssl_factory.get_https_connection(host)
+
+class HTTPResponse(httplib.HTTPResponse):
+ # we need to subclass HTTPResponse in order to
+ # 1) add readline() and readlines() methods
+ # 2) add close_connection() methods
+ # 3) add info() and geturl() methods
+
+ # in order to add readline(), read must be modified to deal with a
+ # buffer. example: readline must read a buffer and then spit back
+ # one line at a time. The only real alternative is to read one
+ # BYTE at a time (ick). Once something has been read, it can't be
+ # put back (ok, maybe it can, but that's even uglier than this),
+ # so if you THEN do a normal read, you must first take stuff from
+ # the buffer.
+
+ # the read method wraps the original to accomodate buffering,
+ # although read() never adds to the buffer.
+ # Both readline and readlines have been stolen with almost no
+ # modification from socket.py
+
+
+ def __init__(self, sock, debuglevel=0, strict=0, method=None):
+ if method: # the httplib in python 2.3 uses the method arg
+ httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
+ else: # 2.2 doesn't
+ httplib.HTTPResponse.__init__(self, sock, debuglevel)
+ self.fileno = sock.fileno
+ self.code = None
+ self._rbuf = ''
+ self._rbufsize = 8096
+ self._handler = None # inserted by the handler later
+ self._host = None # (same)
+ self._url = None # (same)
+ self._connection = None # (same)
+
+ _raw_read = httplib.HTTPResponse.read
+
+ def close(self):
+ if self.fp:
+ self.fp.close()
+ self.fp = None
+ if self._handler:
+ self._handler._request_closed(self, self._host,
+ self._connection)
+
+ def close_connection(self):
+ self._handler._remove_connection(self._host, self._connection, close=1)
+ self.close()
+
+ def info(self):
+ return self.headers
+
+ def geturl(self):
+ return self._url
+
+ def read(self, amt=None):
+ # the _rbuf test is only in this first if for speed. It's not
+ # logically necessary
+ if self._rbuf and not amt is None:
+ L = len(self._rbuf)
+ if amt > L:
+ amt -= L
+ else:
+ s = self._rbuf[:amt]
+ self._rbuf = self._rbuf[amt:]
+ return s
+
+ s = self._rbuf + self._raw_read(amt)
+ self._rbuf = ''
+ return s
+
+ def readline(self, limit=-1):
+ data = ""
+ i = self._rbuf.find('\n')
+ while i < 0 and not (0 < limit <= len(self._rbuf)):
+ new = self._raw_read(self._rbufsize)
+ if not new: break
+ i = new.find('\n')
+ if i >= 0: i = i + len(self._rbuf)
+ self._rbuf = self._rbuf + new
+ if i < 0: i = len(self._rbuf)
+ else: i = i+1
+ if 0 <= limit < len(self._rbuf): i = limit
+ data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
+ return data
+
+ def readlines(self, sizehint = 0):
+ total = 0
+ list = []
+ while 1:
+ line = self.readline()
+ if not line: break
+ list.append(line)
+ total += len(line)
+ if sizehint and total >= sizehint:
+ break
+ return list
+
+
+class HTTPConnection(httplib.HTTPConnection):
+ # use the modified response class
+ response_class = HTTPResponse
+
+class HTTPSConnection(httplib.HTTPSConnection):
+ response_class = HTTPResponse
+
+#########################################################################
+##### TEST FUNCTIONS
+#########################################################################
+
+def error_handler(url):
+ global HANDLE_ERRORS
+ orig = HANDLE_ERRORS
+ keepalive_handler = HTTPHandler()
+ opener = urllib2.build_opener(keepalive_handler)
+ urllib2.install_opener(opener)
+ pos = {0: 'off', 1: 'on'}
+ for i in (0, 1):
+ print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
+ HANDLE_ERRORS = i
+ try:
+ fo = urllib2.urlopen(url)
+ foo = fo.read()
+ fo.close()
+ try: status, reason = fo.status, fo.reason
+ except AttributeError: status, reason = None, None
+ except IOError, e:
+ print " EXCEPTION: %s" % e
+ raise
+ else:
+ print " status = %s, reason = %s" % (status, reason)
+ HANDLE_ERRORS = orig
+ hosts = keepalive_handler.open_connections()
+ print "open connections:", hosts
+ keepalive_handler.close_all()
+
+def continuity(url):
+ import md5
+ format = '%25s: %s'
+
+ # first fetch the file with the normal http handler
+ opener = urllib2.build_opener()
+ urllib2.install_opener(opener)
+ fo = urllib2.urlopen(url)
+ foo = fo.read()
+ fo.close()
+ m = md5.new(foo)
+ print format % ('normal urllib', m.hexdigest())
+
+ # now install the keepalive handler and try again
+ opener = urllib2.build_opener(HTTPHandler())
+ urllib2.install_opener(opener)
+
+ fo = urllib2.urlopen(url)
+ foo = fo.read()
+ fo.close()
+ m = md5.new(foo)
+ print format % ('keepalive read', m.hexdigest())
+
+ fo = urllib2.urlopen(url)
+ foo = ''
+ while 1:
+ f = fo.readline()
+ if f: foo = foo + f
+ else: break
+ fo.close()
+ m = md5.new(foo)
+ print format % ('keepalive readline', m.hexdigest())
+
+def comp(N, url):
+ print ' making %i connections to:\n %s' % (N, url)
+
+ sys.stdout.write(' first using the normal urllib handlers')
+ # first use normal opener
+ opener = urllib2.build_opener()
+ urllib2.install_opener(opener)
+ t1 = fetch(N, url)
+ print ' TIME: %.3f s' % t1
+
+ sys.stdout.write(' now using the keepalive handler ')
+ # now install the keepalive handler and try again
+ opener = urllib2.build_opener(HTTPHandler())
+ urllib2.install_opener(opener)
+ t2 = fetch(N, url)
+ print ' TIME: %.3f s' % t2
+ print ' improvement factor: %.2f' % (t1/t2, )
+
+def fetch(N, url, delay=0):
+ import time
+ lens = []
+ starttime = time.time()
+ for i in range(N):
+ if delay and i > 0: time.sleep(delay)
+ fo = urllib2.urlopen(url)
+ foo = fo.read()
+ fo.close()
+ lens.append(len(foo))
+ diff = time.time() - starttime
+
+ j = 0
+ for i in lens[1:]:
+ j = j + 1
+ if not i == lens[0]:
+ print "WARNING: inconsistent length on read %i: %i" % (j, i)
+
+ return diff
+
+def test_timeout(url):
+ global DEBUG
+ dbbackup = DEBUG
+ class FakeLogger:
+ def debug(self, msg, *args): print msg % args
+ info = warning = error = debug
+ DEBUG = FakeLogger()
+ print " fetching the file to establish a connection"
+ fo = urllib2.urlopen(url)
+ data1 = fo.read()
+ fo.close()
+
+ i = 20
+ print " waiting %i seconds for the server to close the connection" % i
+ while i > 0:
+ sys.stdout.write('\r %2i' % i)
+ sys.stdout.flush()
+ time.sleep(1)
+ i -= 1
+ sys.stderr.write('\r')
+
+ print " fetching the file a second time"
+ fo = urllib2.urlopen(url)
+ data2 = fo.read()
+ fo.close()
+
+ if data1 == data2:
+ print ' data are identical'
+ else:
+ print ' ERROR: DATA DIFFER'
+
+ DEBUG = dbbackup
+
+
+def test(url, N=10):
+ print "checking error hander (do this on a non-200)"
+ try: error_handler(url)
+ except IOError, e:
+ print "exiting - exception will prevent further tests"
+ sys.exit()
+ print
+ print "performing continuity test (making sure stuff isn't corrupted)"
+ continuity(url)
+ print
+ print "performing speed comparison"
+ comp(N, url)
+ print
+ print "performing dropped-connection check"
+ test_timeout(url)
+
+if __name__ == '__main__':
+ import time
+ import sys
+ try:
+ N = int(sys.argv[1])
+ url = sys.argv[2]
+ except:
+ print "%s <integer> <url>" % sys.argv[0]
+ else:
+ test(url, N)
diff --git a/mic/3rdparty/pykickstart/urlgrabber/mirror.py b/mic/3rdparty/pykickstart/urlgrabber/mirror.py
new file mode 100644
index 0000000..9664c6b
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/mirror.py
@@ -0,0 +1,458 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
+
+"""Module for downloading files from a pool of mirrors
+
+DESCRIPTION
+
+ This module provides support for downloading files from a pool of
+ mirrors with configurable failover policies. To a large extent, the
+ failover policy is chosen by using different classes derived from
+ the main class, MirrorGroup.
+
+ Instances of MirrorGroup (and cousins) act very much like URLGrabber
+ instances in that they have urlread, urlgrab, and urlopen methods.
+ They can therefore, be used in very similar ways.
+
+ from urlgrabber.grabber import URLGrabber
+ from urlgrabber.mirror import MirrorGroup
+ gr = URLGrabber()
+ mg = MirrorGroup(gr, ['http://foo.com/some/directory/',
+ 'http://bar.org/maybe/somewhere/else/',
+ 'ftp://baz.net/some/other/place/entirely/']
+ mg.urlgrab('relative/path.zip')
+
+ The assumption is that all mirrors are identical AFTER the base urls
+ specified, so that any mirror can be used to fetch any file.
+
+FAILOVER
+
+ The failover mechanism is designed to be customized by subclassing
+ from MirrorGroup to change the details of the behavior. In general,
+ the classes maintain a master mirror list and a "current mirror"
+ index. When a download is initiated, a copy of this list and index
+ is created for that download only. The specific failover policy
+ depends on the class used, and so is documented in the class
+ documentation. Note that ANY behavior of the class can be
+ overridden, so any failover policy at all is possible (although
+ you may need to change the interface in extreme cases).
+
+CUSTOMIZATION
+
+ Most customization of a MirrorGroup object is done at instantiation
+ time (or via subclassing). There are four major types of
+ customization:
+
+ 1) Pass in a custom urlgrabber - The passed in urlgrabber will be
+ used (by default... see #2) for the grabs, so options to it
+ apply for the url-fetching
+
+ 2) Custom mirror list - Mirror lists can simply be a list of
+ stings mirrors (as shown in the example above) but each can
+ also be a dict, allowing for more options. For example, the
+ first mirror in the list above could also have been:
+
+ {'mirror': 'http://foo.com/some/directory/',
+ 'grabber': <a custom grabber to be used for this mirror>,
+ 'kwargs': { <a dict of arguments passed to the grabber> }}
+
+ All mirrors are converted to this format internally. If
+ 'grabber' is omitted, the default grabber will be used. If
+ kwargs are omitted, then (duh) they will not be used.
+
+ 3) Pass keyword arguments when instantiating the mirror group.
+ See, for example, the failure_callback argument.
+
+ 4) Finally, any kwargs passed in for the specific file (to the
+ urlgrab method, for example) will be folded in. The options
+ passed into the grabber's urlXXX methods will override any
+ options specified in a custom mirror dict.
+
+"""
+
+# $Id: mirror.py,v 1.14 2006/02/22 18:26:46 mstenner Exp $
+
+import random
+import thread # needed for locking to make this threadsafe
+
+from grabber import URLGrabError, CallbackObject, DEBUG
+
+try:
+ from i18n import _
+except ImportError, msg:
+ def _(st): return st
+
+class GrabRequest:
+ """This is a dummy class used to hold information about the specific
+ request. For example, a single file. By maintaining this information
+ separately, we can accomplish two things:
+
+ 1) make it a little easier to be threadsafe
+ 2) have request-specific parameters
+ """
+ pass
+
+class MirrorGroup:
+ """Base Mirror class
+
+ Instances of this class are built with a grabber object and a list
+ of mirrors. Then all calls to urlXXX should be passed relative urls.
+ The requested file will be searched for on the first mirror. If the
+ grabber raises an exception (possibly after some retries) then that
+ mirror will be removed from the list, and the next will be attempted.
+ If all mirrors are exhausted, then an exception will be raised.
+
+ MirrorGroup has the following failover policy:
+
+ * downloads begin with the first mirror
+
+ * by default (see default_action below) a failure (after retries)
+ causes it to increment the local AND master indices. Also,
+ the current mirror is removed from the local list (but NOT the
+ master list - the mirror can potentially be used for other
+ files)
+
+ * if the local list is ever exhausted, a URLGrabError will be
+ raised (errno=256, no more mirrors)
+
+ OPTIONS
+
+ In addition to the required arguments "grabber" and "mirrors",
+ MirrorGroup also takes the following optional arguments:
+
+ default_action
+
+ A dict that describes the actions to be taken upon failure
+ (after retries). default_action can contain any of the
+ following keys (shown here with their default values):
+
+ default_action = {'increment': 1,
+ 'increment_master': 1,
+ 'remove': 1,
+ 'remove_master': 0,
+ 'fail': 0}
+
+ In this context, 'increment' means "use the next mirror" and
+ 'remove' means "never use this mirror again". The two
+ 'master' values refer to the instance-level mirror list (used
+ for all files), whereas the non-master values refer to the
+ current download only.
+
+ The 'fail' option will cause immediate failure by re-raising
+ the exception and no further attempts to get the current
+ download.
+
+ This dict can be set at instantiation time,
+ mg = MirrorGroup(grabber, mirrors, default_action={'fail':1})
+ at method-execution time (only applies to current fetch),
+ filename = mg.urlgrab(url, default_action={'increment': 0})
+ or by returning an action dict from the failure_callback
+ return {'fail':0}
+ in increasing precedence.
+
+ If all three of these were done, the net result would be:
+ {'increment': 0, # set in method
+ 'increment_master': 1, # class default
+ 'remove': 1, # class default
+ 'remove_master': 0, # class default
+ 'fail': 0} # set at instantiation, reset
+ # from callback
+
+ failure_callback
+
+ this is a callback that will be called when a mirror "fails",
+ meaning the grabber raises some URLGrabError. If this is a
+ tuple, it is interpreted to be of the form (cb, args, kwargs)
+ where cb is the actual callable object (function, method,
+ etc). Otherwise, it is assumed to be the callable object
+ itself. The callback will be passed a grabber.CallbackObject
+ instance along with args and kwargs (if present). The following
+ attributes are defined withing the instance:
+
+ obj.exception = < exception that was raised >
+ obj.mirror = < the mirror that was tried >
+ obj.relative_url = < url relative to the mirror >
+ obj.url = < full url that failed >
+ # .url is just the combination of .mirror
+ # and .relative_url
+
+ The failure callback can return an action dict, as described
+ above.
+
+ Like default_action, the failure_callback can be set at
+ instantiation time or when the urlXXX method is called. In
+ the latter case, it applies only for that fetch.
+
+ The callback can re-raise the exception quite easily. For
+ example, this is a perfectly adequate callback function:
+
+ def callback(obj): raise obj.exception
+
+ WARNING: do not save the exception object (or the
+ CallbackObject instance). As they contain stack frame
+ references, they can lead to circular references.
+
+ Notes:
+ * The behavior can be customized by deriving and overriding the
+ 'CONFIGURATION METHODS'
+ * The 'grabber' instance is kept as a reference, not copied.
+ Therefore, the grabber instance can be modified externally
+ and changes will take effect immediately.
+ """
+
+ # notes on thread-safety:
+
+ # A GrabRequest should never be shared by multiple threads because
+ # it's never saved inside the MG object and never returned outside it.
+ # therefore, it should be safe to access/modify grabrequest data
+ # without a lock. However, accessing the mirrors and _next attributes
+ # of the MG itself must be done when locked to prevent (for example)
+ # removal of the wrong mirror.
+
+ ##############################################################
+ # CONFIGURATION METHODS - intended to be overridden to
+ # customize behavior
+ def __init__(self, grabber, mirrors, **kwargs):
+ """Initialize the MirrorGroup object.
+
+ REQUIRED ARGUMENTS
+
+ grabber - URLGrabber instance
+ mirrors - a list of mirrors
+
+ OPTIONAL ARGUMENTS
+
+ failure_callback - callback to be used when a mirror fails
+ default_action - dict of failure actions
+
+ See the module-level and class level documentation for more
+ details.
+ """
+
+ # OVERRIDE IDEAS:
+ # shuffle the list to randomize order
+ self.grabber = grabber
+ self.mirrors = self._parse_mirrors(mirrors)
+ self._next = 0
+ self._lock = thread.allocate_lock()
+ self.default_action = None
+ self._process_kwargs(kwargs)
+
+ # if these values are found in **kwargs passed to one of the urlXXX
+ # methods, they will be stripped before getting passed on to the
+ # grabber
+ options = ['default_action', 'failure_callback']
+
+ def _process_kwargs(self, kwargs):
+ self.failure_callback = kwargs.get('failure_callback')
+ self.default_action = kwargs.get('default_action')
+
+ def _parse_mirrors(self, mirrors):
+ parsed_mirrors = []
+ for m in mirrors:
+ if type(m) == type(''): m = {'mirror': m}
+ parsed_mirrors.append(m)
+ return parsed_mirrors
+
+ def _load_gr(self, gr):
+ # OVERRIDE IDEAS:
+ # shuffle gr list
+ self._lock.acquire()
+ gr.mirrors = list(self.mirrors)
+ gr._next = self._next
+ self._lock.release()
+
+ def _get_mirror(self, gr):
+ # OVERRIDE IDEAS:
+ # return a random mirror so that multiple mirrors get used
+ # even without failures.
+ if not gr.mirrors:
+ raise URLGrabError(256, _('No more mirrors to try.'))
+ return gr.mirrors[gr._next]
+
+ def _failure(self, gr, cb_obj):
+ # OVERRIDE IDEAS:
+ # inspect the error - remove=1 for 404, remove=2 for connection
+ # refused, etc. (this can also be done via
+ # the callback)
+ cb = gr.kw.get('failure_callback') or self.failure_callback
+ if cb:
+ if type(cb) == type( () ):
+ cb, args, kwargs = cb
+ else:
+ args, kwargs = (), {}
+ action = cb(cb_obj, *args, **kwargs) or {}
+ else:
+ action = {}
+ # XXXX - decide - there are two ways to do this
+ # the first is action-overriding as a whole - use the entire action
+ # or fall back on module level defaults
+ #action = action or gr.kw.get('default_action') or self.default_action
+ # the other is to fall through for each element in the action dict
+ a = dict(self.default_action or {})
+ a.update(gr.kw.get('default_action', {}))
+ a.update(action)
+ action = a
+ self.increment_mirror(gr, action)
+ if action and action.get('fail', 0): raise
+
+ def increment_mirror(self, gr, action={}):
+ """Tell the mirror object increment the mirror index
+
+ This increments the mirror index, which amounts to telling the
+ mirror object to use a different mirror (for this and future
+ downloads).
+
+ This is a SEMI-public method. It will be called internally,
+ and you may never need to call it. However, it is provided
+ (and is made public) so that the calling program can increment
+ the mirror choice for methods like urlopen. For example, with
+ urlopen, there's no good way for the mirror group to know that
+ an error occurs mid-download (it's already returned and given
+ you the file object).
+
+ remove --- can have several values
+ 0 do not remove the mirror from the list
+ 1 remove the mirror for this download only
+ 2 remove the mirror permanently
+
+ beware of remove=0 as it can lead to infinite loops
+ """
+ badmirror = gr.mirrors[gr._next]
+
+ self._lock.acquire()
+ try:
+ ind = self.mirrors.index(badmirror)
+ except ValueError:
+ pass
+ else:
+ if action.get('remove_master', 0):
+ del self.mirrors[ind]
+ elif self._next == ind and action.get('increment_master', 1):
+ self._next += 1
+ if self._next >= len(self.mirrors): self._next = 0
+ self._lock.release()
+
+ if action.get('remove', 1):
+ del gr.mirrors[gr._next]
+ elif action.get('increment', 1):
+ gr._next += 1
+ if gr._next >= len(gr.mirrors): gr._next = 0
+
+ if DEBUG:
+ grm = [m['mirror'] for m in gr.mirrors]
+ DEBUG.info('GR mirrors: [%s] %i', ' '.join(grm), gr._next)
+ selfm = [m['mirror'] for m in self.mirrors]
+ DEBUG.info('MAIN mirrors: [%s] %i', ' '.join(selfm), self._next)
+
+ #####################################################################
+ # NON-CONFIGURATION METHODS
+ # these methods are designed to be largely workhorse methods that
+ # are not intended to be overridden. That doesn't mean you can't;
+ # if you want to, feel free, but most things can be done by
+ # by overriding the configuration methods :)
+
+ def _join_url(self, base_url, rel_url):
+ if base_url.endswith('/') or rel_url.startswith('/'):
+ return base_url + rel_url
+ else:
+ return base_url + '/' + rel_url
+
+ def _mirror_try(self, func, url, kw):
+ gr = GrabRequest()
+ gr.func = func
+ gr.url = url
+ gr.kw = dict(kw)
+ self._load_gr(gr)
+
+ for k in self.options:
+ try: del kw[k]
+ except KeyError: pass
+
+ while 1:
+ mirrorchoice = self._get_mirror(gr)
+ fullurl = self._join_url(mirrorchoice['mirror'], gr.url)
+ kwargs = dict(mirrorchoice.get('kwargs', {}))
+ kwargs.update(kw)
+ grabber = mirrorchoice.get('grabber') or self.grabber
+ func_ref = getattr(grabber, func)
+ if DEBUG: DEBUG.info('MIRROR: trying %s -> %s', url, fullurl)
+ try:
+ return func_ref( *(fullurl,), **kwargs )
+ except URLGrabError, e:
+ if DEBUG: DEBUG.info('MIRROR: failed')
+ obj = CallbackObject()
+ obj.exception = e
+ obj.mirror = mirrorchoice['mirror']
+ obj.relative_url = gr.url
+ obj.url = fullurl
+ self._failure(gr, obj)
+
+ def urlgrab(self, url, filename=None, **kwargs):
+ kw = dict(kwargs)
+ kw['filename'] = filename
+ func = 'urlgrab'
+ return self._mirror_try(func, url, kw)
+
+ def urlopen(self, url, **kwargs):
+ kw = dict(kwargs)
+ func = 'urlopen'
+ return self._mirror_try(func, url, kw)
+
+ def urlread(self, url, limit=None, **kwargs):
+ kw = dict(kwargs)
+ kw['limit'] = limit
+ func = 'urlread'
+ return self._mirror_try(func, url, kw)
+
+
+class MGRandomStart(MirrorGroup):
+ """A mirror group that starts at a random mirror in the list.
+
+ This behavior of this class is identical to MirrorGroup, except that
+ it starts at a random location in the mirror list.
+ """
+
+ def __init__(self, grabber, mirrors, **kwargs):
+ """Initialize the object
+
+ The arguments for intialization are the same as for MirrorGroup
+ """
+ MirrorGroup.__init__(self, grabber, mirrors, **kwargs)
+ self._next = random.randrange(len(mirrors))
+
+class MGRandomOrder(MirrorGroup):
+ """A mirror group that uses mirrors in a random order.
+
+ This behavior of this class is identical to MirrorGroup, except that
+ it uses the mirrors in a random order. Note that the order is set at
+ initialization time and fixed thereafter. That is, it does not pick a
+ random mirror after each failure.
+ """
+
+ def __init__(self, grabber, mirrors, **kwargs):
+ """Initialize the object
+
+ The arguments for intialization are the same as for MirrorGroup
+ """
+ MirrorGroup.__init__(self, grabber, mirrors, **kwargs)
+ random.shuffle(self.mirrors)
+
+if __name__ == '__main__':
+ pass
diff --git a/mic/3rdparty/pykickstart/urlgrabber/progress.py b/mic/3rdparty/pykickstart/urlgrabber/progress.py
new file mode 100644
index 0000000..02db524
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/progress.py
@@ -0,0 +1,530 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
+
+# $Id: progress.py,v 1.7 2005/08/19 21:59:07 mstenner Exp $
+
+import sys
+import time
+import math
+import thread
+
+class BaseMeter:
+ def __init__(self):
+ self.update_period = 0.3 # seconds
+
+ self.filename = None
+ self.url = None
+ self.basename = None
+ self.text = None
+ self.size = None
+ self.start_time = None
+ self.last_amount_read = 0
+ self.last_update_time = None
+ self.re = RateEstimator()
+
+ def start(self, filename=None, url=None, basename=None,
+ size=None, now=None, text=None):
+ self.filename = filename
+ self.url = url
+ self.basename = basename
+ self.text = text
+
+ #size = None ######### TESTING
+ self.size = size
+ if not size is None: self.fsize = format_number(size) + 'B'
+
+ if now is None: now = time.time()
+ self.start_time = now
+ self.re.start(size, now)
+ self.last_amount_read = 0
+ self.last_update_time = now
+ self._do_start(now)
+
+ def _do_start(self, now=None):
+ pass
+
+ def update(self, amount_read, now=None):
+ # for a real gui, you probably want to override and put a call
+ # to your mainloop iteration function here
+ if now is None: now = time.time()
+ if (now >= self.last_update_time + self.update_period) or \
+ not self.last_update_time:
+ self.re.update(amount_read, now)
+ self.last_amount_read = amount_read
+ self.last_update_time = now
+ self._do_update(amount_read, now)
+
+ def _do_update(self, amount_read, now=None):
+ pass
+
+ def end(self, amount_read, now=None):
+ if now is None: now = time.time()
+ self.re.update(amount_read, now)
+ self.last_amount_read = amount_read
+ self.last_update_time = now
+ self._do_end(amount_read, now)
+
+ def _do_end(self, amount_read, now=None):
+ pass
+
+class TextMeter(BaseMeter):
+ def __init__(self, fo=sys.stderr):
+ BaseMeter.__init__(self)
+ self.fo = fo
+
+ def _do_update(self, amount_read, now=None):
+ etime = self.re.elapsed_time()
+ fetime = format_time(etime)
+ fread = format_number(amount_read)
+ #self.size = None
+ if self.text is not None:
+ text = self.text
+ else:
+ text = self.basename
+ if self.size is None:
+ out = '\r%-60.60s %5sB %s ' % \
+ (text, fread, fetime)
+ else:
+ rtime = self.re.remaining_time()
+ frtime = format_time(rtime)
+ frac = self.re.fraction_read()
+ bar = '='*int(25 * frac)
+
+ out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ETA ' % \
+ (text, frac*100, bar, fread, frtime)
+
+ self.fo.write(out)
+ self.fo.flush()
+
+ def _do_end(self, amount_read, now=None):
+ total_time = format_time(self.re.elapsed_time())
+ total_size = format_number(amount_read)
+ if self.text is not None:
+ text = self.text
+ else:
+ text = self.basename
+ if self.size is None:
+ out = '\r%-60.60s %5sB %s ' % \
+ (text, total_size, total_time)
+ else:
+ bar = '='*25
+ out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ' % \
+ (text, 100, bar, total_size, total_time)
+ self.fo.write(out + '\n')
+ self.fo.flush()
+
+text_progress_meter = TextMeter
+
+class MultiFileHelper(BaseMeter):
+ def __init__(self, master):
+ BaseMeter.__init__(self)
+ self.master = master
+
+ def _do_start(self, now):
+ self.master.start_meter(self, now)
+
+ def _do_update(self, amount_read, now):
+ # elapsed time since last update
+ self.master.update_meter(self, now)
+
+ def _do_end(self, amount_read, now):
+ self.ftotal_time = format_time(now - self.start_time)
+ self.ftotal_size = format_number(self.last_amount_read)
+ self.master.end_meter(self, now)
+
+ def failure(self, message, now=None):
+ self.master.failure_meter(self, message, now)
+
+ def message(self, message):
+ self.master.message_meter(self, message)
+
+class MultiFileMeter:
+ helperclass = MultiFileHelper
+ def __init__(self):
+ self.meters = []
+ self.in_progress_meters = []
+ self._lock = thread.allocate_lock()
+ self.update_period = 0.3 # seconds
+
+ self.numfiles = None
+ self.finished_files = 0
+ self.failed_files = 0
+ self.open_files = 0
+ self.total_size = None
+ self.failed_size = 0
+ self.start_time = None
+ self.finished_file_size = 0
+ self.last_update_time = None
+ self.re = RateEstimator()
+
+ def start(self, numfiles=None, total_size=None, now=None):
+ if now is None: now = time.time()
+ self.numfiles = numfiles
+ self.finished_files = 0
+ self.failed_files = 0
+ self.open_files = 0
+ self.total_size = total_size
+ self.failed_size = 0
+ self.start_time = now
+ self.finished_file_size = 0
+ self.last_update_time = now
+ self.re.start(total_size, now)
+ self._do_start(now)
+
+ def _do_start(self, now):
+ pass
+
+ def end(self, now=None):
+ if now is None: now = time.time()
+ self._do_end(now)
+
+ def _do_end(self, now):
+ pass
+
+ def lock(self): self._lock.acquire()
+ def unlock(self): self._lock.release()
+
+ ###########################################################
+ # child meter creation and destruction
+ def newMeter(self):
+ newmeter = self.helperclass(self)
+ self.meters.append(newmeter)
+ return newmeter
+
+ def removeMeter(self, meter):
+ self.meters.remove(meter)
+
+ ###########################################################
+ # child functions - these should only be called by helpers
+ def start_meter(self, meter, now):
+ if not meter in self.meters:
+ raise ValueError('attempt to use orphaned meter')
+ self._lock.acquire()
+ try:
+ if not meter in self.in_progress_meters:
+ self.in_progress_meters.append(meter)
+ self.open_files += 1
+ finally:
+ self._lock.release()
+ self._do_start_meter(meter, now)
+
+ def _do_start_meter(self, meter, now):
+ pass
+
+ def update_meter(self, meter, now):
+ if not meter in self.meters:
+ raise ValueError('attempt to use orphaned meter')
+ if (now >= self.last_update_time + self.update_period) or \
+ not self.last_update_time:
+ self.re.update(self._amount_read(), now)
+ self.last_update_time = now
+ self._do_update_meter(meter, now)
+
+ def _do_update_meter(self, meter, now):
+ pass
+
+ def end_meter(self, meter, now):
+ if not meter in self.meters:
+ raise ValueError('attempt to use orphaned meter')
+ self._lock.acquire()
+ try:
+ try: self.in_progress_meters.remove(meter)
+ except ValueError: pass
+ self.open_files -= 1
+ self.finished_files += 1
+ self.finished_file_size += meter.last_amount_read
+ finally:
+ self._lock.release()
+ self._do_end_meter(meter, now)
+
+ def _do_end_meter(self, meter, now):
+ pass
+
+ def failure_meter(self, meter, message, now):
+ if not meter in self.meters:
+ raise ValueError('attempt to use orphaned meter')
+ self._lock.acquire()
+ try:
+ try: self.in_progress_meters.remove(meter)
+ except ValueError: pass
+ self.open_files -= 1
+ self.failed_files += 1
+ if meter.size and self.failed_size is not None:
+ self.failed_size += meter.size
+ else:
+ self.failed_size = None
+ finally:
+ self._lock.release()
+ self._do_failure_meter(meter, message, now)
+
+ def _do_failure_meter(self, meter, message, now):
+ pass
+
+ def message_meter(self, meter, message):
+ pass
+
+ ########################################################
+ # internal functions
+ def _amount_read(self):
+ tot = self.finished_file_size
+ for m in self.in_progress_meters:
+ tot += m.last_amount_read
+ return tot
+
+
+class TextMultiFileMeter(MultiFileMeter):
+ def __init__(self, fo=sys.stderr):
+ self.fo = fo
+ MultiFileMeter.__init__(self)
+
+ # files: ###/### ###% data: ######/###### ###% time: ##:##:##/##:##:##
+ def _do_update_meter(self, meter, now):
+ self._lock.acquire()
+ try:
+ format = "files: %3i/%-3i %3i%% data: %6.6s/%-6.6s %3i%% " \
+ "time: %8.8s/%8.8s"
+ df = self.finished_files
+ tf = self.numfiles or 1
+ pf = 100 * float(df)/tf + 0.49
+ dd = self.re.last_amount_read
+ td = self.total_size
+ pd = 100 * (self.re.fraction_read() or 0) + 0.49
+ dt = self.re.elapsed_time()
+ rt = self.re.remaining_time()
+ if rt is None: tt = None
+ else: tt = dt + rt
+
+ fdd = format_number(dd) + 'B'
+ ftd = format_number(td) + 'B'
+ fdt = format_time(dt, 1)
+ ftt = format_time(tt, 1)
+
+ out = '%-79.79s' % (format % (df, tf, pf, fdd, ftd, pd, fdt, ftt))
+ self.fo.write('\r' + out)
+ self.fo.flush()
+ finally:
+ self._lock.release()
+
+ def _do_end_meter(self, meter, now):
+ self._lock.acquire()
+ try:
+ format = "%-30.30s %6.6s %8.8s %9.9s"
+ fn = meter.basename
+ size = meter.last_amount_read
+ fsize = format_number(size) + 'B'
+ et = meter.re.elapsed_time()
+ fet = format_time(et, 1)
+ frate = format_number(size / et) + 'B/s'
+
+ out = '%-79.79s' % (format % (fn, fsize, fet, frate))
+ self.fo.write('\r' + out + '\n')
+ finally:
+ self._lock.release()
+ self._do_update_meter(meter, now)
+
+ def _do_failure_meter(self, meter, message, now):
+ self._lock.acquire()
+ try:
+ format = "%-30.30s %6.6s %s"
+ fn = meter.basename
+ if type(message) in (type(''), type(u'')):
+ message = message.splitlines()
+ if not message: message = ['']
+ out = '%-79s' % (format % (fn, 'FAILED', message[0] or ''))
+ self.fo.write('\r' + out + '\n')
+ for m in message[1:]: self.fo.write(' ' + m + '\n')
+ self._lock.release()
+ finally:
+ self._do_update_meter(meter, now)
+
+ def message_meter(self, meter, message):
+ self._lock.acquire()
+ try:
+ pass
+ finally:
+ self._lock.release()
+
+ def _do_end(self, now):
+ self._do_update_meter(None, now)
+ self._lock.acquire()
+ try:
+ self.fo.write('\n')
+ self.fo.flush()
+ finally:
+ self._lock.release()
+
+######################################################################
+# support classes and functions
+
+class RateEstimator:
+ def __init__(self, timescale=5.0):
+ self.timescale = timescale
+
+ def start(self, total=None, now=None):
+ if now is None: now = time.time()
+ self.total = total
+ self.start_time = now
+ self.last_update_time = now
+ self.last_amount_read = 0
+ self.ave_rate = None
+
+ def update(self, amount_read, now=None):
+ if now is None: now = time.time()
+ if amount_read == 0:
+ # if we just started this file, all bets are off
+ self.last_update_time = now
+ self.last_amount_read = 0
+ self.ave_rate = None
+ return
+
+ #print 'times', now, self.last_update_time
+ time_diff = now - self.last_update_time
+ read_diff = amount_read - self.last_amount_read
+ self.last_update_time = now
+ self.last_amount_read = amount_read
+ self.ave_rate = self._temporal_rolling_ave(\
+ time_diff, read_diff, self.ave_rate, self.timescale)
+ #print 'results', time_diff, read_diff, self.ave_rate
+
+ #####################################################################
+ # result methods
+ def average_rate(self):
+ "get the average transfer rate (in bytes/second)"
+ return self.ave_rate
+
+ def elapsed_time(self):
+ "the time between the start of the transfer and the most recent update"
+ return self.last_update_time - self.start_time
+
+ def remaining_time(self):
+ "estimated time remaining"
+ if not self.ave_rate or not self.total: return None
+ return (self.total - self.last_amount_read) / self.ave_rate
+
+ def fraction_read(self):
+ """the fraction of the data that has been read
+ (can be None for unknown transfer size)"""
+ if self.total is None: return None
+ elif self.total == 0: return 1.0
+ else: return float(self.last_amount_read)/self.total
+
+ #########################################################################
+ # support methods
+ def _temporal_rolling_ave(self, time_diff, read_diff, last_ave, timescale):
+ """a temporal rolling average performs smooth averaging even when
+ updates come at irregular intervals. This is performed by scaling
+ the "epsilon" according to the time since the last update.
+ Specifically, epsilon = time_diff / timescale
+
+ As a general rule, the average will take on a completely new value
+ after 'timescale' seconds."""
+ epsilon = time_diff / timescale
+ if epsilon > 1: epsilon = 1.0
+ return self._rolling_ave(time_diff, read_diff, last_ave, epsilon)
+
+ def _rolling_ave(self, time_diff, read_diff, last_ave, epsilon):
+ """perform a "rolling average" iteration
+ a rolling average "folds" new data into an existing average with
+ some weight, epsilon. epsilon must be between 0.0 and 1.0 (inclusive)
+ a value of 0.0 means only the old value (initial value) counts,
+ and a value of 1.0 means only the newest value is considered."""
+
+ try:
+ recent_rate = read_diff / time_diff
+ except ZeroDivisionError:
+ recent_rate = None
+ if last_ave is None: return recent_rate
+ elif recent_rate is None: return last_ave
+
+ # at this point, both last_ave and recent_rate are numbers
+ return epsilon * recent_rate + (1 - epsilon) * last_ave
+
+ def _round_remaining_time(self, rt, start_time=15.0):
+ """round the remaining time, depending on its size
+ If rt is between n*start_time and (n+1)*start_time round downward
+ to the nearest multiple of n (for any counting number n).
+ If rt < start_time, round down to the nearest 1.
+ For example (for start_time = 15.0):
+ 2.7 -> 2.0
+ 25.2 -> 25.0
+ 26.4 -> 26.0
+ 35.3 -> 34.0
+ 63.6 -> 60.0
+ """
+
+ if rt < 0: return 0.0
+ shift = int(math.log(rt/start_time)/math.log(2))
+ rt = int(rt)
+ if shift <= 0: return rt
+ return float(int(rt) >> shift << shift)
+
+
+def format_time(seconds, use_hours=0):
+ if seconds is None or seconds < 0:
+ if use_hours: return '--:--:--'
+ else: return '--:--'
+ else:
+ seconds = int(seconds)
+ minutes = seconds / 60
+ seconds = seconds % 60
+ if use_hours:
+ hours = minutes / 60
+ minutes = minutes % 60
+ return '%02i:%02i:%02i' % (hours, minutes, seconds)
+ else:
+ return '%02i:%02i' % (minutes, seconds)
+
+def format_number(number, SI=0, space=' '):
+ """Turn numbers into human-readable metric-like numbers"""
+ symbols = ['', # (none)
+ 'k', # kilo
+ 'M', # mega
+ 'G', # giga
+ 'T', # tera
+ 'P', # peta
+ 'E', # exa
+ 'Z', # zetta
+ 'Y'] # yotta
+
+ if SI: step = 1000.0
+ else: step = 1024.0
+
+ thresh = 999
+ depth = 0
+ max_depth = len(symbols) - 1
+
+ # we want numbers between 0 and thresh, but don't exceed the length
+ # of our list. In that event, the formatting will be screwed up,
+ # but it'll still show the right number.
+ while number > thresh and depth < max_depth:
+ depth = depth + 1
+ number = number / step
+
+ if type(number) == type(1) or type(number) == type(1L):
+ # it's an int or a long, which means it didn't get divided,
+ # which means it's already short enough
+ format = '%i%s%s'
+ elif number < 9.95:
+ # must use 9.95 for proper sizing. For example, 9.99 will be
+ # rounded to 10.0 with the .1f format string (which is too long)
+ format = '%.1f%s%s'
+ else:
+ format = '%.0f%s%s'
+
+ return(format % (float(number or 0), space, symbols[depth]))
diff --git a/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py b/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py
new file mode 100644
index 0000000..07848da
--- /dev/null
+++ b/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py
@@ -0,0 +1,90 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+# This file is part of urlgrabber, a high-level cross-protocol url-grabber
+
+import httplib
+import urllib2
+
+try:
+ from M2Crypto import SSL
+ from M2Crypto import httpslib
+ from M2Crypto import m2urllib2
+
+ SSL.Connection.clientPostConnectionCheck = None
+ have_m2crypto = True
+except ImportError:
+ have_m2crypto = False
+
+DEBUG = None
+
+if have_m2crypto:
+
+ class M2SSLFactory:
+
+ def __init__(self, ssl_ca_cert, ssl_context):
+ self.ssl_context = self._get_ssl_context(ssl_ca_cert, ssl_context)
+
+ def _get_ssl_context(self, ssl_ca_cert, ssl_context):
+ """
+ Create an ssl context using the CA cert file or ssl context.
+
+ The CA cert is used first if it was passed as an option. If not,
+ then the supplied ssl context is used. If no ssl context was supplied,
+ None is returned.
+ """
+ if ssl_ca_cert:
+ context = SSL.Context()
+ context.load_verify_locations(ssl_ca_cert)
+ context.set_verify(SSL.verify_none, -1)
+ return context
+ else:
+ return ssl_context
+
+ def create_https_connection(self, host, response_class = None):
+ connection = httplib.HTTPSConnection(host, self.ssl_context)
+ if response_class:
+ connection.response_class = response_class
+ return connection
+
+ def create_opener(self, *handlers):
+ return m2urllib2.build_opener(self.ssl_context, *handlers)
+
+
+class SSLFactory:
+
+ def create_https_connection(self, host, response_class = None):
+ connection = httplib.HTTPSConnection(host)
+ if response_class:
+ connection.response_class = response_class
+ return connection
+
+ def create_opener(self, *handlers):
+ return urllib2.build_opener(*handlers)
+
+
+
+def get_factory(ssl_ca_cert = None, ssl_context = None):
+ """ Return an SSLFactory, based on if M2Crypto is available. """
+ if have_m2crypto:
+ return M2SSLFactory(ssl_ca_cert, ssl_context)
+ else:
+ # Log here if someone provides the args but we don't use them.
+ if ssl_ca_cert or ssl_context:
+ if DEBUG:
+ DEBUG.warning("SSL arguments supplied, but M2Crypto is not available. "
+ "Using Python SSL.")
+ return SSLFactory()
diff --git a/mic/3rdparty/pykickstart/version.py b/mic/3rdparty/pykickstart/version.py
new file mode 100644
index 0000000..102cc37
--- /dev/null
+++ b/mic/3rdparty/pykickstart/version.py
@@ -0,0 +1,197 @@
+#
+# Chris Lumens <clumens@redhat.com>
+#
+# Copyright 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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. Any Red Hat
+# trademarks that are incorporated in the source code or documentation are not
+# subject to the GNU General Public License and may only be used or replicated
+# with the express permission of Red Hat, Inc.
+#
+"""
+Methods for working with kickstart versions.
+
+This module defines several symbolic constants that specify kickstart syntax
+versions. Each version corresponds roughly to one release of Red Hat Linux,
+Red Hat Enterprise Linux, or Fedora Core as these are where most syntax
+changes take place.
+
+This module also exports several functions:
+
+ makeVersion - Given a version number, return an instance of the
+ matching handler class.
+
+ returnClassForVersion - Given a version number, return the matching
+ handler class. This does not return an
+ instance of that class, however.
+
+ stringToVersion - Convert a string representation of a version number
+ into the symbolic constant.
+
+ versionToString - Perform the reverse mapping.
+
+ versionFromFile - Read a kickstart file and determine the version of
+ syntax it uses. This requires the kickstart file to
+ have a version= comment in it.
+"""
+import imputil, re, sys
+from urlgrabber import urlopen
+
+import gettext
+_ = lambda x: gettext.ldgettext("pykickstart", x)
+
+from pykickstart.errors import KickstartVersionError
+
+# Symbolic names for internal version numbers.
+RHEL3 = 900
+FC3 = 1000
+RHEL4 = 1100
+FC4 = 2000
+FC5 = 3000
+FC6 = 4000
+RHEL5 = 4100
+F7 = 5000
+F8 = 6000
+F9 = 7000
+F10 = 8000
+F11 = 9000
+F12 = 10000
+F13 = 11000
+RHEL6 = 11100
+F14 = 12000
+F15 = 13000
+F16 = 14000
+
+# This always points at the latest version and is the default.
+DEVEL = F16
+
+# A one-to-one mapping from string representations to version numbers.
+versionMap = {
+ "DEVEL": DEVEL,
+ "FC3": FC3, "FC4": FC4, "FC5": FC5, "FC6": FC6, "F7": F7, "F8": F8,
+ "F9": F9, "F10": F10, "F11": F11, "F12": F12, "F13": F13,
+ "F14": F14, "F15": F15, "F16": F16,
+ "RHEL3": RHEL3, "RHEL4": RHEL4, "RHEL5": RHEL5, "RHEL6": RHEL6
+}
+
+def stringToVersion(s):
+ """Convert string into one of the provided version constants. Raises
+ KickstartVersionError if string does not match anything.
+ """
+ # First try these short forms.
+ try:
+ return versionMap[s.upper()]
+ except KeyError:
+ pass
+
+ # Now try the Fedora versions.
+ m = re.match("^fedora.* (\d+)$", s, re.I)
+
+ if m and m.group(1):
+ if versionMap.has_key("FC" + m.group(1)):
+ return versionMap["FC" + m.group(1)]
+ elif versionMap.has_key("F" + m.group(1)):
+ return versionMap["F" + m.group(1)]
+ else:
+ raise KickstartVersionError(_("Unsupported version specified: %s") % s)
+
+ # Now try the RHEL versions.
+ m = re.match("^red hat enterprise linux.* (\d+)([\.\d]*)$", s, re.I)
+
+ if m and m.group(1):
+ if versionMap.has_key("RHEL" + m.group(1)):
+ return versionMap["RHEL" + m.group(1)]
+ else:
+ raise KickstartVersionError(_("Unsupported version specified: %s") % s)
+
+ # If nothing else worked, we're out of options.
+ raise KickstartVersionError(_("Unsupported version specified: %s") % s)
+
+def versionToString(version, skipDevel=False):
+ """Convert version into a string representation of the version number.
+ This is the reverse operation of stringToVersion. Raises
+ KickstartVersionError if version does not match anything.
+ """
+ if not skipDevel and version == versionMap["DEVEL"]:
+ return "DEVEL"
+
+ for (key, val) in versionMap.iteritems():
+ if key == "DEVEL":
+ continue
+ elif val == version:
+ return key
+
+ raise KickstartVersionError(_("Unsupported version specified: %s") % version)
+
+def versionFromFile(f):
+ """Given a file or URL, look for a line starting with #version= and
+ return the version number. If no version is found, return DEVEL.
+ """
+ v = DEVEL
+
+ fh = urlopen(f)
+
+ while True:
+ try:
+ l = fh.readline()
+ except StopIteration:
+ break
+
+ # At the end of the file?
+ if l == "":
+ break
+
+ if l.isspace() or l.strip() == "":
+ continue
+
+ if l[:9] == "#version=":
+ v = stringToVersion(l[9:].rstrip())
+ break
+
+ fh.close()
+ return v
+
+def returnClassForVersion(version=DEVEL):
+ """Return the class of the syntax handler for version. version can be
+ either a string or the matching constant. Raises KickstartValueError
+ if version does not match anything.
+ """
+ try:
+ version = int(version)
+ module = "%s" % versionToString(version, skipDevel=True)
+ except ValueError:
+ module = "%s" % version
+ version = stringToVersion(version)
+
+ module = module.lower()
+
+ try:
+ import pykickstart.handlers
+ sys.path.extend(pykickstart.handlers.__path__)
+ found = imputil.imp.find_module(module)
+ loaded = imputil.imp.load_module(module, found[0], found[1], found[2])
+
+ for (k, v) in loaded.__dict__.iteritems():
+ if k.lower().endswith("%shandler" % module):
+ return v
+ except:
+ raise KickstartVersionError(_("Unsupported version specified: %s") % version)
+
+def makeVersion(version=DEVEL):
+ """Return a new instance of the syntax handler for version. version can be
+ either a string or the matching constant. This function is useful for
+ standalone programs which just need to handle a specific version of
+ kickstart syntax (as provided by a command line argument, for example)
+ and need to instantiate the correct object.
+ """
+ cl = returnClassForVersion(version)
+ return cl()
diff --git a/mic/__init__.py b/mic/__init__.py
new file mode 100644
index 0000000..63c1d9c
--- /dev/null
+++ b/mic/__init__.py
@@ -0,0 +1,4 @@
+import os, sys
+
+cur_path = os.path.dirname(__file__) or '.'
+sys.path.insert(0, cur_path + '/3rdparty')
diff --git a/mic/bootstrap.py b/mic/bootstrap.py
new file mode 100644
index 0000000..0eb2b7a
--- /dev/null
+++ b/mic/bootstrap.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+import os, sys
+import pickle
+import shutil
+import subprocess
+import rpm
+
+from mic import msger
+from mic import chroot
+from mic.plugin import pluginmgr
+from mic.utils import proxy
+from mic.utils import misc
+from mic.utils import errors
+
+minibase_grps = [ "tizen-bootstrap" ]
+minibase_pkgs = [ ]
+required_pkgs = [ "syslinux", "syslinux-extlinux", "satsolver-tools",
+ "libzypp", "python-zypp", "qemu-arm-static", "mic" ]
+
+
+def query_package_rpmdb(root='/', tag='name', pattern=None):
+ name = pattern
+ version = None
+ ts = rpm.TransactionSet(root)
+ mi = ts.dbMatch(tag, pattern)
+ for hdr in mi:
+ version = hdr['version']
+ return (name, version)
+
+def query_package_metadat(root='/', tag='name', pattern=None):
+ name = pattern
+ version = None
+ try:
+ with open(root + '/.metadata', 'r') as f:
+ metadata = pickle.load(f)
+ f.close()
+ except:
+ raise errors.BootstrapError("Load %s/.metadata error" % root)
+ else:
+ for pkg in metadata.keys():
+ m = misc.RPM_RE.match(pkg)
+ if m:
+ (n, a, v, r) = m.groups()
+ else:
+ raise errors.BootstrapError("Wrong Format .metadata in %s"
+ % root)
+ if n == pattern:
+ version = v
+ return (name, version)
+
+class Bootstrap(object):
+ def __init__(self, homedir='/var/mic/bootstrap', **kwargs):
+ self._pkgmgr = None
+ self._rootdir = None
+ self._bootstraps = []
+ self.homedir = homedir
+
+ if not os.path.exists(self.homedir):
+ os.makedirs(self.homedir)
+
+ self.__dict__.update(**kwargs)
+
+ def _setRootdir(self, name):
+ self._rootdir = os.path.join(self.homedir, name)
+
+ def _getRootdir(self):
+ if not os.path.exists(self._rootdir):
+ raise errors.BootstrapError("dir: %s not exist" % self._rootdir)
+ return self._rootdir
+
+ rootdir = property(fget = lambda self: self._getRootdir(),
+ fset = lambda self, name: self._setRootdir(name),
+ doc = 'root directory')
+
+ def _setPkgmgr(self, name):
+ backend_plugins = pluginmgr.get_plugins('backend')
+ for (key, cls) in backend_plugins.iteritems():
+ if key == name:
+ self._pkgmgr = cls
+ if not self._pkgmgr:
+ raise errors.BootstrapError("Backend: %s can't be loaded correctly"\
+ % name)
+
+ pkgmgr = property(fget = lambda self: self._pkgmgr,
+ fset = lambda self, name: self._setPkgmgr(name),
+ doc = 'package manager')
+
+ @property
+ def bootstraps(self):
+ if self._bootstraps:
+ return self._bootstraps
+ for dir in os.listdir(self.homedir):
+ metadata_fp = os.path.join(self.homedir, dir, '.metadata')
+ if os.path.exists(metadata_fp) \
+ and 0 != os.path.getsize(metadata_fp):
+ self._bootstraps.append(dir)
+ return self._bootstraps
+
+ def run(self, name, cmd, chdir='/', bindmounts=None):
+ self.rootdir = name
+ def mychroot():
+ os.chroot(self.rootdir)
+ os.chdir(chdir)
+
+ if isinstance(cmd, list):
+ cmd = ' '.join(cmd)
+
+ lvl = msger.get_loglevel()
+ msger.set_loglevel('quiet')
+ globalmounts = chroot.setup_chrootenv(self.rootdir, bindmounts)
+ try:
+ proxy.set_proxy_environ()
+ subprocess.call(cmd, preexec_fn=mychroot, shell=True)
+ proxy.unset_proxy_environ()
+ except:
+ raise errors.BootstrapError("Run in bootstrap fail")
+ finally:
+ chroot.cleanup_chrootenv(self.rootdir, bindmounts, globalmounts)
+
+ msger.set_loglevel(lvl)
+
+ def list(self, **kwargs):
+ bslist = []
+ for binst in self.bootstraps:
+ (mver, kver, rver) = self.status(binst)
+ bsinfo = {'name':binst, 'meego':mver, 'kernel':kver, 'rpm': rver}
+ bslist.append(bsinfo)
+
+ return bslist
+
+ def status(self, name):
+ self.rootdir = name
+ if os.path.exists(self.rootdir + '/.metadata'):
+ query_package = query_package_metadat
+ else:
+ query_package = query_package_rpmdb
+
+ name, mver = query_package(self.rootdir, 'name', 'meego-release')
+ msger.debug("MeeGo Release: %s" % mver)
+
+ name, kver = query_package(self.rootdir, 'name', 'kernel')
+ msger.debug("Kernel Version: %s" % kver)
+
+ name, rver = query_package(self.rootdir, 'name', 'rpm')
+ msger.debug("RPM Version: %s" % rver)
+
+ return (mver, kver, rver)
+
+ def create(self, name, repolist, **kwargs):
+ self.name = name
+ self.pkgmgr = 'zypp'
+ self.arch = 'i686'
+ self.rootdir = name
+ self.cachedir = '/var/tmp/mic/cache' # TBD from conf, do NOT hardcode
+
+ if 'arch' in kwargs:
+ self.arch = kwargs['arch']
+ if 'cachedir' in kwargs:
+ self.cachedir = kwargs['cachedir']
+
+ if os.path.exists(self._rootdir):
+ metadata_fp = os.path.join(self._rootdir, '.metadata')
+ if os.path.exists(metadata_fp) and \
+ 0 != os.path.getsize(metadata_fp):
+ msger.warning("bootstrap already exists") # TBD more details
+ return
+ else:
+ shutil.rmtree(self._rootdir)
+
+ if not os.path.exists(self._rootdir):
+ os.makedirs(self._rootdir)
+
+ pkg_manager = self.pkgmgr(self.arch, self.rootdir, self.cachedir)
+ pkg_manager.setup()
+
+ for repo in repolist:
+ if 'proxy' in repo:
+ pkg_manager.addRepository(repo['name'], repo['baseurl'],
+ proxy=repo['proxy'])
+ else:
+ pkg_manager.addRepository(repo['name'], repo['baseurl'])
+
+ rpm.addMacro("_dbpath", "/var/lib/rpm")
+ rpm.addMacro("__file_context_path", "%{nil}")
+
+ for grp in minibase_grps:
+ pkg_manager.selectGroup(grp)
+ for pkg in minibase_pkgs:
+ pkg_manager.selectPackage(pkg)
+ for pkg in required_pkgs:
+ pkg_manager.selectPackage(pkg)
+
+ try:
+ pkg_manager.runInstall(512 * 1024L * 1024L)
+ except:
+ raise errors.BootstrapError("Create bootstrap fail")
+ else:
+ metadata = pkg_manager.getAllContent()
+ metadata_fp = os.path.join(self.rootdir, '.metadata')
+ with open(metadata_fp, 'w') as f:
+ pickle.dump(metadata, f)
+ f.close()
+ finally:
+ pkg_manager.closeRpmDB()
+ pkg_manager.close()
+
+ # Copy bootstrap repo files
+ srcdir = "%s/etc/zypp/repos.d/" % self.cachedir
+ destdir= "%s/etc/zypp/repos.d/" % os.path.abspath(self.rootdir)
+ shutil.rmtree(destdir, ignore_errors = True)
+ shutil.copytree(srcdir, destdir)
+ # create '/tmp' in chroot
+ _tmpdir = os.path.join(os.path.abspath(self.rootdir), "tmp")
+ if not os.path.exists(_tmpdir):
+ os.makedirs(_tmpdir)
+
+ msger.info("Bootstrap created.")
+
+ def rebuild(self):
+ pass
+
+ def update(self, name):
+ self.rootdir = name
+ chrootdir = self.rootdir
+
+ def mychroot():
+ os.chroot(chrootdir)
+
+ shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
+ try:
+ subprocess.call("zypper -n --no-gpg-checks update",
+ preexec_fn=mychroot, shell=True)
+ except OSError, err:
+ raise errors.BootstrapError("Bootstrap: %s update failed" %\
+ chrootdir)
+
+ def cleanup(self, name):
+ self.rootdir = name
+ try:
+ chroot.cleanup_mounts(self.rootdir)
+ shutil.rmtree(self.rootdir, ignore_errors=True)
+ except:
+ raise errors.BootstrapError("Bootstrap: %s clean up failed" %\
+ self.rootdir)
diff --git a/mic/chroot.py b/mic/chroot.py
new file mode 100644
index 0000000..0e7be5d
--- /dev/null
+++ b/mic/chroot.py
@@ -0,0 +1,340 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+import os
+import shutil
+import subprocess
+
+from mic import msger
+from mic.conf import configmgr
+from mic.utils import misc, errors, runner, fs_related
+
+chroot_lockfd = -1
+chroot_lock = ""
+BIND_MOUNTS = (
+ "/proc",
+ "/proc/sys/fs/binfmt_misc",
+ "/sys",
+ "/dev",
+ "/dev/pts",
+ "/dev/shm",
+ "/var/lib/dbus",
+ "/var/run/dbus",
+ "/var/lock",
+ )
+
+def cleanup_after_chroot(targettype,imgmount,tmpdir,tmpmnt):
+ if imgmount and targettype == "img":
+ imgmount.cleanup()
+
+ if tmpdir:
+ shutil.rmtree(tmpdir, ignore_errors = True)
+
+ if tmpmnt:
+ shutil.rmtree(tmpmnt, ignore_errors = True)
+
+def check_bind_mounts(chrootdir, bindmounts):
+ chrootmounts = []
+ for mount in bindmounts.split(";"):
+ if not mount:
+ continue
+
+ srcdst = mount.split(":")
+ if len(srcdst) == 1:
+ srcdst.append("none")
+
+ if not os.path.isdir(srcdst[0]):
+ return False
+
+ if srcdst[1] == "" or srcdst[1] == "none":
+ srcdst[1] = None
+
+ if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
+ continue
+
+ if chrootdir:
+ if not srcdst[1]:
+ srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[0]))
+ else:
+ srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
+
+ tmpdir = chrootdir + "/" + srcdst[1]
+ if os.path.isdir(tmpdir):
+ msger.warning("Warning: dir %s has existed." % tmpdir)
+
+ return True
+
+def cleanup_mounts(chrootdir):
+ umountcmd = misc.find_binary_path("umount")
+ for point in BIND_MOUNTS:
+ args = [ umountcmd, "-l", chrootdir + point ]
+ runner.quiet(args)
+ point = '/parentroot'
+ args = [ umountcmd, "-l", chrootdir + point ]
+ runner.quiet(args)
+
+ abs_chrootdir = os.path.abspath(chrootdir)
+ with open('/proc/mounts') as f:
+ for line in f:
+ if abs_chrootdir in line:
+ point = line.split()[1]
+
+ if abs_chrootdir == point:
+ continue
+
+ args = [ umountcmd, "-l", point ]
+ ret = runner.quiet(args)
+ if ret != 0:
+ msger.warning("failed to unmount %s" % point)
+ return ret
+
+ return 0
+
+def setup_chrootenv(chrootdir, bindmounts = None):
+ global chroot_lockfd, chroot_lock
+
+ def get_bind_mounts(chrootdir, bindmounts):
+ chrootmounts = []
+ if bindmounts in ("", None):
+ bindmounts = ""
+
+ for mount in bindmounts.split(";"):
+ if not mount:
+ continue
+
+ srcdst = mount.split(":")
+ srcdst[0] = os.path.abspath(os.path.expanduser(srcdst[0]))
+ if len(srcdst) == 1:
+ srcdst.append("none")
+
+ if not os.path.isdir(srcdst[0]):
+ continue
+
+ if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
+ msger.warning("%s will be mounted by default." % srcdst[0])
+ continue
+
+ if srcdst[1] == "" or srcdst[1] == "none":
+ srcdst[1] = None
+ else:
+ srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
+ if os.path.isdir(chrootdir + "/" + srcdst[1]):
+ msger.warning("%s has existed in %s , skip it."\
+ % (srcdst[1], chrootdir))
+ continue
+
+ chrootmounts.append(fs_related.BindChrootMount(srcdst[0],
+ chrootdir,
+ srcdst[1]))
+
+ """Default bind mounts"""
+ for pt in BIND_MOUNTS:
+ chrootmounts.append(fs_related.BindChrootMount(pt,
+ chrootdir,
+ None))
+
+ chrootmounts.append(fs_related.BindChrootMount("/",
+ chrootdir,
+ "/parentroot",
+ "ro"))
+
+ for kernel in os.listdir("/lib/modules"):
+ chrootmounts.append(fs_related.BindChrootMount(
+ "/lib/modules/"+kernel,
+ chrootdir,
+ None,
+ "ro"))
+
+ return chrootmounts
+
+ def bind_mount(chrootmounts):
+ for b in chrootmounts:
+ msger.info("bind_mount: %s -> %s" % (b.src, b.dest))
+ b.mount()
+
+ def setup_resolv(chrootdir):
+ try:
+ shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
+ except:
+ pass
+
+ globalmounts = get_bind_mounts(chrootdir, bindmounts)
+ bind_mount(globalmounts)
+
+ setup_resolv(chrootdir)
+
+ mtab = "/etc/mtab"
+ dstmtab = chrootdir + mtab
+ if not os.path.islink(dstmtab):
+ shutil.copyfile(mtab, dstmtab)
+
+ chroot_lock = os.path.join(chrootdir, ".chroot.lock")
+ chroot_lockfd = open(chroot_lock, "w")
+
+ return globalmounts
+
+def cleanup_chrootenv(chrootdir, bindmounts = None, globalmounts = []):
+ global chroot_lockfd, chroot_lock
+
+ def bind_unmount(chrootmounts):
+ chrootmounts.reverse()
+ for b in chrootmounts:
+ msger.info("bind_unmount: %s -> %s" % (b.src, b.dest))
+ b.unmount()
+
+ def cleanup_resolv(chrootdir):
+ try:
+ fd = open(chrootdir + "/etc/resolv.conf", "w")
+ fd.truncate(0)
+ fd.close()
+ except:
+ pass
+
+ def kill_processes(chrootdir):
+ import glob
+ for fp in glob.glob("/proc/*/root"):
+ try:
+ if os.readlink(fp) == chrootdir:
+ pid = int(fp.split("/")[2])
+ os.kill(pid, 9)
+ except:
+ pass
+
+ def cleanup_mountdir(chrootdir, bindmounts):
+ if bindmounts == "" or bindmounts == None:
+ return
+ chrootmounts = []
+ for mount in bindmounts.split(";"):
+ if not mount:
+ continue
+
+ srcdst = mount.split(":")
+
+ if len(srcdst) == 1:
+ srcdst.append("none")
+
+ if srcdst[1] == "" or srcdst[1] == "none":
+ srcdst[1] = srcdst[0]
+
+ srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
+ tmpdir = chrootdir + "/" + srcdst[1]
+ if os.path.isdir(tmpdir):
+ if len(os.listdir(tmpdir)) == 0:
+ shutil.rmtree(tmpdir, ignore_errors = True)
+ else:
+ msger.warning("Warning: dir %s isn't empty." % tmpdir)
+
+ chroot_lockfd.close()
+ bind_unmount(globalmounts)
+
+ if not fs_related.my_fuser(chroot_lock):
+ tmpdir = chrootdir + "/parentroot"
+ if len(os.listdir(tmpdir)) == 0:
+ shutil.rmtree(tmpdir, ignore_errors = True)
+
+ cleanup_resolv(chrootdir)
+
+ if os.path.exists(chrootdir + "/etc/mtab"):
+ os.unlink(chrootdir + "/etc/mtab")
+
+ kill_processes(chrootdir)
+
+ cleanup_mountdir(chrootdir, bindmounts)
+
+def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
+ def mychroot():
+ os.chroot(chrootdir)
+ os.chdir("/")
+
+ if configmgr.chroot['saveto']:
+ savefs = True
+ saveto = configmgr.chroot['saveto']
+ wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
+ if saveto == chrootdir:
+ savefs = False
+ wrnmsg = "Dir %s is being used to chroot" % saveto
+ elif os.path.exists(saveto):
+ if msger.ask("Dir %s already exists, cleanup and continue?" %
+ saveto):
+ shutil.rmtree(saveto, ignore_errors = True)
+ savefs = True
+ else:
+ savefs = False
+
+ if savefs:
+ msger.info("Saving image to directory %s" % saveto)
+ fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
+ runner.quiet("cp -af %s %s" % (chrootdir, saveto))
+ devs = ['dev/fd',
+ 'dev/stdin',
+ 'dev/stdout',
+ 'dev/stderr',
+ 'etc/mtab']
+ ignlst = [os.path.join(saveto, x) for x in devs]
+ map(os.unlink, filter(os.path.exists, ignlst))
+ else:
+ msger.warning(wrnmsg)
+
+ dev_null = os.open("/dev/null", os.O_WRONLY)
+ files_to_check = ["/bin/bash", "/sbin/init"]
+
+ architecture_found = False
+
+ """ Register statically-linked qemu-arm if it is an ARM fs """
+ qemu_emulator = None
+
+ for ftc in files_to_check:
+ ftc = "%s/%s" % (chrootdir,ftc)
+
+ # Return code of 'file' is "almost always" 0 based on some man pages
+ # so we need to check the file existance first.
+ if not os.path.exists(ftc):
+ continue
+
+ for line in runner.outs(['file', ftc]).splitlines():
+ if 'ARM' in line:
+ qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
+ architecture_found = True
+ break
+
+ if 'Intel' in line:
+ architecture_found = True
+ break
+
+ if architecture_found:
+ break
+
+ os.close(dev_null)
+ if not architecture_found:
+ raise errors.CreatorError("Failed to get architecture from any of the "
+ "following files %s from chroot." \
+ % files_to_check)
+
+ try:
+ msger.info("Launching shell. Exit to continue.\n"
+ "----------------------------------")
+ globalmounts = setup_chrootenv(chrootdir, bindmounts)
+ subprocess.call(execute, preexec_fn = mychroot, shell=True)
+
+ except OSError, err:
+ raise errors.CreatorError("chroot err: %s" % str(err))
+
+ finally:
+ cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
+ if qemu_emulator:
+ os.unlink(chrootdir + qemu_emulator)
diff --git a/mic/conf.py b/mic/conf.py
new file mode 100644
index 0000000..ec2da56
--- /dev/null
+++ b/mic/conf.py
@@ -0,0 +1,233 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys, re
+import ConfigParser
+
+import msger
+import kickstart
+from .utils import misc, runner, proxy, errors
+
+DEFAULT_GSITECONF = '/etc/mic/mic.conf'
+
+def get_siteconf():
+ mic_path = os.path.dirname(__file__)
+
+ m = re.match(r"(?P<prefix>.*)\/lib(64)?\/.*", mic_path)
+ if m and m.group('prefix') != "/usr":
+ return os.path.join(m.group('prefix'), "etc/mic/mic.conf")
+
+ return DEFAULT_GSITECONF
+
+class ConfigMgr(object):
+ DEFAULTS = {'common': {
+ "distro_name": "Default Distribution",
+ "plugin_dir": "/usr/lib/mic/plugins", # TODO use prefix also?
+ },
+ 'create': {
+ "tmpdir": '/var/tmp/mic',
+ "cachedir": '/var/tmp/mic/cache',
+ "outdir": './mic-output',
+ "bootstrapdir": '/var/tmp/mic/bootstrap',
+
+ "arch": None, # None means auto-detect
+ "pkgmgr": "yum",
+ "name": "output",
+ "ksfile": None,
+ "ks": None,
+ "repomd": None,
+ "local_pkgs_path": None,
+ "release": None,
+ "logfile": None,
+ "record_pkgs": [],
+ "rpmver": None,
+ "pack_to": None,
+ "name_prefix": None,
+ "proxy": None,
+ "no_proxy": None,
+ "copy_kernel": False,
+
+ "runtime": None,
+ },
+ 'chroot': {
+ "saveto": None,
+ },
+ 'convert': {
+ "shell": False,
+ },
+ 'bootstraps': {},
+ }
+
+ # make the manager class as singleton
+ _instance = None
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = super(ConfigMgr, cls).__new__(cls, *args, **kwargs)
+
+ return cls._instance
+
+ def __init__(self, ksconf=None, siteconf=None):
+ # reset config options
+ self.reset()
+
+ if not siteconf:
+ siteconf = get_siteconf()
+
+ # initial options from siteconf
+ self._siteconf = siteconf
+
+ if ksconf:
+ self._ksconf = ksconf
+
+ def reset(self):
+ self.__ksconf = None
+ self.__siteconf = None
+
+ # initialize the values with defaults
+ for sec, vals in self.DEFAULTS.iteritems():
+ setattr(self, sec, vals)
+
+ def __set_siteconf(self, siteconf):
+ try:
+ self.__siteconf = siteconf
+ self._parse_siteconf(siteconf)
+ except ConfigParser.Error, error:
+ raise errors.ConfigError("%s" % error)
+ def __get_siteconf(self):
+ return self.__siteconf
+ _siteconf = property(__get_siteconf, __set_siteconf)
+
+ def __set_ksconf(self, ksconf):
+ if not os.path.isfile(ksconf):
+ msger.error('Cannot find ks file: %s' % ksconf)
+
+ self.__ksconf = ksconf
+ self._parse_kickstart(ksconf)
+ def __get_ksconf(self):
+ return self.__ksconf
+ _ksconf = property(__get_ksconf, __set_ksconf)
+
+ def _parse_siteconf(self, siteconf):
+ if not siteconf:
+ return
+
+ if not os.path.exists(siteconf):
+ msger.warning("cannot read config file: %s" % siteconf)
+ return
+
+ parser = ConfigParser.SafeConfigParser()
+ parser.read(siteconf)
+
+ for section in parser.sections():
+ if section in self.DEFAULTS:
+ getattr(self, section).update(dict(parser.items(section)))
+
+ # append common section items to other sections
+ for section in self.DEFAULTS.keys():
+ if section != "common" and not section.startswith('bootstrap'):
+ getattr(self, section).update(self.common)
+
+ # check and normalize the scheme of proxy url
+ if self.create['proxy']:
+ m = re.match('^(\w+)://.*', self.create['proxy'])
+ if m:
+ scheme = m.group(1)
+ if scheme not in ('http', 'https', 'ftp', 'socks'):
+ msger.error("%s: proxy scheme is incorrect" % siteconf)
+ else:
+ msger.warning("%s: proxy url w/o scheme, use http as default"
+ % siteconf)
+ self.create['proxy'] = "http://" + self.create['proxy']
+
+ proxy.set_proxies(self.create['proxy'], self.create['no_proxy'])
+
+ for section in parser.sections():
+ if section.startswith('bootstrap'):
+ name = section
+ repostr = {}
+ for option in parser.options(section):
+ if option == 'name':
+ name = parser.get(section, 'name')
+ continue
+
+ val = parser.get(section, option)
+ if '_' in option:
+ (reponame, repoopt) = option.split('_')
+ if repostr.has_key(reponame):
+ repostr[reponame] += "%s:%s," % (repoopt, val)
+ else:
+ repostr[reponame] = "%s:%s," % (repoopt, val)
+ continue
+
+ if val.split(':')[0] in ('file', 'http', 'https', 'ftp'):
+ if repostr.has_key(option):
+ repostr[option] += "name:%s,baseurl:%s," % \
+ (option, val)
+ else:
+ repostr[option] = "name:%s,baseurl:%s," % \
+ (option, val)
+ continue
+
+ self.bootstraps[name] = repostr
+
+ def _parse_kickstart(self, ksconf=None):
+ if not ksconf:
+ return
+
+ ks = kickstart.read_kickstart(ksconf)
+
+ self.create['ks'] = ks
+ self.create['name'] = os.path.splitext(os.path.basename(ksconf))[0]
+
+ if self.create['name_prefix']:
+ self.create['name'] = "%s-%s" % (self.create['name_prefix'],
+ self.create['name'])
+
+ msger.info("Retrieving repo metadata:")
+ ksrepos = misc.get_repostrs_from_ks(ks)
+ if not ksrepos:
+ raise errors.KsError('no valid repos found in ks file')
+
+ self.create['repomd'] = misc.get_metadata_from_repos(
+ ksrepos,
+ self.create['cachedir'])
+ msger.raw(" DONE")
+
+ self.create['rpmver'] = misc.get_rpmver_in_repo(self.create['repomd'])
+
+ target_archlist, archlist = misc.get_arch(self.create['repomd'])
+ if self.create['arch']:
+ if self.create['arch'] not in archlist:
+ raise errors.ConfigError("Invalid arch %s for repository. "
+ "Valid arches: %s" \
+ % (self.create['arch'], ', '.join(archlist)))
+ else:
+ if len(target_archlist) == 1:
+ self.create['arch'] = str(target_archlist[0])
+ msger.info("\nUse detected arch %s." % target_archlist[0])
+ else:
+ raise errors.ConfigError("Please specify a valid arch, "
+ "the choice can be: %s" \
+ % ', '.join(archlist))
+
+ kickstart.resolve_groups(self.create, self.create['repomd'])
+
+ # check selinux, it will block arm and btrfs image creation
+ misc.selinux_check(self.create['arch'],
+ [p.fstype for p in ks.handler.partition.partitions])
+
+configmgr = ConfigMgr()
diff --git a/mic/creator.py b/mic/creator.py
new file mode 100644
index 0000000..d88d7bd
--- /dev/null
+++ b/mic/creator.py
@@ -0,0 +1,305 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys, re
+from optparse import SUPPRESS_HELP
+
+from mic import msger
+from mic.utils import cmdln, errors, rpmmisc
+from conf import configmgr
+from plugin import pluginmgr
+
+class Creator(cmdln.Cmdln):
+ """${name}: create an image
+
+ Usage:
+ ${name} SUBCOMMAND <ksfile> [OPTS]
+
+ ${command_list}
+ ${option_list}
+ """
+
+ name = 'mic create(cr)'
+
+ def __init__(self, *args, **kwargs):
+ cmdln.Cmdln.__init__(self, *args, **kwargs)
+ self._subcmds = []
+
+ # get cmds from pluginmgr
+ # mix-in do_subcmd interface
+ for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():
+ if not hasattr(klass, 'do_create'):
+ msger.warning("Unsurpport subcmd: %s" % subcmd)
+ continue
+
+ func = getattr(klass, 'do_create')
+ setattr(self.__class__, "do_"+subcmd, func)
+ self._subcmds.append(subcmd)
+
+ def get_optparser(self):
+ optparser = cmdln.CmdlnOptionParser(self)
+ optparser.add_option('-d', '--debug', action='store_true',
+ dest='debug',
+ help=SUPPRESS_HELP)
+ optparser.add_option('-v', '--verbose', action='store_true',
+ dest='verbose',
+ help=SUPPRESS_HELP)
+ optparser.add_option('', '--logfile', type='string', dest='logfile',
+ default=None,
+ help='Path of logfile')
+ optparser.add_option('-c', '--config', type='string', dest='config',
+ default=None,
+ help='Specify config file for mic')
+ optparser.add_option('-k', '--cachedir', type='string', action='store',
+ dest='cachedir', default=None,
+ help='Cache directory to store the downloaded')
+ optparser.add_option('-o', '--outdir', type='string', action='store',
+ dest='outdir', default=None,
+ help='Output directory')
+ optparser.add_option('-A', '--arch', type='string', dest='arch',
+ default=None,
+ help='Specify repo architecture')
+ optparser.add_option('', '--release', type='string', dest='release',
+ default=None, metavar='RID',
+ help='Generate a release of RID with all necessary'
+ ' files, when @BUILD_ID@ is contained in '
+ 'kickstart file, it will be replaced by RID')
+ optparser.add_option("", "--record-pkgs", type="string",
+ dest="record_pkgs", default=None,
+ help='Record the info of installed packages, '
+ 'multiple values can be specified which '
+ 'joined by ",", valid values: "name", '
+ '"content", "license"')
+ optparser.add_option('', '--pkgmgr', type='string', dest='pkgmgr',
+ default=None,
+ help='Specify backend package manager')
+ optparser.add_option('', '--local-pkgs-path', type='string',
+ dest='local_pkgs_path', default=None,
+ help='Path for local pkgs(rpms) to be installed')
+ optparser.add_option('', '--runtime', type='string',
+ dest='runtime', default=None,
+ #help='Specify runtime mode, avaiable: bootstrap')
+ help=SUPPRESS_HELP)
+ # --taring-to is alias to --pack-to
+ optparser.add_option('', '--taring-to', type='string',
+ dest='pack_to', default=None,
+ help=SUPPRESS_HELP)
+ optparser.add_option('', '--pack-to', type='string',
+ dest='pack_to', default=None,
+ help='Pack the images together into the specified'
+ ' achive, extension supported: .zip, .tar, '
+ '.tar.gz, .tar.bz2, etc. by default, .tar '
+ 'will be used')
+ optparser.add_option('', '--copy-kernel', action='store_true',
+ dest='copy_kernel',
+ help='Copy kernel files from image /boot directory'
+ ' to the image output directory.')
+ return optparser
+
+ def preoptparse(self, argv):
+ optparser = self.get_optparser()
+
+ largs = []
+ rargs = []
+ while argv:
+ arg = argv.pop(0)
+
+ if arg in ('-h', '--help'):
+ rargs.append(arg)
+
+ elif optparser.has_option(arg):
+ largs.append(arg)
+
+ if optparser.get_option(arg).takes_value():
+ try:
+ largs.append(argv.pop(0))
+ except IndexError:
+ raise errors.Usage("option %s requires arguments" % arg)
+
+ else:
+ if arg.startswith("--"):
+ if "=" in arg:
+ opt = arg.split("=")[0]
+ else:
+ opt = None
+ elif arg.startswith("-") and len(arg) > 2:
+ opt = arg[0:2]
+ else:
+ opt = None
+
+ if opt and optparser.has_option(opt):
+ largs.append(arg)
+ else:
+ rargs.append(arg)
+
+ return largs + rargs
+
+ def postoptparse(self):
+ abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))
+
+ if self.options.verbose:
+ msger.set_loglevel('verbose')
+ if self.options.debug:
+ msger.set_loglevel('debug')
+
+ if self.options.logfile:
+ msger.set_interactive(False)
+ msger.set_logfile(self.options.logfile)
+ configmgr.create['logfile'] = self.options.logfile
+
+ if self.options.config:
+ configmgr.reset()
+ configmgr._siteconf = self.options.config
+
+ if self.options.outdir is not None:
+ configmgr.create['outdir'] = abspath(self.options.outdir)
+ if self.options.cachedir is not None:
+ configmgr.create['cachedir'] = abspath(self.options.cachedir)
+ os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']
+
+ if self.options.local_pkgs_path is not None:
+ if not os.path.exists(self.options.local_pkgs_path):
+ msger.error('Local pkgs directory: \'%s\' not exist' \
+ % self.options.local_pkgs_path)
+ configmgr.create['local_pkgs_path'] = self.options.local_pkgs_path
+
+ if self.options.release:
+ configmgr.create['release'] = self.options.release
+
+ if self.options.record_pkgs:
+ configmgr.create['record_pkgs'] = []
+ for infotype in self.options.record_pkgs.split(','):
+ if infotype not in ('name', 'content', 'license'):
+ raise errors.Usage('Invalid pkg recording: %s, valid ones:'
+ ' "name", "content", "license"' \
+ % infotype)
+
+ configmgr.create['record_pkgs'].append(infotype)
+
+ if self.options.arch is not None:
+ supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True)
+ if self.options.arch in supported_arch:
+ configmgr.create['arch'] = self.options.arch
+ else:
+ raise errors.Usage('Invalid architecture: "%s".\n'
+ ' Supported architectures are: \n'
+ ' %s' % (self.options.arch,
+ ', '.join(supported_arch)))
+
+ if self.options.pkgmgr is not None:
+ configmgr.create['pkgmgr'] = self.options.pkgmgr
+
+ if self.options.runtime:
+ configmgr.create['runtime'] = self.options.runtime
+
+ if self.options.pack_to is not None:
+ configmgr.create['pack_to'] = self.options.pack_to
+
+ if self.options.copy_kernel:
+ configmgr.create['copy_kernel'] = self.options.copy_kernel
+
+ def main(self, argv=None):
+ if argv is None:
+ argv = sys.argv
+ else:
+ argv = argv[:] # don't modify caller's list
+
+ self.optparser = self.get_optparser()
+ if self.optparser:
+ try:
+ argv = self.preoptparse(argv)
+ self.options, args = self.optparser.parse_args(argv)
+
+ except cmdln.CmdlnUserError, ex:
+ msg = "%s: %s\nTry '%s help' for info.\n"\
+ % (self.name, ex, self.name)
+ msger.error(msg)
+
+ except cmdln.StopOptionProcessing, ex:
+ return 0
+ else:
+ # optparser=None means no process for opts
+ self.options, args = None, argv[1:]
+
+ if not args:
+ return self.emptyline()
+
+ self.postoptparse()
+
+ if os.geteuid() != 0 and args[0] != 'help':
+ msger.error('root permission is required to continue, abort')
+
+ return self.cmd(args)
+
+ def do_auto(self, subcmd, opts, *args):
+ """${cmd_name}: auto detect image type from magic header
+
+ Usage:
+ ${name} ${cmd_name} <ksfile>
+
+ ${cmd_option_list}
+ """
+ def parse_magic_line(re_str, pstr, ptype='mic'):
+ ptn = re.compile(re_str)
+ m = ptn.match(pstr)
+ if not m or not m.groups():
+ return None
+
+ inline_argv = m.group(1).strip()
+ if ptype == 'mic':
+ m2 = re.search('(?P<format>\w+)', inline_argv)
+ elif ptype == 'mic2':
+ m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',
+ inline_argv)
+ else:
+ return None
+
+ if m2:
+ cmdname = m2.group('format')
+ inline_argv = inline_argv.replace(m2.group(0), '')
+ return (cmdname, inline_argv)
+
+ return None
+
+ if not args:
+ self.do_help(['help', subcmd])
+ return None
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ if not os.path.exists(args[0]):
+ raise errors.CreatorError("Can't find the file: %s" % args[0])
+
+ with open(args[0], 'r') as rf:
+ first_line = rf.readline()
+
+ mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'
+ mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'
+
+ result = parse_magic_line(mic_re, first_line, 'mic') \
+ or parse_magic_line(mic2_re, first_line, 'mic2')
+ if not result:
+ raise errors.KsError("Invalid magic line in file: %s" % args[0])
+
+ if result[0] not in self._subcmds:
+ raise errors.KsError("Unsupport format '%s' in %s"
+ % (result[0], args[0]))
+
+ argv = ' '.join(result + args).split()
+ self.main(argv)
+
diff --git a/mic/imager/__init__.py b/mic/imager/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mic/imager/__init__.py
diff --git a/mic/imager/baseimager.py b/mic/imager/baseimager.py
new file mode 100644
index 0000000..6d4256d
--- /dev/null
+++ b/mic/imager/baseimager.py
@@ -0,0 +1,1242 @@
+
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2007 Red Hat Inc.
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+import os, sys
+import stat
+import tempfile
+import shutil
+import subprocess
+import re
+import tarfile
+import glob
+
+import rpm
+
+from mic import kickstart
+from mic import msger
+from mic.utils.errors import CreatorError, Abort
+from mic.utils import misc, rpmmisc, runner, fs_related as fs
+
+class BaseImageCreator(object):
+ """Installs a system to a chroot directory.
+
+ ImageCreator is the simplest creator class available; it will install and
+ configure a system image according to the supplied kickstart file.
+
+ e.g.
+
+ import mic.imgcreate as imgcreate
+ ks = imgcreate.read_kickstart("foo.ks")
+ imgcreate.ImageCreator(ks, "foo").create()
+
+ """
+
+ def __del__(self):
+ self.cleanup()
+
+ def __init__(self, createopts = None, pkgmgr = None):
+ """Initialize an ImageCreator instance.
+
+ ks -- a pykickstart.KickstartParser instance; this instance will be
+ used to drive the install by e.g. providing the list of packages
+ to be installed, the system configuration and %post scripts
+
+ name -- a name for the image; used for e.g. image filenames or
+ filesystem labels
+ """
+
+ self.pkgmgr = pkgmgr
+
+ self.__builddir = None
+ self.__bindmounts = []
+
+ self.ks = None
+ self.name = "target"
+ self.tmpdir = "/var/tmp/mic"
+ self.cachedir = "/var/tmp/mic/cache"
+ self.destdir = "."
+ self.target_arch = "noarch"
+ self._local_pkgs_path = None
+ self.pack_to = None
+
+ # If the kernel is save to the destdir when copy_kernel cmd is called.
+ self._need_copy_kernel = False
+
+ if createopts:
+ # Mapping table for variables that have different names.
+ optmap = {"pkgmgr" : "pkgmgr_name",
+ "outdir" : "destdir",
+ "arch" : "target_arch",
+ "local_pkgs_path" : "_local_pkgs_path",
+ "copy_kernel" : "_need_copy_kernel",
+ }
+
+ # update setting from createopts
+ for key in createopts.keys():
+ if key in optmap:
+ option = optmap[key]
+ else:
+ option = key
+ setattr(self, option, createopts[key])
+
+ self.destdir = os.path.abspath(os.path.expanduser(self.destdir))
+
+ if 'release' in createopts and createopts['release']:
+ self.name += '-' + createopts['release']
+
+ if self.pack_to:
+ if '@NAME@' in self.pack_to:
+ self.pack_to = self.pack_to.replace('@NAME@', self.name)
+ (tar, ext) = os.path.splitext(self.pack_to)
+ if ext in (".gz", ".bz2") and tar.endswith(".tar"):
+ ext = ".tar" + ext
+ if ext not in misc.pack_formats:
+ self.pack_to += ".tar"
+
+ self._dep_checks = ["ls", "bash", "cp", "echo", "modprobe", "passwd"]
+
+ # Output image file names
+ self.outimage = []
+
+ # A flag to generate checksum
+ self._genchecksum = False
+
+ self._alt_initrd_name = None
+
+ self._recording_pkgs = []
+
+ # available size in root fs, init to 0
+ self._root_fs_avail = 0
+
+ # Name of the disk image file that is created.
+ self._img_name = None
+
+ self.image_format = None
+
+ # Save qemu emulator file name in order to clean up it finally
+ self.qemu_emulator = None
+
+ # No ks provided when called by convertor, so skip the dependency check
+ if self.ks:
+ # If we have btrfs partition we need to check necessary tools
+ for part in self.ks.handler.partition.partitions:
+ if part.fstype and part.fstype == "btrfs":
+ self._dep_checks.append("mkfs.btrfs")
+ break
+
+ if self.target_arch and self.target_arch.startswith("arm"):
+ for dep in self._dep_checks:
+ if dep == "extlinux":
+ self._dep_checks.remove(dep)
+
+ if not os.path.exists("/usr/bin/qemu-arm") or \
+ not misc.is_statically_linked("/usr/bin/qemu-arm"):
+ self._dep_checks.append("qemu-arm-static")
+
+ if os.path.exists("/proc/sys/vm/vdso_enabled"):
+ vdso_fh = open("/proc/sys/vm/vdso_enabled","r")
+ vdso_value = vdso_fh.read().strip()
+ vdso_fh.close()
+ if (int)(vdso_value) == 1:
+ msger.warning("vdso is enabled on your host, which might "
+ "cause problems with arm emulations.\n"
+ "\tYou can disable vdso with following command before "
+ "starting image build:\n"
+ "\techo 0 | sudo tee /proc/sys/vm/vdso_enabled")
+
+ # make sure the specified tmpdir and cachedir exist
+ if not os.path.exists(self.tmpdir):
+ os.makedirs(self.tmpdir)
+ if not os.path.exists(self.cachedir):
+ os.makedirs(self.cachedir)
+
+
+ #
+ # Properties
+ #
+ def __get_instroot(self):
+ if self.__builddir is None:
+ raise CreatorError("_instroot is not valid before calling mount()")
+ return self.__builddir + "/install_root"
+ _instroot = property(__get_instroot)
+ """The location of the install root directory.
+
+ This is the directory into which the system is installed. Subclasses may
+ mount a filesystem image here or copy files to/from here.
+
+ Note, this directory does not exist before ImageCreator.mount() is called.
+
+ Note also, this is a read-only attribute.
+
+ """
+
+ def __get_outdir(self):
+ if self.__builddir is None:
+ raise CreatorError("_outdir is not valid before calling mount()")
+ return self.__builddir + "/out"
+ _outdir = property(__get_outdir)
+ """The staging location for the final image.
+
+ This is where subclasses should stage any files that are part of the final
+ image. ImageCreator.package() will copy any files found here into the
+ requested destination directory.
+
+ Note, this directory does not exist before ImageCreator.mount() is called.
+
+ Note also, this is a read-only attribute.
+
+ """
+
+
+ #
+ # Hooks for subclasses
+ #
+ def _mount_instroot(self, base_on = None):
+ """Mount or prepare the install root directory.
+
+ This is the hook where subclasses may prepare the install root by e.g.
+ mounting creating and loopback mounting a filesystem image to
+ _instroot.
+
+ There is no default implementation.
+
+ base_on -- this is the value passed to mount() and can be interpreted
+ as the subclass wishes; it might e.g. be the location of
+ a previously created ISO containing a system image.
+
+ """
+ pass
+
+ def _unmount_instroot(self):
+ """Undo anything performed in _mount_instroot().
+
+ This is the hook where subclasses must undo anything which was done
+ in _mount_instroot(). For example, if a filesystem image was mounted
+ onto _instroot, it should be unmounted here.
+
+ There is no default implementation.
+
+ """
+ pass
+
+ def _create_bootconfig(self):
+ """Configure the image so that it's bootable.
+
+ This is the hook where subclasses may prepare the image for booting by
+ e.g. creating an initramfs and bootloader configuration.
+
+ This hook is called while the install root is still mounted, after the
+ packages have been installed and the kickstart configuration has been
+ applied, but before the %post scripts have been executed.
+
+ There is no default implementation.
+
+ """
+ pass
+
+ def _stage_final_image(self):
+ """Stage the final system image in _outdir.
+
+ This is the hook where subclasses should place the image in _outdir
+ so that package() can copy it to the requested destination directory.
+
+ By default, this moves the install root into _outdir.
+
+ """
+ shutil.move(self._instroot, self._outdir + "/" + self.name)
+
+ def get_installed_packages(self):
+ return self._pkgs_content.keys()
+
+ def _save_recording_pkgs(self, destdir):
+ """Save the list or content of installed packages to file.
+ """
+ pkgs = self._pkgs_content.keys()
+ pkgs.sort() # inplace op
+
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+ if 'name' in self._recording_pkgs :
+ namefile = os.path.join(destdir, self.name + '.packages')
+ f = open(namefile, "w")
+ content = '\n'.join(pkgs)
+ f.write(content)
+ f.close()
+ self.outimage.append(namefile);
+
+ # if 'content', save more details
+ if 'content' in self._recording_pkgs :
+ contfile = os.path.join(destdir, self.name + '.files')
+ f = open(contfile, "w")
+
+ for pkg in pkgs:
+ content = pkg + '\n'
+
+ pkgcont = self._pkgs_content[pkg]
+ items = []
+ if pkgcont.has_key('dir'):
+ items = map(lambda x:x+'/', pkgcont['dir'])
+ if pkgcont.has_key('file'):
+ items.extend(pkgcont['file'])
+
+ if items:
+ content += ' '
+ content += '\n '.join(items)
+ content += '\n'
+
+ content += '\n'
+ f.write(content)
+ f.close()
+ self.outimage.append(contfile)
+
+ if 'license' in self._recording_pkgs:
+ licensefile = os.path.join(destdir, self.name + '.license')
+ f = open(licensefile, "w")
+
+ f.write('Summary:\n')
+ for license in reversed(sorted(self._pkgs_license, key=\
+ lambda license: len(self._pkgs_license[license]))):
+ f.write(" - %s: %s\n" \
+ % (license, len(self._pkgs_license[license])))
+
+ f.write('\nDetails:\n')
+ for license in reversed(sorted(self._pkgs_license, key=\
+ lambda license: len(self._pkgs_license[license]))):
+ f.write(" - %s:\n" % (license))
+ for pkg in sorted(self._pkgs_license[license]):
+ f.write(" - %s\n" % (pkg))
+ f.write('\n')
+
+ f.close()
+ self.outimage.append(licensefile);
+
+ def _get_required_packages(self):
+ """Return a list of required packages.
+
+ This is the hook where subclasses may specify a set of packages which
+ it requires to be installed.
+
+ This returns an empty list by default.
+
+ Note, subclasses should usually chain up to the base class
+ implementation of this hook.
+
+ """
+ return []
+
+ def _get_excluded_packages(self):
+ """Return a list of excluded packages.
+
+ This is the hook where subclasses may specify a set of packages which
+ it requires _not_ to be installed.
+
+ This returns an empty list by default.
+
+ Note, subclasses should usually chain up to the base class
+ implementation of this hook.
+
+ """
+ return []
+
+ def _get_local_packages(self):
+ """Return a list of rpm path to be local installed.
+
+ This is the hook where subclasses may specify a set of rpms which
+ it requires to be installed locally.
+
+ This returns an empty list by default.
+
+ Note, subclasses should usually chain up to the base class
+ implementation of this hook.
+
+ """
+ if self._local_pkgs_path:
+ if os.path.isdir(self._local_pkgs_path):
+ return glob.glob(
+ os.path.join(self._local_pkgs_path, '*.rpm'))
+ elif os.path.splitext(self._local_pkgs_path)[-1] == '.rpm':
+ return [self._local_pkgs_path]
+
+ return []
+
+ def _get_fstab(self):
+ """Return the desired contents of /etc/fstab.
+
+ This is the hook where subclasses may specify the contents of
+ /etc/fstab by returning a string containing the desired contents.
+
+ A sensible default implementation is provided.
+
+ """
+ s = "/dev/root / %s %s 0 0\n" \
+ % (self._fstype,
+ "defaults,noatime" if not self._fsopts else self._fsopts)
+ s += self._get_fstab_special()
+ return s
+
+ def _get_fstab_special(self):
+ s = "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
+ s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
+ s += "proc /proc proc defaults 0 0\n"
+ s += "sysfs /sys sysfs defaults 0 0\n"
+ return s
+
+ def _get_post_scripts_env(self, in_chroot):
+ """Return an environment dict for %post scripts.
+
+ This is the hook where subclasses may specify some environment
+ variables for %post scripts by return a dict containing the desired
+ environment.
+
+ By default, this returns an empty dict.
+
+ in_chroot -- whether this %post script is to be executed chroot()ed
+ into _instroot.
+
+ """
+ return {}
+
+ def __get_imgname(self):
+ return self.name
+ _name = property(__get_imgname)
+ """The name of the image file.
+
+ """
+
+ def _get_kernel_versions(self):
+ """Return a dict detailing the available kernel types/versions.
+
+ This is the hook where subclasses may override what kernel types and
+ versions should be available for e.g. creating the booloader
+ configuration.
+
+ A dict should be returned mapping the available kernel types to a list
+ of the available versions for those kernels.
+
+ The default implementation uses rpm to iterate over everything
+ providing 'kernel', finds /boot/vmlinuz-* and returns the version
+ obtained from the vmlinuz filename. (This can differ from the kernel
+ RPM's n-v-r in the case of e.g. xen)
+
+ """
+ def get_kernel_versions(instroot):
+ ret = {}
+ versions = set()
+ files = glob.glob(instroot + "/boot/vmlinuz-*")
+ for file in files:
+ version = os.path.basename(file)[8:]
+ if version is None:
+ continue
+ versions.add(version)
+ ret["kernel"] = list(versions)
+ return ret
+
+ def get_version(header):
+ version = None
+ for f in header['filenames']:
+ if f.startswith('/boot/vmlinuz-'):
+ version = f[14:]
+ return version
+
+ if self.ks is None:
+ return get_kernel_versions(self._instroot)
+
+ ts = rpm.TransactionSet(self._instroot)
+
+ ret = {}
+ for header in ts.dbMatch('provides', 'kernel'):
+ version = get_version(header)
+ if version is None:
+ continue
+
+ name = header['name']
+ if not name in ret:
+ ret[name] = [version]
+ elif not version in ret[name]:
+ ret[name].append(version)
+
+ return ret
+
+
+ #
+ # Helpers for subclasses
+ #
+ def _do_bindmounts(self):
+ """Mount various system directories onto _instroot.
+
+ This method is called by mount(), but may also be used by subclasses
+ in order to re-mount the bindmounts after modifying the underlying
+ filesystem.
+
+ """
+ for b in self.__bindmounts:
+ b.mount()
+
+ def _undo_bindmounts(self):
+ """Unmount the bind-mounted system directories from _instroot.
+
+ This method is usually only called by unmount(), but may also be used
+ by subclasses in order to gain access to the filesystem obscured by
+ the bindmounts - e.g. in order to create device nodes on the image
+ filesystem.
+
+ """
+ self.__bindmounts.reverse()
+ for b in self.__bindmounts:
+ b.unmount()
+
+ def _chroot(self):
+ """Chroot into the install root.
+
+ This method may be used by subclasses when executing programs inside
+ the install root e.g.
+
+ subprocess.call(["/bin/ls"], preexec_fn = self.chroot)
+
+ """
+ os.chroot(self._instroot)
+ os.chdir("/")
+
+ def _mkdtemp(self, prefix = "tmp-"):
+ """Create a temporary directory.
+
+ This method may be used by subclasses to create a temporary directory
+ for use in building the final image - e.g. a subclass might create
+ a temporary directory in order to bundle a set of files into a package.
+
+ The subclass may delete this directory if it wishes, but it will be
+ automatically deleted by cleanup().
+
+ The absolute path to the temporary directory is returned.
+
+ Note, this method should only be called after mount() has been called.
+
+ prefix -- a prefix which should be used when creating the directory;
+ defaults to "tmp-".
+
+ """
+ self.__ensure_builddir()
+ return tempfile.mkdtemp(dir = self.__builddir, prefix = prefix)
+
+ def _mkstemp(self, prefix = "tmp-"):
+ """Create a temporary file.
+
+ This method may be used by subclasses to create a temporary file
+ for use in building the final image - e.g. a subclass might need
+ a temporary location to unpack a compressed file.
+
+ The subclass may delete this file if it wishes, but it will be
+ automatically deleted by cleanup().
+
+ A tuple containing a file descriptor (returned from os.open() and the
+ absolute path to the temporary directory is returned.
+
+ Note, this method should only be called after mount() has been called.
+
+ prefix -- a prefix which should be used when creating the file;
+ defaults to "tmp-".
+
+ """
+ self.__ensure_builddir()
+ return tempfile.mkstemp(dir = self.__builddir, prefix = prefix)
+
+ def _mktemp(self, prefix = "tmp-"):
+ """Create a temporary file.
+
+ This method simply calls _mkstemp() and closes the returned file
+ descriptor.
+
+ The absolute path to the temporary file is returned.
+
+ Note, this method should only be called after mount() has been called.
+
+ prefix -- a prefix which should be used when creating the file;
+ defaults to "tmp-".
+
+ """
+
+ (f, path) = self._mkstemp(prefix)
+ os.close(f)
+ return path
+
+
+ #
+ # Actual implementation
+ #
+ def __ensure_builddir(self):
+ if not self.__builddir is None:
+ return
+
+ try:
+ self.__builddir = tempfile.mkdtemp(dir = self.tmpdir,
+ prefix = "imgcreate-")
+ except OSError, (err, msg):
+ raise CreatorError("Failed create build directory in %s: %s" %
+ (self.tmpdir, msg))
+
+ def get_cachedir(self, cachedir = None):
+ if self.cachedir:
+ return self.cachedir
+
+ self.__ensure_builddir()
+ if cachedir:
+ self.cachedir = cachedir
+ else:
+ self.cachedir = self.__builddir + "/mic-cache"
+ fs.makedirs(self.cachedir)
+ return self.cachedir
+
+ def __sanity_check(self):
+ """Ensure that the config we've been given is sane."""
+ if not (kickstart.get_packages(self.ks) or
+ kickstart.get_groups(self.ks)):
+ raise CreatorError("No packages or groups specified")
+
+ kickstart.convert_method_to_repo(self.ks)
+
+ if not kickstart.get_repos(self.ks):
+ raise CreatorError("No repositories specified")
+
+ def __write_fstab(self):
+ fstab = open(self._instroot + "/etc/fstab", "w")
+ fstab.write(self._get_fstab())
+ fstab.close()
+
+ def __create_minimal_dev(self):
+ """Create a minimal /dev so that we don't corrupt the host /dev"""
+ origumask = os.umask(0000)
+ devices = (('null', 1, 3, 0666),
+ ('urandom',1, 9, 0666),
+ ('random', 1, 8, 0666),
+ ('full', 1, 7, 0666),
+ ('ptmx', 5, 2, 0666),
+ ('tty', 5, 0, 0666),
+ ('zero', 1, 5, 0666))
+
+ links = (("/proc/self/fd", "/dev/fd"),
+ ("/proc/self/fd/0", "/dev/stdin"),
+ ("/proc/self/fd/1", "/dev/stdout"),
+ ("/proc/self/fd/2", "/dev/stderr"))
+
+ for (node, major, minor, perm) in devices:
+ if not os.path.exists(self._instroot + "/dev/" + node):
+ os.mknod(self._instroot + "/dev/" + node,
+ perm | stat.S_IFCHR,
+ os.makedev(major,minor))
+
+ for (src, dest) in links:
+ if not os.path.exists(self._instroot + dest):
+ os.symlink(src, self._instroot + dest)
+
+ os.umask(origumask)
+
+ def mount(self, base_on = None, cachedir = None):
+ """Setup the target filesystem in preparation for an install.
+
+ This function sets up the filesystem which the ImageCreator will
+ install into and configure. The ImageCreator class merely creates an
+ install root directory, bind mounts some system directories (e.g. /dev)
+ and writes out /etc/fstab. Other subclasses may also e.g. create a
+ sparse file, format it and loopback mount it to the install root.
+
+ base_on -- a previous install on which to base this install; defaults
+ to None, causing a new image to be created
+
+ cachedir -- a directory in which to store the Yum cache; defaults to
+ None, causing a new cache to be created; by setting this
+ to another directory, the same cache can be reused across
+ multiple installs.
+
+ """
+ self.__ensure_builddir()
+
+ # prevent popup dialog in Ubuntu(s)
+ misc.hide_loopdev_presentation()
+
+ fs.makedirs(self._instroot)
+ fs.makedirs(self._outdir)
+
+ self._mount_instroot(base_on)
+
+ for d in ("/dev/pts",
+ "/etc",
+ "/boot",
+ "/var/log",
+ "/sys",
+ "/proc",
+ "/usr/bin"):
+ fs.makedirs(self._instroot + d)
+
+ if self.target_arch and self.target_arch.startswith("arm"):
+ self.qemu_emulator = misc.setup_qemu_emulator(self._instroot,
+ self.target_arch)
+
+ self.get_cachedir(cachedir)
+
+ # bind mount system directories into _instroot
+ for (f, dest) in [("/sys", None),
+ ("/proc", None),
+ ("/proc/sys/fs/binfmt_misc", None),
+ ("/dev/pts", None)]:
+ self.__bindmounts.append(
+ fs.BindChrootMount(
+ f, self._instroot, dest))
+
+ self._do_bindmounts()
+
+ self.__create_minimal_dev()
+
+ if os.path.exists(self._instroot + "/etc/mtab"):
+ os.unlink(self._instroot + "/etc/mtab")
+ os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
+
+ self.__write_fstab()
+
+ # get size of available space in 'instroot' fs
+ self._root_fs_avail = misc.get_filesystem_avail(self._instroot)
+
+ def unmount(self):
+ """Unmounts the target filesystem.
+
+ The ImageCreator class detaches the system from the install root, but
+ other subclasses may also detach the loopback mounted filesystem image
+ from the install root.
+
+ """
+ try:
+ mtab = self._instroot + "/etc/mtab"
+ if not os.path.islink(mtab):
+ os.unlink(self._instroot + "/etc/mtab")
+
+ if self.qemu_emulator:
+ os.unlink(self._instroot + self.qemu_emulator)
+ except OSError:
+ pass
+
+ self._undo_bindmounts()
+
+ """ Clean up yum garbage """
+ try:
+ instroot_pdir = os.path.dirname(self._instroot + self._instroot)
+ if os.path.exists(instroot_pdir):
+ shutil.rmtree(instroot_pdir, ignore_errors = True)
+ yumlibdir = self._instroot + "/var/lib/yum"
+ if os.path.exists(yumlibdir):
+ shutil.rmtree(yumlibdir, ignore_errors = True)
+ except OSError:
+ pass
+
+ self._unmount_instroot()
+
+ # reset settings of popup dialog in Ubuntu(s)
+ misc.unhide_loopdev_presentation()
+
+ def cleanup(self):
+ """Unmounts the target filesystem and deletes temporary files.
+
+ This method calls unmount() and then deletes any temporary files and
+ directories that were created on the host system while building the
+ image.
+
+ Note, make sure to call this method once finished with the creator
+ instance in order to ensure no stale files are left on the host e.g.:
+
+ creator = ImageCreator(ks, name)
+ try:
+ creator.create()
+ finally:
+ creator.cleanup()
+
+ """
+ if not self.__builddir:
+ return
+
+ self.unmount()
+
+ shutil.rmtree(self.__builddir, ignore_errors = True)
+ self.__builddir = None
+
+ def __is_excluded_pkg(self, pkg):
+ if pkg in self._excluded_pkgs:
+ self._excluded_pkgs.remove(pkg)
+ return True
+
+ for xpkg in self._excluded_pkgs:
+ if xpkg.endswith('*'):
+ if pkg.startswith(xpkg[:-1]):
+ return True
+ elif xpkg.startswith('*'):
+ if pkg.endswith(xpkg[1:]):
+ return True
+
+ return None
+
+ def __select_packages(self, pkg_manager):
+ skipped_pkgs = []
+ for pkg in self._required_pkgs:
+ e = pkg_manager.selectPackage(pkg)
+ if e:
+ if kickstart.ignore_missing(self.ks):
+ skipped_pkgs.append(pkg)
+ elif self.__is_excluded_pkg(pkg):
+ skipped_pkgs.append(pkg)
+ else:
+ raise CreatorError("Failed to find package '%s' : %s" %
+ (pkg, e))
+
+ for pkg in skipped_pkgs:
+ msger.warning("Skipping missing package '%s'" % (pkg,))
+
+ def __select_groups(self, pkg_manager):
+ skipped_groups = []
+ for group in self._required_groups:
+ e = pkg_manager.selectGroup(group.name, group.include)
+ if e:
+ if kickstart.ignore_missing(self.ks):
+ skipped_groups.append(group)
+ else:
+ raise CreatorError("Failed to find group '%s' : %s" %
+ (group.name, e))
+
+ for group in skipped_groups:
+ msger.warning("Skipping missing group '%s'" % (group.name,))
+
+ def __deselect_packages(self, pkg_manager):
+ for pkg in self._excluded_pkgs:
+ pkg_manager.deselectPackage(pkg)
+
+ def __localinst_packages(self, pkg_manager):
+ for rpm_path in self._get_local_packages():
+ pkg_manager.installLocal(rpm_path)
+
+ def __preinstall_packages(self, pkg_manager):
+ if not self.ks:
+ return
+
+ self._preinstall_pkgs = kickstart.get_pre_packages(self.ks)
+ for pkg in self._preinstall_pkgs:
+ pkg_manager.preInstall(pkg)
+
+ def __attachment_packages(self, pkg_manager):
+ if not self.ks:
+ return
+
+ self._attachment = []
+ for item in kickstart.get_attachment(self.ks):
+ if item.startswith('/'):
+ fpaths = os.path.join(self._instroot, item.lstrip('/'))
+ for fpath in glob.glob(fpaths):
+ self._attachment.append(fpath)
+ continue
+
+ filelist = pkg_manager.getFilelist(item)
+ if filelist:
+ # found rpm in rootfs
+ for pfile in pkg_manager.getFilelist(item):
+ fpath = os.path.join(self._instroot, pfile.lstrip('/'))
+ self._attachment.append(fpath)
+ continue
+
+ # try to retrieve rpm file
+ (url, proxies) = pkg_manager.package_url(item)
+ if not url:
+ msger.warning("Can't get url from repo for %s" % item)
+ continue
+ fpath = os.path.join(self.cachedir, os.path.basename(url))
+ if not os.path.exists(fpath):
+ # download pkgs
+ try:
+ fpath = rpmmisc.myurlgrab(url, fpath, proxies, None)
+ except CreatorError:
+ raise
+
+ tmpdir = self._mkdtemp()
+ misc.extract_rpm(fpath, tmpdir)
+ for (root, dirs, files) in os.walk(tmpdir):
+ for fname in files:
+ fpath = os.path.join(root, fname)
+ self._attachment.append(fpath)
+
+ def install(self, repo_urls = {}):
+ """Install packages into the install root.
+
+ This function installs the packages listed in the supplied kickstart
+ into the install root. By default, the packages are installed from the
+ repository URLs specified in the kickstart.
+
+ repo_urls -- a dict which maps a repository name to a repository URL;
+ if supplied, this causes any repository URLs specified in
+ the kickstart to be overridden.
+
+ """
+
+ # initialize pkg list to install
+ if self.ks:
+ self.__sanity_check()
+
+ self._required_pkgs = \
+ kickstart.get_packages(self.ks, self._get_required_packages())
+ self._excluded_pkgs = \
+ kickstart.get_excluded(self.ks, self._get_excluded_packages())
+ self._required_groups = kickstart.get_groups(self.ks)
+ else:
+ self._required_pkgs = None
+ self._excluded_pkgs = None
+ self._required_groups = None
+
+ pkg_manager = self.get_pkg_manager()
+ pkg_manager.setup()
+
+ for repo in kickstart.get_repos(self.ks, repo_urls):
+ (name, baseurl, mirrorlist, inc, exc,
+ proxy, proxy_username, proxy_password, debuginfo,
+ source, gpgkey, disable, ssl_verify, cost, priority) = repo
+
+ yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy,
+ proxy_username, proxy_password, inc, exc, ssl_verify,
+ cost, priority)
+
+ if kickstart.exclude_docs(self.ks):
+ rpm.addMacro("_excludedocs", "1")
+ rpm.addMacro("_dbpath", "/var/lib/rpm")
+ rpm.addMacro("__file_context_path", "%{nil}")
+ if kickstart.inst_langs(self.ks) != None:
+ rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks))
+
+ try:
+ try:
+ self.__preinstall_packages(pkg_manager)
+ self.__select_packages(pkg_manager)
+ self.__select_groups(pkg_manager)
+ self.__deselect_packages(pkg_manager)
+ self.__localinst_packages(pkg_manager)
+
+ BOOT_SAFEGUARD = 256L * 1024 * 1024 # 256M
+ checksize = self._root_fs_avail
+ if checksize:
+ checksize -= BOOT_SAFEGUARD
+ if self.target_arch:
+ pkg_manager._add_prob_flags(rpm.RPMPROB_FILTER_IGNOREARCH)
+ pkg_manager.runInstall(checksize)
+ except CreatorError, e:
+ raise
+ finally:
+ self._pkgs_content = pkg_manager.getAllContent()
+ self._pkgs_license = pkg_manager.getPkgsLicense()
+ self.__attachment_packages(pkg_manager)
+
+ pkg_manager.close()
+
+ # hook post install
+ self.postinstall()
+
+ # do some clean up to avoid lvm info leakage. this sucks.
+ for subdir in ("cache", "backup", "archive"):
+ lvmdir = self._instroot + "/etc/lvm/" + subdir
+ try:
+ for f in os.listdir(lvmdir):
+ os.unlink(lvmdir + "/" + f)
+ except:
+ pass
+
+ def postinstall(self):
+ self.copy_attachment()
+
+ def __run_post_scripts(self):
+ msger.info("Running scripts ...")
+ if os.path.exists(self._instroot + "/tmp"):
+ shutil.rmtree(self._instroot + "/tmp")
+ os.mkdir (self._instroot + "/tmp", 0755)
+ for s in kickstart.get_post_scripts(self.ks):
+ (fd, path) = tempfile.mkstemp(prefix = "ks-script-",
+ dir = self._instroot + "/tmp")
+
+ s.script = s.script.replace("\r", "")
+ os.write(fd, s.script)
+ os.close(fd)
+ os.chmod(path, 0700)
+
+ env = self._get_post_scripts_env(s.inChroot)
+
+ if not s.inChroot:
+ env["INSTALL_ROOT"] = self._instroot
+ env["IMG_NAME"] = self._name
+ preexec = None
+ script = path
+ else:
+ preexec = self._chroot
+ script = "/tmp/" + os.path.basename(path)
+
+ try:
+ try:
+ subprocess.call([s.interp, script],
+ preexec_fn = preexec,
+ env = env,
+ stdout = sys.stdout,
+ stderr = sys.stderr)
+ except OSError, (err, msg):
+ raise CreatorError("Failed to execute %%post script "
+ "with '%s' : %s" % (s.interp, msg))
+ finally:
+ os.unlink(path)
+
+ def __save_repo_keys(self, repodata):
+ if not repodata:
+ return None
+
+ gpgkeydir = "/etc/pki/rpm-gpg"
+ fs.makedirs(self._instroot + gpgkeydir)
+ for repo in repodata:
+ if repo["repokey"]:
+ repokey = gpgkeydir + "/RPM-GPG-KEY-%s" % repo["name"]
+ shutil.copy(repo["repokey"], self._instroot + repokey)
+
+ def configure(self, repodata = None):
+ """Configure the system image according to the kickstart.
+
+ This method applies the (e.g. keyboard or network) configuration
+ specified in the kickstart and executes the kickstart %post scripts.
+
+ If necessary, it also prepares the image to be bootable by e.g.
+ creating an initrd and bootloader configuration.
+
+ """
+ ksh = self.ks.handler
+
+ msger.info('Applying configurations ...')
+ try:
+ kickstart.LanguageConfig(self._instroot).apply(ksh.lang)
+ kickstart.KeyboardConfig(self._instroot).apply(ksh.keyboard)
+ kickstart.TimezoneConfig(self._instroot).apply(ksh.timezone)
+ #kickstart.AuthConfig(self._instroot).apply(ksh.authconfig)
+ kickstart.FirewallConfig(self._instroot).apply(ksh.firewall)
+ kickstart.RootPasswordConfig(self._instroot).apply(ksh.rootpw)
+ kickstart.UserConfig(self._instroot).apply(ksh.user)
+ kickstart.ServicesConfig(self._instroot).apply(ksh.services)
+ kickstart.XConfig(self._instroot).apply(ksh.xconfig)
+ kickstart.NetworkConfig(self._instroot).apply(ksh.network)
+ kickstart.RPMMacroConfig(self._instroot).apply(self.ks)
+ kickstart.DesktopConfig(self._instroot).apply(ksh.desktop)
+ self.__save_repo_keys(repodata)
+ kickstart.MoblinRepoConfig(self._instroot).apply(ksh.repo, repodata)
+ except:
+ msger.warning("Failed to apply configuration to image")
+ raise
+
+ self._create_bootconfig()
+ self.__run_post_scripts()
+
+ def launch_shell(self, launch):
+ """Launch a shell in the install root.
+
+ This method is launches a bash shell chroot()ed in the install root;
+ this can be useful for debugging.
+
+ """
+ if launch:
+ msger.info("Launching shell. Exit to continue.")
+ subprocess.call(["/bin/bash"], preexec_fn = self._chroot)
+
+ def do_genchecksum(self, image_name):
+ if not self._genchecksum:
+ return
+
+ md5sum = misc.get_md5sum(image_name)
+ with open(image_name + ".md5sum", "w") as f:
+ f.write("%s %s" % (md5sum, os.path.basename(image_name)))
+ self.outimage.append(image_name+".md5sum")
+
+ def package(self, destdir = "."):
+ """Prepares the created image for final delivery.
+
+ In its simplest form, this method merely copies the install root to the
+ supplied destination directory; other subclasses may choose to package
+ the image by e.g. creating a bootable ISO containing the image and
+ bootloader configuration.
+
+ destdir -- the directory into which the final image should be moved;
+ this defaults to the current directory.
+
+ """
+ self._stage_final_image()
+
+ if not os.path.exists(destdir):
+ fs.makedirs(destdir)
+
+ if self._recording_pkgs:
+ self._save_recording_pkgs(destdir)
+
+ # For image formats with two or multiple image files, it will be
+ # better to put them under a directory
+ if self.image_format in ("raw", "vmdk", "vdi", "nand", "mrstnand"):
+ destdir = os.path.join(destdir, "%s-%s" \
+ % (self.name, self.image_format))
+ msger.debug("creating destination dir: %s" % destdir)
+ fs.makedirs(destdir)
+
+ # Ensure all data is flushed to _outdir
+ runner.quiet('sync')
+
+ misc.check_space_pre_cp(self._outdir, destdir)
+ for f in os.listdir(self._outdir):
+ shutil.move(os.path.join(self._outdir, f),
+ os.path.join(destdir, f))
+ self.outimage.append(os.path.join(destdir, f))
+ self.do_genchecksum(os.path.join(destdir, f))
+
+ def print_outimage_info(self):
+ msg = "The new image can be found here:\n"
+ self.outimage.sort()
+ for file in self.outimage:
+ msg += ' %s\n' % os.path.abspath(file)
+
+ msger.info(msg)
+
+ def check_depend_tools(self):
+ for tool in self._dep_checks:
+ fs.find_binary_path(tool)
+
+ def package_output(self, image_format, destdir = ".", package="none"):
+ if not package or package == "none":
+ return
+
+ destdir = os.path.abspath(os.path.expanduser(destdir))
+ (pkg, comp) = os.path.splitext(package)
+ if comp:
+ comp=comp.lstrip(".")
+
+ if pkg == "tar":
+ if comp:
+ dst = "%s/%s-%s.tar.%s" %\
+ (destdir, self.name, image_format, comp)
+ else:
+ dst = "%s/%s-%s.tar" %\
+ (destdir, self.name, image_format)
+
+ msger.info("creating %s" % dst)
+ tar = tarfile.open(dst, "w:" + comp)
+
+ for file in self.outimage:
+ msger.info("adding %s to %s" % (file, dst))
+ tar.add(file,
+ arcname=os.path.join("%s-%s" \
+ % (self.name, image_format),
+ os.path.basename(file)))
+ if os.path.isdir(file):
+ shutil.rmtree(file, ignore_errors = True)
+ else:
+ os.remove(file)
+
+ tar.close()
+
+ '''All the file in outimage has been packaged into tar.* file'''
+ self.outimage = [dst]
+
+ def release_output(self, config, destdir, release):
+ """ Create release directory and files
+ """
+
+ def _rpath(fn):
+ """ release path """
+ return os.path.join(destdir, fn)
+
+ outimages = self.outimage
+
+ # new ks
+ new_kspath = _rpath(self.name+'.ks')
+ with open(config) as fr:
+ with open(new_kspath, "w") as wf:
+ # When building a release we want to make sure the .ks
+ # file generates the same build even when --release not used.
+ wf.write(fr.read().replace("@BUILD_ID@", release))
+ outimages.append(new_kspath)
+
+ # save log file, logfile is only available in creator attrs
+ if hasattr(self, 'logfile') and not self.logfile:
+ log_path = _rpath(self.name + ".log")
+ # touch the log file, else outimages will filter it out
+ with open(log_path, 'w') as wf:
+ wf.write('')
+ msger.set_logfile(log_path)
+ outimages.append(_rpath(self.name + ".log"))
+
+ # rename iso and usbimg
+ for f in os.listdir(destdir):
+ if f.endswith(".iso"):
+ newf = f[:-4] + '.img'
+ elif f.endswith(".usbimg"):
+ newf = f[:-7] + '.img'
+ else:
+ continue
+ os.rename(_rpath(f), _rpath(newf))
+ outimages.append(_rpath(newf))
+
+ # generate MANIFEST
+ with open(_rpath("MANIFEST"), "w") as wf:
+ for f in os.listdir(destdir):
+ if f == "MANIFEST":
+ continue
+
+ if os.path.isdir(os.path.join(destdir, f)):
+ continue
+
+ md5sum = misc.get_md5sum(_rpath(f))
+ # There needs to be two spaces between the sum and
+ # filepath to match the syntax with md5sum.
+ # This way also md5sum -c MANIFEST can be used by users
+ wf.write("%s *%s\n" % (md5sum, f))
+
+ outimages.append("%s/MANIFEST" % destdir)
+
+ # Filter out the nonexist file
+ for fp in outimages[:]:
+ if not os.path.exists("%s" % fp):
+ outimages.remove(fp)
+
+ def copy_kernel(self):
+ """ Copy kernel files to the outimage directory.
+ NOTE: This needs to be called before unmounting the instroot.
+ """
+
+ if not self._need_copy_kernel:
+ return
+
+ if not os.path.exists(self.destdir):
+ os.makedirs(self.destdir)
+
+ for kernel in glob.glob("%s/boot/vmlinuz-*" % self._instroot):
+ kernelfilename = "%s/%s-%s" % (self.destdir,
+ self.name,
+ os.path.basename(kernel))
+ msger.info('copy kernel file %s as %s' % (os.path.basename(kernel),
+ kernelfilename))
+ shutil.copy(kernel, kernelfilename)
+ self.outimage.append(kernelfilename)
+
+ def copy_attachment(self):
+ """ Subclass implement it to handle attachment files
+ NOTE: This needs to be called before unmounting the instroot.
+ """
+ pass
+
+ def get_pkg_manager(self):
+ return self.pkgmgr(target_arch = self.target_arch,
+ instroot = self._instroot,
+ cachedir = self.cachedir)
diff --git a/mic/imager/fs.py b/mic/imager/fs.py
new file mode 100644
index 0000000..e9ef00e
--- /dev/null
+++ b/mic/imager/fs.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys
+
+from baseimager import BaseImageCreator
+from mic import msger
+from mic.utils import runner, misc
+from mic.utils.fs_related import *
+from subprocess import call
+
+class FsImageCreator(BaseImageCreator):
+ def __init__(self, cfgmgr = None, pkgmgr = None):
+ self.zips = {
+ "tar.bz2" : ""
+ }
+ BaseImageCreator.__init__(self, cfgmgr, pkgmgr)
+ self._fstype = None
+ self._fsopts = None
+ self._include_src = False
+
+ def package(self, destdir = "."):
+
+ ignores = ["/dev/fd",
+ "/dev/stdin",
+ "/dev/stdout",
+ "/dev/stderr",
+ "/etc/mtab"]
+
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+
+ if self._recording_pkgs:
+ self._save_recording_pkgs(destdir)
+
+ if not self.pack_to:
+ fsdir = os.path.join(destdir, self.name)
+
+ misc.check_space_pre_cp(self._instroot, destdir)
+ msger.info("Copying %s to %s ..." % (self._instroot, fsdir))
+ runner.show(['cp', "-af", self._instroot, fsdir])
+
+ for exclude in ignores:
+ if os.path.exists(fsdir + exclude):
+ os.unlink(fsdir + exclude)
+
+ self.outimage.append(fsdir)
+
+ else:
+ (tar, comp) = os.path.splitext(self.pack_to)
+ try:
+ tarcreat = {'.tar': '-cf',
+ '.gz': '-czf',
+ '.bz2': '-cjf',
+ '.tgz': '-czf',
+ '.tbz': '-cjf'}[comp]
+ except KeyError:
+ raise CreatorError("Unsupported comression for this image type:"
+ " '%s', try '.tar', '.tar.gz', etc" % comp)
+
+ dst = os.path.join(destdir, self.pack_to)
+ msger.info("Pack rootfs to %s. Please wait..." % dst)
+
+ tar = find_binary_path('tar')
+ tar_cmdline = [tar, "--numeric-owner",
+ "--preserve-permissions",
+ "--preserve-order",
+ "--one-file-system",
+ "--directory",
+ self._instroot]
+ for ignore_entry in ignores:
+ if ignore_entry.startswith('/'):
+ ignore_entry = ignore_entry[1:]
+
+ tar_cmdline.append("--exclude=%s" % (ignore_entry))
+
+ tar_cmdline.extend([tarcreat, dst, "."])
+
+ rc = runner.show(tar_cmdline)
+ if rc:
+ raise CreatorError("Failed compress image with tar.bz2. "
+ "Cmdline: %s" % (" ".join(tar_cmdline)))
+
+ self.outimage.append(dst)
+
diff --git a/mic/imager/livecd.py b/mic/imager/livecd.py
new file mode 100644
index 0000000..a9f4d09
--- /dev/null
+++ b/mic/imager/livecd.py
@@ -0,0 +1,725 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys
+import glob
+import shutil
+
+from mic import kickstart, msger
+from mic.utils import fs_related, rpmmisc, runner, misc
+from mic.utils.errors import CreatorError
+
+from loop import LoopImageCreator
+class LiveImageCreatorBase(LoopImageCreator):
+ """A base class for LiveCD image creators.
+
+ This class serves as a base class for the architecture-specific LiveCD
+ image creator subclass, LiveImageCreator.
+
+ LiveImageCreator creates a bootable ISO containing the system image,
+ bootloader, bootloader configuration, kernel and initramfs.
+ """
+
+ def __init__(self, creatoropts = None, pkgmgr = None):
+ """Initialise a LiveImageCreator instance.
+
+ This method takes the same arguments as ImageCreator.__init__().
+ """
+ LoopImageCreator.__init__(self, creatoropts, pkgmgr)
+
+ #Controls whether to use squashfs to compress the image.
+ self.skip_compression = False
+
+ #Controls whether an image minimizing snapshot should be created.
+ #
+ #This snapshot can be used when copying the system image from the ISO in
+ #order to minimize the amount of data that needs to be copied; simply,
+ #it makes it possible to create a version of the image's filesystem with
+ #no spare space.
+ self.skip_minimize = False
+
+ #A flag which indicates i act as a convertor default false
+ self.actasconvertor = False
+
+ #The bootloader timeout from kickstart.
+ if self.ks:
+ self._timeout = kickstart.get_timeout(self.ks, 10)
+ else:
+ self._timeout = 10
+
+ #The default kernel type from kickstart.
+ if self.ks:
+ self._default_kernel = kickstart.get_default_kernel(self.ks,
+ "kernel")
+ else:
+ self._default_kernel = None
+
+ if self.ks:
+ parts = kickstart.get_partitions(self.ks)
+ if len(parts) > 1:
+ raise CreatorError("Can't support multi partitions in ks file "
+ "for this image type")
+ # FIXME: rename rootfs img to self.name,
+ # else can't find files when create iso
+ self._instloops[0]['name'] = self.name + ".img"
+
+ self.__isodir = None
+
+ self.__modules = ["=ata",
+ "sym53c8xx",
+ "aic7xxx",
+ "=usb",
+ "=firewire",
+ "=mmc",
+ "=pcmcia",
+ "mptsas"]
+ if self.ks:
+ self.__modules.extend(kickstart.get_modules(self.ks))
+
+ self._dep_checks.extend(["isohybrid",
+ "unsquashfs",
+ "mksquashfs",
+ "dd",
+ "genisoimage"])
+
+ #
+ # Hooks for subclasses
+ #
+ def _configure_bootloader(self, isodir):
+ """Create the architecture specific booloader configuration.
+
+ This is the hook where subclasses must create the booloader
+ configuration in order to allow a bootable ISO to be built.
+
+ isodir -- the directory where the contents of the ISO are to
+ be staged
+ """
+ raise CreatorError("Bootloader configuration is arch-specific, "
+ "but not implemented for this arch!")
+ def _get_menu_options(self):
+ """Return a menu options string for syslinux configuration.
+ """
+ if self.ks is None:
+ return "liveinst autoinst"
+ r = kickstart.get_menu_args(self.ks)
+ return r
+
+ def _get_kernel_options(self):
+ """Return a kernel options string for bootloader configuration.
+
+ This is the hook where subclasses may specify a set of kernel
+ options which should be included in the images bootloader
+ configuration.
+
+ A sensible default implementation is provided.
+ """
+
+ if self.ks is None:
+ r = "ro liveimg"
+ else:
+ r = kickstart.get_kernel_args(self.ks)
+
+ if os.path.exists(self._instroot + "/usr/bin/rhgb") or \
+ os.path.exists(self._instroot + "/usr/bin/plymouth") and \
+ ' rhgb' not in r:
+ r += ' rhgb'
+
+ return r
+
+ def _get_mkisofs_options(self, isodir):
+ """Return the architecture specific mkisosfs options.
+
+ This is the hook where subclasses may specify additional arguments
+ to mkisofs, e.g. to enable a bootable ISO to be built.
+
+ By default, an empty list is returned.
+ """
+ return []
+
+ #
+ # Helpers for subclasses
+ #
+ def _has_checkisomd5(self):
+ """Check whether checkisomd5 is available in the install root."""
+ def _exists(path):
+ return os.path.exists(self._instroot + path)
+
+ if (_exists("/usr/lib/moblin-installer-runtime/checkisomd5") or \
+ _exists("/usr/bin/checkisomd5")):
+ if (os.path.exists("/usr/bin/implantisomd5") or
+ os.path.exists("/usr/lib/anaconda-runtime/implantisomd5")):
+ return True
+
+ return False
+
+ def _mount_instroot(self, base_on = None):
+ LoopImageCreator._mount_instroot(self, base_on)
+ self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd")
+
+ def _unmount_instroot(self):
+ try:
+ os.unlink(self._instroot + "/etc/sysconfig/mkinitrd")
+ except:
+ pass
+ LoopImageCreator._unmount_instroot(self)
+
+ def __ensure_isodir(self):
+ if self.__isodir is None:
+ self.__isodir = self._mkdtemp("iso-")
+ return self.__isodir
+
+ def _get_isodir(self):
+ return self.__ensure_isodir()
+
+ def _set_isodir(self, isodir = None):
+ self.__isodir = isodir
+
+ def _create_bootconfig(self):
+ """Configure the image so that it's bootable."""
+ self._configure_bootloader(self.__ensure_isodir())
+
+ def _get_post_scripts_env(self, in_chroot):
+ env = LoopImageCreator._get_post_scripts_env(self, in_chroot)
+
+ if not in_chroot:
+ env["LIVE_ROOT"] = self.__ensure_isodir()
+
+ return env
+
+ def __write_initrd_conf(self, path):
+ content = ""
+ if not os.path.exists(os.path.dirname(path)):
+ fs_related.makedirs(os.path.dirname(path))
+ f = open(path, "w")
+
+ content += 'LIVEOS="yes"\n'
+ content += 'PROBE="no"\n'
+ content += 'MODULES+="squashfs ext3 ext2 vfat msdos "\n'
+ content += 'MODULES+="sr_mod sd_mod ide-cd cdrom "\n'
+
+ for module in self.__modules:
+ if module == "=usb":
+ content += 'MODULES+="ehci_hcd uhci_hcd ohci_hcd "\n'
+ content += 'MODULES+="usb_storage usbhid "\n'
+ elif module == "=firewire":
+ content += 'MODULES+="firewire-sbp2 firewire-ohci "\n'
+ content += 'MODULES+="sbp2 ohci1394 ieee1394 "\n'
+ elif module == "=mmc":
+ content += 'MODULES+="mmc_block sdhci sdhci-pci "\n'
+ elif module == "=pcmcia":
+ content += 'MODULES+="pata_pcmcia "\n'
+ else:
+ content += 'MODULES+="' + module + ' "\n'
+ f.write(content)
+ f.close()
+
+ def __create_iso(self, isodir):
+ iso = self._outdir + "/" + self.name + ".iso"
+ genisoimage = fs_related.find_binary_path("genisoimage")
+ args = [genisoimage,
+ "-J", "-r",
+ "-hide-rr-moved", "-hide-joliet-trans-tbl",
+ "-V", self.fslabel,
+ "-o", iso]
+
+ args.extend(self._get_mkisofs_options(isodir))
+
+ args.append(isodir)
+
+ if runner.show(args) != 0:
+ raise CreatorError("ISO creation failed!")
+
+ """ It should be ok still even if you haven't isohybrid """
+ isohybrid = None
+ try:
+ isohybrid = fs_related.find_binary_path("isohybrid")
+ except:
+ pass
+
+ if isohybrid:
+ args = [isohybrid, "-partok", iso ]
+ if runner.show(args) != 0:
+ raise CreatorError("Hybrid ISO creation failed!")
+
+ self.__implant_md5sum(iso)
+
+ def __implant_md5sum(self, iso):
+ """Implant an isomd5sum."""
+ if os.path.exists("/usr/bin/implantisomd5"):
+ implantisomd5 = "/usr/bin/implantisomd5"
+ elif os.path.exists("/usr/lib/anaconda-runtime/implantisomd5"):
+ implantisomd5 = "/usr/lib/anaconda-runtime/implantisomd5"
+ else:
+ msger.warning("isomd5sum not installed; not setting up mediacheck")
+ implantisomd5 = ""
+ return
+
+ runner.show([implantisomd5, iso])
+
+ def _stage_final_image(self):
+ try:
+ fs_related.makedirs(self.__ensure_isodir() + "/LiveOS")
+
+ minimal_size = self._resparse()
+
+ if not self.skip_minimize:
+ fs_related.create_image_minimizer(self.__isodir + \
+ "/LiveOS/osmin.img",
+ self._image,
+ minimal_size)
+
+ if self.skip_compression:
+ shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
+ else:
+ fs_related.makedirs(os.path.join(
+ os.path.dirname(self._image),
+ "LiveOS"))
+ shutil.move(self._image,
+ os.path.join(os.path.dirname(self._image),
+ "LiveOS", "ext3fs.img"))
+ fs_related.mksquashfs(os.path.dirname(self._image),
+ self.__isodir + "/LiveOS/squashfs.img")
+
+ self.__create_iso(self.__isodir)
+
+ if self.pack_to:
+ isoimg = os.path.join(self._outdir, self.name + ".iso")
+ packimg = os.path.join(self._outdir, self.pack_to)
+ misc.packing(packimg, isoimg)
+ os.unlink(isoimg)
+
+ finally:
+ shutil.rmtree(self.__isodir, ignore_errors = True)
+ self.__isodir = None
+
+class x86LiveImageCreator(LiveImageCreatorBase):
+ """ImageCreator for x86 machines"""
+ def _get_mkisofs_options(self, isodir):
+ return [ "-b", "isolinux/isolinux.bin",
+ "-c", "isolinux/boot.cat",
+ "-no-emul-boot", "-boot-info-table",
+ "-boot-load-size", "4" ]
+
+ def _get_required_packages(self):
+ return ["syslinux", "syslinux-extlinux"] + \
+ LiveImageCreatorBase._get_required_packages(self)
+
+ def _get_isolinux_stanzas(self, isodir):
+ return ""
+
+ def __find_syslinux_menu(self):
+ for menu in ["vesamenu.c32", "menu.c32"]:
+ if os.path.isfile(self._instroot + "/usr/share/syslinux/" + menu):
+ return menu
+
+ raise CreatorError("syslinux not installed : "
+ "no suitable /usr/share/syslinux/*menu.c32 found")
+
+ def __find_syslinux_mboot(self):
+ #
+ # We only need the mboot module if we have any xen hypervisors
+ #
+ if not glob.glob(self._instroot + "/boot/xen.gz*"):
+ return None
+
+ return "mboot.c32"
+
+ def __copy_syslinux_files(self, isodir, menu, mboot = None):
+ files = ["isolinux.bin", menu]
+ if mboot:
+ files += [mboot]
+
+ for f in files:
+ path = self._instroot + "/usr/share/syslinux/" + f
+
+ if not os.path.isfile(path):
+ raise CreatorError("syslinux not installed : "
+ "%s not found" % path)
+
+ shutil.copy(path, isodir + "/isolinux/")
+
+ def __copy_syslinux_background(self, isodest):
+ background_path = self._instroot + \
+ "/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg"
+
+ if not os.path.exists(background_path):
+ return False
+
+ shutil.copyfile(background_path, isodest)
+
+ return True
+
+ def __copy_kernel_and_initramfs(self, isodir, version, index):
+ bootdir = self._instroot + "/boot"
+
+ if self._alt_initrd_name:
+ src_initrd_path = os.path.join(bootdir, self._alt_initrd_name)
+ else:
+ src_initrd_path = os.path.join(bootdir, "initrd-" +version+ ".img")
+
+ try:
+ shutil.copyfile(bootdir + "/vmlinuz-" + version,
+ isodir + "/isolinux/vmlinuz" + index)
+ shutil.copyfile(src_initrd_path,
+ isodir + "/isolinux/initrd" + index + ".img")
+ except:
+ raise CreatorError("Unable to copy valid kernels or initrds, "
+ "please check the repo.")
+
+ is_xen = False
+ if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
+ shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
+ isodir + "/isolinux/xen" + index + ".gz")
+ is_xen = True
+
+ return is_xen
+
+ def __is_default_kernel(self, kernel, kernels):
+ if len(kernels) == 1:
+ return True
+
+ if kernel == self._default_kernel:
+ return True
+
+ if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
+ return True
+
+ return False
+
+ def __get_basic_syslinux_config(self, **args):
+ return """
+default %(menu)s
+timeout %(timeout)d
+
+%(background)s
+menu title Welcome to %(distroname)s!
+menu color border 0 #ffffffff #00000000
+menu color sel 7 #ffffffff #ff000000
+menu color title 0 #ffffffff #00000000
+menu color tabmsg 0 #ffffffff #00000000
+menu color unsel 0 #ffffffff #00000000
+menu color hotsel 0 #ff000000 #ffffffff
+menu color hotkey 7 #ffffffff #ff000000
+menu color timeout_msg 0 #ffffffff #00000000
+menu color timeout 0 #ffffffff #00000000
+menu color cmdline 0 #ffffffff #00000000
+""" % args
+
+ def __get_image_stanza(self, is_xen, **args):
+ if not is_xen:
+ template = """label %(short)s
+ menu label %(long)s
+ kernel vmlinuz%(index)s
+ append initrd=initrd%(index)s.img root=CDLABEL=%(fslabel)s rootfstype=iso9660 %(liveargs)s %(extra)s
+"""
+ else:
+ template = """label %(short)s
+ menu label %(long)s
+ kernel mboot.c32
+ append xen%(index)s.gz --- vmlinuz%(index)s root=CDLABEL=%(fslabel)s rootfstype=iso9660 %(liveargs)s %(extra)s --- initrd%(index)s.img
+"""
+ return template % args
+
+ def __get_image_stanzas(self, isodir):
+ versions = []
+ kernels = self._get_kernel_versions()
+ for kernel in kernels:
+ for version in kernels[kernel]:
+ versions.append(version)
+
+ if not versions:
+ raise CreatorError("Unable to find valid kernels, "
+ "please check the repo")
+
+ kernel_options = self._get_kernel_options()
+
+ """ menu can be customized highly, the format is:
+
+ short_name1:long_name1:extra_opts1;short_name2:long_name2:extra_opts2
+
+ e.g.: autoinst:InstallationOnly:systemd.unit=installer-graphical.service
+ but in order to keep compatible with old format, these are still ok:
+
+ liveinst autoinst
+ liveinst;autoinst
+ liveinst::;autoinst::
+ """
+ oldmenus = {"basic": {
+ "short": "basic",
+ "long": "Installation Only (Text based)",
+ "extra": "basic nosplash 4"
+ },
+ "liveinst": {
+ "short": "liveinst",
+ "long": "Installation Only",
+ "extra": "liveinst nosplash 4"
+ },
+ "autoinst": {
+ "short": "autoinst",
+ "long": "Autoinstall (Deletes all existing content)",
+ "extra": "autoinst nosplash 4"
+ },
+ "netinst": {
+ "short": "netinst",
+ "long": "Network Installation",
+ "extra": "netinst 4"
+ },
+ "verify": {
+ "short": "check",
+ "long": "Verify and",
+ "extra": "check"
+ }
+ }
+ menu_options = self._get_menu_options()
+ menus = menu_options.split(";")
+ for i in range(len(menus)):
+ menus[i] = menus[i].split(":")
+ if len(menus) == 1 and len(menus[0]) == 1:
+ """ Keep compatible with the old usage way """
+ menus = menu_options.split()
+ for i in range(len(menus)):
+ menus[i] = [menus[i]]
+
+ cfg = ""
+
+ default_version = None
+ default_index = None
+ index = "0"
+ netinst = None
+ for version in versions:
+ is_xen = self.__copy_kernel_and_initramfs(isodir, version, index)
+
+ default = self.__is_default_kernel(kernel, kernels)
+
+ if default:
+ long = "Boot %s" % self.distro_name
+ elif kernel.startswith("kernel-"):
+ long = "Boot %s(%s)" % (self.name, kernel[7:])
+ else:
+ long = "Boot %s(%s)" % (self.name, kernel)
+
+ oldmenus["verify"]["long"] = "%s %s" % (oldmenus["verify"]["long"],
+ long)
+
+ cfg += self.__get_image_stanza(is_xen,
+ fslabel = self.fslabel,
+ liveargs = kernel_options,
+ long = long,
+ short = "linux" + index,
+ extra = "",
+ index = index)
+
+ if default:
+ cfg += "menu default\n"
+ default_version = version
+ default_index = index
+
+ for menu in menus:
+ if not menu[0]:
+ continue
+ short = menu[0] + index
+
+ if len(menu) >= 2:
+ long = menu[1]
+ else:
+ if menu[0] in oldmenus.keys():
+ if menu[0] == "verify" and not self._has_checkisomd5():
+ continue
+ if menu[0] == "netinst":
+ netinst = oldmenus[menu[0]]
+ continue
+ long = oldmenus[menu[0]]["long"]
+ extra = oldmenus[menu[0]]["extra"]
+ else:
+ long = short.upper() + " X" + index
+ extra = ""
+
+ if len(menu) >= 3:
+ extra = menu[2]
+
+ cfg += self.__get_image_stanza(is_xen,
+ fslabel = self.fslabel,
+ liveargs = kernel_options,
+ long = long,
+ short = short,
+ extra = extra,
+ index = index)
+
+ index = str(int(index) + 1)
+
+ if not default_version:
+ default_version = versions[0]
+ if not default_index:
+ default_index = "0"
+
+ if netinst:
+ cfg += self.__get_image_stanza(is_xen,
+ fslabel = self.fslabel,
+ liveargs = kernel_options,
+ long = netinst["long"],
+ short = netinst["short"],
+ extra = netinst["extra"],
+ index = default_index)
+
+ return cfg
+
+ def __get_memtest_stanza(self, isodir):
+ memtest = glob.glob(self._instroot + "/boot/memtest86*")
+ if not memtest:
+ return ""
+
+ shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
+
+ return """label memtest
+ menu label Memory Test
+ kernel memtest
+"""
+
+ def __get_local_stanza(self, isodir):
+ return """label local
+ menu label Boot from local drive
+ localboot 0xffff
+"""
+
+ def _configure_syslinux_bootloader(self, isodir):
+ """configure the boot loader"""
+ fs_related.makedirs(isodir + "/isolinux")
+
+ menu = self.__find_syslinux_menu()
+
+ self.__copy_syslinux_files(isodir, menu,
+ self.__find_syslinux_mboot())
+
+ background = ""
+ if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
+ background = "menu background splash.jpg"
+
+ cfg = self.__get_basic_syslinux_config(menu = menu,
+ background = background,
+ name = self.name,
+ timeout = self._timeout * 10,
+ distroname = self.distro_name)
+
+ cfg += self.__get_image_stanzas(isodir)
+ cfg += self.__get_memtest_stanza(isodir)
+ cfg += self.__get_local_stanza(isodir)
+ cfg += self._get_isolinux_stanzas(isodir)
+
+ cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
+ cfgf.write(cfg)
+ cfgf.close()
+
+ def __copy_efi_files(self, isodir):
+ if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"):
+ return False
+ shutil.copy(self._instroot + "/boot/efi/EFI/redhat/grub.efi",
+ isodir + "/EFI/boot/grub.efi")
+ shutil.copy(self._instroot + "/boot/grub/splash.xpm.gz",
+ isodir + "/EFI/boot/splash.xpm.gz")
+
+ return True
+
+ def __get_basic_efi_config(self, **args):
+ return """
+default=0
+splashimage=/EFI/boot/splash.xpm.gz
+timeout %(timeout)d
+hiddenmenu
+
+""" %args
+
+ def __get_efi_image_stanza(self, **args):
+ return """title %(long)s
+ kernel /EFI/boot/vmlinuz%(index)s root=CDLABEL=%(fslabel)s rootfstype=iso9660 %(liveargs)s %(extra)s
+ initrd /EFI/boot/initrd%(index)s.img
+""" %args
+
+ def __get_efi_image_stanzas(self, isodir, name):
+ # FIXME: this only supports one kernel right now...
+
+ kernel_options = self._get_kernel_options()
+ checkisomd5 = self._has_checkisomd5()
+
+ cfg = ""
+
+ for index in range(0, 9):
+ # we don't support xen kernels
+ if os.path.exists("%s/EFI/boot/xen%d.gz" %(isodir, index)):
+ continue
+ cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
+ liveargs = kernel_options,
+ long = name,
+ extra = "", index = index)
+ if checkisomd5:
+ cfg += self.__get_efi_image_stanza(
+ fslabel = self.fslabel,
+ liveargs = kernel_options,
+ long = "Verify and Boot " + name,
+ extra = "check",
+ index = index)
+ break
+
+ return cfg
+
+ def _configure_efi_bootloader(self, isodir):
+ """Set up the configuration for an EFI bootloader"""
+ fs_related.makedirs(isodir + "/EFI/boot")
+
+ if not self.__copy_efi_files(isodir):
+ shutil.rmtree(isodir + "/EFI")
+ return
+
+ for f in os.listdir(isodir + "/isolinux"):
+ os.link("%s/isolinux/%s" %(isodir, f),
+ "%s/EFI/boot/%s" %(isodir, f))
+
+
+ cfg = self.__get_basic_efi_config(name = self.name,
+ timeout = self._timeout)
+ cfg += self.__get_efi_image_stanzas(isodir, self.name)
+
+ cfgf = open(isodir + "/EFI/boot/grub.conf", "w")
+ cfgf.write(cfg)
+ cfgf.close()
+
+ # first gen mactel machines get the bootloader name wrong apparently
+ if rpmmisc.getBaseArch() == "i386":
+ os.link(isodir + "/EFI/boot/grub.efi",
+ isodir + "/EFI/boot/boot.efi")
+ os.link(isodir + "/EFI/boot/grub.conf",
+ isodir + "/EFI/boot/boot.conf")
+
+ # for most things, we want them named boot$efiarch
+ efiarch = {"i386": "ia32", "x86_64": "x64"}
+ efiname = efiarch[rpmmisc.getBaseArch()]
+ os.rename(isodir + "/EFI/boot/grub.efi",
+ isodir + "/EFI/boot/boot%s.efi" %(efiname,))
+ os.link(isodir + "/EFI/boot/grub.conf",
+ isodir + "/EFI/boot/boot%s.conf" %(efiname,))
+
+
+ def _configure_bootloader(self, isodir):
+ self._configure_syslinux_bootloader(isodir)
+ self._configure_efi_bootloader(isodir)
+
+arch = rpmmisc.getBaseArch()
+if arch in ("i386", "x86_64"):
+ LiveCDImageCreator = x86LiveImageCreator
+elif arch.startswith("arm"):
+ LiveCDImageCreator = LiveImageCreatorBase
+else:
+ raise CreatorError("Architecture not supported!")
diff --git a/mic/imager/liveusb.py b/mic/imager/liveusb.py
new file mode 100644
index 0000000..e46f122
--- /dev/null
+++ b/mic/imager/liveusb.py
@@ -0,0 +1,306 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import re
+
+from mic import msger
+from mic.utils import misc, fs_related, runner
+from mic.utils.errors import CreatorError, MountError
+from mic.utils.partitionedfs import PartitionedMount
+
+from livecd import LiveCDImageCreator
+class LiveUSBImageCreator(LiveCDImageCreator):
+ def __init__(self, *args):
+ LiveCDImageCreator.__init__(self, *args)
+
+ self._dep_checks.extend(["kpartx", "parted"])
+
+ # remove dependency of genisoimage in parent class
+ if "genisoimage" in self._dep_checks:
+ self._dep_checks.remove("genisoimage")
+
+ def _create_usbimg(self, isodir):
+ overlaysizemb = 64 #default
+ #skipcompress = self.skip_compression?
+ fstype = "vfat"
+ homesizemb=0
+ swapsizemb=0
+ homefile="home.img"
+ plussize=128
+ kernelargs=None
+
+ if fstype == 'vfat':
+ if overlaysizemb > 2047:
+ raise CreatorError("Can't have an overlay of 2048MB or "
+ "greater on VFAT")
+
+ if homesizemb > 2047:
+ raise CreatorError("Can't have an home overlay of 2048MB or "
+ "greater on VFAT")
+
+ if swapsizemb > 2047:
+ raise CreatorError("Can't have an swap overlay of 2048MB or "
+ "greater on VFAT")
+
+ livesize = misc.get_file_size(isodir + "/LiveOS")
+
+ usbimgsize = (overlaysizemb + \
+ homesizemb + \
+ swapsizemb + \
+ livesize + \
+ plussize) * 1024L * 1024L
+
+ disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \
+ % (self._outdir, self.name),
+ usbimgsize)
+ usbmnt = self._mkdtemp("usb-mnt")
+ usbloop = PartitionedMount({'/dev/sdb':disk}, usbmnt)
+
+ usbloop.add_partition(usbimgsize/1024/1024,
+ "/dev/sdb",
+ "/",
+ fstype,
+ boot=True)
+
+ usbloop.mount()
+
+ try:
+ fs_related.makedirs(usbmnt + "/LiveOS")
+
+ if os.path.exists(isodir + "/LiveOS/squashfs.img"):
+ shutil.copyfile(isodir + "/LiveOS/squashfs.img",
+ usbmnt + "/LiveOS/squashfs.img")
+ else:
+ fs_related.mksquashfs(os.path.dirname(self._image),
+ usbmnt + "/LiveOS/squashfs.img")
+
+ if os.path.exists(isodir + "/LiveOS/osmin.img"):
+ shutil.copyfile(isodir + "/LiveOS/osmin.img",
+ usbmnt + "/LiveOS/osmin.img")
+
+ if fstype == "vfat" or fstype == "msdos":
+ uuid = usbloop.partitions[0]['mount'].uuid
+ label = usbloop.partitions[0]['mount'].fslabel
+ usblabel = "UUID=%s-%s" % (uuid[0:4], uuid[4:8])
+ overlaysuffix = "-%s-%s-%s" % (label, uuid[0:4], uuid[4:8])
+ else:
+ diskmount = usbloop.partitions[0]['mount']
+ usblabel = "UUID=%s" % diskmount.uuid
+ overlaysuffix = "-%s-%s" % (diskmount.fslabel, diskmount.uuid)
+
+ args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't copy isolinux directory %s" \
+ % (isodir + "/isolinux/*"))
+
+ if os.path.isfile("/usr/share/syslinux/isolinux.bin"):
+ syslinux_path = "/usr/share/syslinux"
+ elif os.path.isfile("/usr/lib/syslinux/isolinux.bin"):
+ syslinux_path = "/usr/lib/syslinux"
+ else:
+ raise CreatorError("syslinux not installed : "
+ "cannot find syslinux installation path")
+
+ for f in ("isolinux.bin", "vesamenu.c32"):
+ path = os.path.join(syslinux_path, f)
+ if os.path.isfile(path):
+ args = ['cp', path, usbmnt + "/syslinux/"]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't copy syslinux file " + path)
+ else:
+ raise CreatorError("syslinux not installed: "
+ "syslinux file %s not found" % path)
+
+ fd = open(isodir + "/isolinux/isolinux.cfg", "r")
+ text = fd.read()
+ fd.close()
+ pattern = re.compile('CDLABEL=[^ ]*')
+ text = pattern.sub(usblabel, text)
+ pattern = re.compile('rootfstype=[^ ]*')
+ text = pattern.sub("rootfstype=" + fstype, text)
+ if kernelargs:
+ text = text.replace("liveimg", "liveimg " + kernelargs)
+
+ if overlaysizemb > 0:
+ msger.info("Initializing persistent overlay file")
+ overfile = "overlay" + overlaysuffix
+ if fstype == "vfat":
+ args = ['dd',
+ "if=/dev/zero",
+ "of=" + usbmnt + "/LiveOS/" + overfile,
+ "count=%d" % overlaysizemb,
+ "bs=1M"]
+ else:
+ args = ['dd',
+ "if=/dev/null",
+ "of=" + usbmnt + "/LiveOS/" + overfile,
+ "count=1",
+ "bs=1M",
+ "seek=%d" % overlaysizemb]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't create overlay file")
+ text = text.replace("liveimg", "liveimg overlay=" + usblabel)
+ text = text.replace(" ro ", " rw ")
+
+ if swapsizemb > 0:
+ msger.info("Initializing swap file")
+ swapfile = usbmnt + "/LiveOS/" + "swap.img"
+ args = ['dd',
+ "if=/dev/zero",
+ "of=" + swapfile,
+ "count=%d" % swapsizemb,
+ "bs=1M"]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't create swap file")
+ args = ["mkswap", "-f", swapfile]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't mkswap on swap file")
+
+ if homesizemb > 0:
+ msger.info("Initializing persistent /home")
+ homefile = usbmnt + "/LiveOS/" + homefile
+ if fstype == "vfat":
+ args = ['dd',
+ "if=/dev/zero",
+ "of=" + homefile,
+ "count=%d" % homesizemb,
+ "bs=1M"]
+ else:
+ args = ['dd',
+ "if=/dev/null",
+ "of=" + homefile,
+ "count=1",
+ "bs=1M",
+ "seek=%d" % homesizemb]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't create home file")
+
+ mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype)
+ if fstype == "ext2" or fstype == "ext3":
+ args = [mkfscmd, "-F", "-j", homefile]
+ else:
+ args = [mkfscmd, homefile]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't mke2fs home file")
+ if fstype == "ext2" or fstype == "ext3":
+ tune2fs = fs_related.find_binary_path("tune2fs")
+ args = [tune2fs,
+ "-c0",
+ "-i0",
+ "-ouser_xattr,acl",
+ homefile]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't tune2fs home file")
+
+ if fstype == "vfat" or fstype == "msdos":
+ syslinuxcmd = fs_related.find_binary_path("syslinux")
+ syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg"
+ args = [syslinuxcmd,
+ "-d",
+ "syslinux",
+ usbloop.partitions[0]["device"]]
+
+ elif fstype == "ext2" or fstype == "ext3":
+ extlinuxcmd = fs_related.find_binary_path("extlinux")
+ syslinuxcfg = usbmnt + "/syslinux/extlinux.conf"
+ args = [extlinuxcmd,
+ "-i",
+ usbmnt + "/syslinux"]
+
+ else:
+ raise CreatorError("Invalid file system type: %s" % (fstype))
+
+ os.unlink(usbmnt + "/syslinux/isolinux.cfg")
+ fd = open(syslinuxcfg, "w")
+ fd.write(text)
+ fd.close()
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't install boot loader.")
+
+ finally:
+ usbloop.unmount()
+ usbloop.cleanup()
+
+ # Need to do this after image is unmounted and device mapper is closed
+ msger.info("set MBR")
+ mbrfile = "/usr/lib/syslinux/mbr.bin"
+ if not os.path.exists(mbrfile):
+ mbrfile = "/usr/share/syslinux/mbr.bin"
+ if not os.path.exists(mbrfile):
+ raise CreatorError("mbr.bin file didn't exist.")
+ mbrsize = os.path.getsize(mbrfile)
+ outimg = "%s/%s.usbimg" % (self._outdir, self.name)
+
+ args = ['dd',
+ "if=" + mbrfile,
+ "of=" + outimg,
+ "seek=0",
+ "conv=notrunc",
+ "bs=1",
+ "count=%d" % (mbrsize)]
+ rc = runner.show(args)
+ if rc:
+ raise CreatorError("Can't set MBR.")
+
+ def _stage_final_image(self):
+ try:
+ isodir = self._get_isodir()
+ fs_related.makedirs(isodir + "/LiveOS")
+
+ minimal_size = self._resparse()
+
+ if not self.skip_minimize:
+ fs_related.create_image_minimizer(isodir + "/LiveOS/osmin.img",
+ self._image,
+ minimal_size)
+
+ if self.skip_compression:
+ shutil.move(self._image,
+ isodir + "/LiveOS/ext3fs.img")
+ else:
+ fs_related.makedirs(os.path.join(
+ os.path.dirname(self._image),
+ "LiveOS"))
+ shutil.move(self._image,
+ os.path.join(os.path.dirname(self._image),
+ "LiveOS", "ext3fs.img"))
+ fs_related.mksquashfs(os.path.dirname(self._image),
+ isodir + "/LiveOS/squashfs.img")
+
+ self._create_usbimg(isodir)
+
+ if self.pack_to:
+ usbimg = os.path.join(self._outdir, self.name + ".usbimg")
+ packimg = os.path.join(self._outdir, self.pack_to)
+ misc.packing(packimg, usbimg)
+ os.unlink(usbimg)
+
+ finally:
+ shutil.rmtree(isodir, ignore_errors = True)
+ self._set_isodir(None)
+
diff --git a/mic/imager/loop.py b/mic/imager/loop.py
new file mode 100644
index 0000000..91b3e9b
--- /dev/null
+++ b/mic/imager/loop.py
@@ -0,0 +1,403 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import glob
+import shutil
+
+from mic import kickstart, msger
+from mic.utils.errors import CreatorError, MountError
+from mic.utils import misc, runner, fs_related as fs
+
+from baseimager import BaseImageCreator
+
+# The maximum string length supported for LoopImageCreator.fslabel
+FSLABEL_MAXLEN = 32
+
+def save_mountpoints(fpath, loops, arch = None):
+ """Save mount points mapping to file
+
+ :fpath, the xml file to store partition info
+ :loops, dict of partition info
+ :arch, image arch
+ """
+
+ if not fpath or not loops:
+ return
+
+ from xml.dom import minidom
+ doc = minidom.Document()
+ imgroot = doc.createElement("image")
+ doc.appendChild(imgroot)
+ if arch:
+ imgroot.setAttribute('arch', arch)
+ for loop in loops:
+ part = doc.createElement("partition")
+ imgroot.appendChild(part)
+ for (key, val) in loop.items():
+ if isinstance(val, fs.Mount):
+ continue
+ part.setAttribute(key, str(val))
+
+ with open(fpath, 'w') as wf:
+ wf.write(doc.toprettyxml(indent=' '))
+
+ return
+
+def load_mountpoints(fpath):
+ """Load mount points mapping from file
+
+ :fpath, file path to load
+ """
+
+ if not fpath:
+ return
+
+ from xml.dom import minidom
+ mount_maps = []
+ with open(fpath, 'r') as rf:
+ dom = minidom.parse(rf)
+ imgroot = dom.documentElement
+ for part in imgroot.getElementsByTagName("partition"):
+ p = dict(part.attributes.items())
+
+ try:
+ mp = (p['mountpoint'], p['label'], p['name'],
+ int(p['size']), p['fstype'])
+ except KeyError:
+ msger.warning("Wrong format line in file: %s" % fpath)
+ except ValueError:
+ msger.warning("Invalid size '%s' in file: %s" % (p['size'], fpath))
+ else:
+ mount_maps.append(mp)
+
+ return mount_maps
+
+class LoopImageCreator(BaseImageCreator):
+ """Installs a system into a loopback-mountable filesystem image.
+
+ LoopImageCreator is a straightforward ImageCreator subclass; the system
+ is installed into an ext3 filesystem on a sparse file which can be
+ subsequently loopback-mounted.
+
+ When specifying multiple partitions in kickstart file, each partition
+ will be created as a separated loop image.
+ """
+
+ def __init__(self, creatoropts=None, pkgmgr=None,
+ compress_image=None,
+ shrink_image=False):
+ """Initialize a LoopImageCreator instance.
+
+ This method takes the same arguments as ImageCreator.__init__()
+ with the addition of:
+
+ fslabel -- A string used as a label for any filesystems created.
+ """
+
+ BaseImageCreator.__init__(self, creatoropts, pkgmgr)
+
+ self.compress_image = compress_image
+ self.shrink_image = shrink_image
+
+ self.__fslabel = None
+ self.fslabel = self.name
+
+ self.__blocksize = 4096
+ if self.ks:
+ self.__fstype = kickstart.get_image_fstype(self.ks,
+ "ext3")
+ self.__fsopts = kickstart.get_image_fsopts(self.ks,
+ "defaults,noatime")
+
+ allloops = []
+ for part in sorted(kickstart.get_partitions(self.ks),
+ key=lambda p: p.mountpoint):
+ if part.fstype == "swap":
+ continue
+
+ label = part.label
+ mp = part.mountpoint
+ if mp == '/':
+ # the base image
+ if not label:
+ label = self.name
+ else:
+ mp = mp.rstrip('/')
+ if not label:
+ msger.warning('no "label" specified for loop img at %s'
+ ', use the mountpoint as the name' % mp)
+ label = mp.split('/')[-1]
+
+ imgname = misc.strip_end(label, '.img') + '.img'
+ allloops.append({
+ 'mountpoint': mp,
+ 'label': label,
+ 'name': imgname,
+ 'size': part.size or 4096L * 1024 * 1024,
+ 'fstype': part.fstype or 'ext3',
+ 'loop': None, # to be created in _mount_instroot
+ })
+ self._instloops = allloops
+
+ else:
+ self.__fstype = None
+ self.__fsopts = None
+ self._instloops = []
+
+ self.__imgdir = None
+
+ if self.ks:
+ self.__image_size = kickstart.get_image_size(self.ks,
+ 4096L * 1024 * 1024)
+ else:
+ self.__image_size = 0
+
+ self._img_name = self.name + ".img"
+
+ def _set_fstype(self, fstype):
+ self.__fstype = fstype
+
+ def _set_image_size(self, imgsize):
+ self.__image_size = imgsize
+
+
+ #
+ # Properties
+ #
+ def __get_fslabel(self):
+ if self.__fslabel is None:
+ return self.name
+ else:
+ return self.__fslabel
+ def __set_fslabel(self, val):
+ if val is None:
+ self.__fslabel = None
+ else:
+ self.__fslabel = val[:FSLABEL_MAXLEN]
+ #A string used to label any filesystems created.
+ #
+ #Some filesystems impose a constraint on the maximum allowed size of the
+ #filesystem label. In the case of ext3 it's 16 characters, but in the case
+ #of ISO9660 it's 32 characters.
+ #
+ #mke2fs silently truncates the label, but mkisofs aborts if the label is
+ #too long. So, for convenience sake, any string assigned to this attribute
+ #is silently truncated to FSLABEL_MAXLEN (32) characters.
+ fslabel = property(__get_fslabel, __set_fslabel)
+
+ def __get_image(self):
+ if self.__imgdir is None:
+ raise CreatorError("_image is not valid before calling mount()")
+ return os.path.join(self.__imgdir, self._img_name)
+ #The location of the image file.
+ #
+ #This is the path to the filesystem image. Subclasses may use this path
+ #in order to package the image in _stage_final_image().
+ #
+ #Note, this directory does not exist before ImageCreator.mount() is called.
+ #
+ #Note also, this is a read-only attribute.
+ _image = property(__get_image)
+
+ def __get_blocksize(self):
+ return self.__blocksize
+ def __set_blocksize(self, val):
+ if self._instloops:
+ raise CreatorError("_blocksize must be set before calling mount()")
+ try:
+ self.__blocksize = int(val)
+ except ValueError:
+ raise CreatorError("'%s' is not a valid integer value "
+ "for _blocksize" % val)
+ #The block size used by the image's filesystem.
+ #
+ #This is the block size used when creating the filesystem image. Subclasses
+ #may change this if they wish to use something other than a 4k block size.
+ #
+ #Note, this attribute may only be set before calling mount().
+ _blocksize = property(__get_blocksize, __set_blocksize)
+
+ def __get_fstype(self):
+ return self.__fstype
+ def __set_fstype(self, val):
+ if val != "ext2" and val != "ext3":
+ raise CreatorError("Unknown _fstype '%s' supplied" % val)
+ self.__fstype = val
+ #The type of filesystem used for the image.
+ #
+ #This is the filesystem type used when creating the filesystem image.
+ #Subclasses may change this if they wish to use something other ext3.
+ #
+ #Note, only ext2 and ext3 are currently supported.
+ #
+ #Note also, this attribute may only be set before calling mount().
+ _fstype = property(__get_fstype, __set_fstype)
+
+ def __get_fsopts(self):
+ return self.__fsopts
+ def __set_fsopts(self, val):
+ self.__fsopts = val
+ #Mount options of filesystem used for the image.
+ #
+ #This can be specified by --fsoptions="xxx,yyy" in part command in
+ #kickstart file.
+ _fsopts = property(__get_fsopts, __set_fsopts)
+
+
+ #
+ # Helpers for subclasses
+ #
+ def _resparse(self, size=None):
+ """Rebuild the filesystem image to be as sparse as possible.
+
+ This method should be used by subclasses when staging the final image
+ in order to reduce the actual space taken up by the sparse image file
+ to be as little as possible.
+
+ This is done by resizing the filesystem to the minimal size (thereby
+ eliminating any space taken up by deleted files) and then resizing it
+ back to the supplied size.
+
+ size -- the size in, in bytes, which the filesystem image should be
+ resized to after it has been minimized; this defaults to None,
+ causing the original size specified by the kickstart file to
+ be used (or 4GiB if not specified in the kickstart).
+ """
+ minsize = 0
+ for item in self._instloops:
+ if item['name'] == self._img_name:
+ minsize = item['loop'].resparse(size)
+ else:
+ item['loop'].resparse(size)
+
+ return minsize
+
+ def _base_on(self, base_on=None):
+ if base_on and self._image != base_on:
+ shutil.copyfile(base_on, self._image)
+
+ def _check_imgdir(self):
+ if self.__imgdir is None:
+ self.__imgdir = self._mkdtemp()
+
+
+ #
+ # Actual implementation
+ #
+ def _mount_instroot(self, base_on=None):
+
+ if base_on and os.path.isfile(base_on):
+ self.__imgdir = os.path.dirname(base_on)
+ imgname = os.path.basename(base_on)
+ self._base_on(base_on)
+ self._set_image_size(misc.get_file_size(self._image))
+
+ # here, self._instloops must be []
+ self._instloops.append({
+ "mountpoint": "/",
+ "label": self.name,
+ "name": imgname,
+ "size": self.__image_size or 4096L,
+ "fstype": self.__fstype or "ext3",
+ "loop": None
+ })
+
+ self._check_imgdir()
+
+ for loop in self._instloops:
+ fstype = loop['fstype']
+ mp = os.path.join(self._instroot, loop['mountpoint'].lstrip('/'))
+ size = loop['size'] * 1024L * 1024L
+ imgname = loop['name']
+
+ if fstype in ("ext2", "ext3", "ext4"):
+ MyDiskMount = fs.ExtDiskMount
+ elif fstype == "btrfs":
+ MyDiskMount = fs.BtrfsDiskMount
+ elif fstype in ("vfat", "msdos"):
+ MyDiskMount = fs.VfatDiskMount
+ else:
+ msger.error('Cannot support fstype: %s' % fstype)
+
+ loop['loop'] = MyDiskMount(fs.SparseLoopbackDisk(
+ os.path.join(self.__imgdir, imgname),
+ size),
+ mp,
+ fstype,
+ self._blocksize,
+ loop['label'])
+
+ try:
+ msger.verbose('Mounting image "%s" on "%s"' % (imgname, mp))
+ fs.makedirs(mp)
+ loop['loop'].mount()
+ except MountError, e:
+ raise
+
+ def _unmount_instroot(self):
+ for item in reversed(self._instloops):
+ item['loop'].cleanup()
+
+ def _stage_final_image(self):
+
+ if self.pack_to or self.shrink_image:
+ self._resparse(0)
+ else:
+ self._resparse()
+
+ for item in self._instloops:
+ imgfile = os.path.join(self.__imgdir, item['name'])
+ if item['fstype'] == "ext4":
+ runner.show('/sbin/tune2fs -O ^huge_file,extents,uninit_bg %s '
+ % imgfile)
+ if self.compress_image:
+ misc.compressing(imgfile, self.compress_image)
+
+ if not self.pack_to:
+ for item in os.listdir(self.__imgdir):
+ shutil.move(os.path.join(self.__imgdir, item),
+ os.path.join(self._outdir, item))
+ else:
+ msger.info("Pack all loop images together to %s" % self.pack_to)
+ dstfile = os.path.join(self._outdir, self.pack_to)
+ misc.packing(dstfile, self.__imgdir)
+
+ if self.pack_to:
+ mountfp_xml = os.path.splitext(self.pack_to)[0]
+ mountfp_xml = misc.strip_end(mountfp_xml, '.tar') + ".xml"
+ else:
+ mountfp_xml = self.name + ".xml"
+ # save mount points mapping file to xml
+ save_mountpoints(os.path.join(self._outdir, mountfp_xml),
+ self._instloops,
+ self.target_arch)
+
+ def copy_attachment(self):
+ if not hasattr(self, '_attachment') or not self._attachment:
+ return
+
+ self._check_imgdir()
+
+ msger.info("Copying attachment files...")
+ for item in self._attachment:
+ if not os.path.exists(item):
+ continue
+ dpath = os.path.join(self.__imgdir, os.path.basename(item))
+ msger.verbose("Copy attachment %s to %s" % (item, dpath))
+ shutil.copy(item, dpath)
+
diff --git a/mic/imager/raw.py b/mic/imager/raw.py
new file mode 100644
index 0000000..9ac3e6e
--- /dev/null
+++ b/mic/imager/raw.py
@@ -0,0 +1,505 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import stat
+import shutil
+
+from urlgrabber import progress
+
+from mic import kickstart, msger
+from mic.utils import fs_related, runner, misc
+from mic.utils.partitionedfs import PartitionedMount
+from mic.utils.errors import CreatorError, MountError
+
+from baseimager import BaseImageCreator
+class RawImageCreator(BaseImageCreator):
+ """Installs a system into a file containing a partitioned disk image.
+
+ ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file
+ is formatted with a partition table, each partition loopback mounted
+ and the system installed into an virtual disk. The disk image can
+ subsequently be booted in a virtual machine or accessed with kpartx
+ """
+
+ def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None):
+ """Initialize a ApplianceImageCreator instance.
+
+ This method takes the same arguments as ImageCreator.__init__()
+ """
+ BaseImageCreator.__init__(self, creatoropts, pkgmgr)
+
+ self.__instloop = None
+ self.__imgdir = None
+ self.__disks = {}
+ self.__disk_format = "raw"
+ self._diskinfo = []
+ self.vmem = 512
+ self.vcpu = 1
+ self.checksum = False
+ self.appliance_version = None
+ self.appliance_release = None
+ self.compress_image = compress_image
+ #self.getsource = False
+ #self.listpkg = False
+
+ self._dep_checks.extend(["sync", "kpartx", "parted", "extlinux"])
+
+ def configure(self, repodata = None):
+ import subprocess
+ def chroot():
+ os.chroot(self._instroot)
+ os.chdir("/")
+
+ if os.path.exists(self._instroot + "/usr/bin/Xorg"):
+ subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"],
+ preexec_fn = chroot)
+
+ BaseImageCreator.configure(self, repodata)
+
+ def _get_fstab(self):
+ s = ""
+ for mp in self.__instloop.mountOrder:
+ p = None
+ for p1 in self.__instloop.partitions:
+ if p1['mountpoint'] == mp:
+ p = p1
+ break
+
+ s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % {
+ 'device': "UUID=%s" % p['uuid'],
+ 'mountpoint': p['mountpoint'],
+ 'fstype': p['fstype'],
+ 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']}
+
+ if p['mountpoint'] == "/":
+ for subvol in self.__instloop.subvolumes:
+ if subvol['mountpoint'] == "/":
+ continue
+ s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % {
+ 'device': "/dev/%s%-d" % (p['disk'], p['num']),
+ 'mountpoint': subvol['mountpoint'],
+ 'fstype': p['fstype'],
+ 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']}
+
+ s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
+ s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
+ s += "proc /proc proc defaults 0 0\n"
+ s += "sysfs /sys sysfs defaults 0 0\n"
+ return s
+
+ def _create_mkinitrd_config(self):
+ """write to tell which modules to be included in initrd"""
+
+ mkinitrd = ""
+ mkinitrd += "PROBE=\"no\"\n"
+ mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n"
+ mkinitrd += "rootfs=\"ext3\"\n"
+ mkinitrd += "rootopts=\"defaults\"\n"
+
+ msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \
+ % self._instroot)
+ os.makedirs(self._instroot + "/etc/sysconfig/",mode=644)
+ cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w")
+ cfg.write(mkinitrd)
+ cfg.close()
+
+ def _get_parts(self):
+ if not self.ks:
+ raise CreatorError("Failed to get partition info, "
+ "please check your kickstart setting.")
+
+ # Set a default partition if no partition is given out
+ if not self.ks.handler.partition.partitions:
+ partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
+ args = partstr.split()
+ pd = self.ks.handler.partition.parse(args[1:])
+ if pd not in self.ks.handler.partition.partitions:
+ self.ks.handler.partition.partitions.append(pd)
+
+ # partitions list from kickstart file
+ return kickstart.get_partitions(self.ks)
+
+ def get_diskinfo(self):
+
+ if self._diskinfo:
+ return self._diskinfo
+
+ #get partition info from ks handler
+ parts = self._get_parts()
+
+ for i in range(len(parts)):
+ if parts[i].disk:
+ disk = parts[i].disk
+ else:
+ raise CreatorError("Failed to create disks, no --ondisk "
+ "specified in partition line of ks file")
+
+ if not parts[i].fstype:
+ raise CreatorError("Failed to create disks, no --fstype "
+ "specified in partition line of ks file")
+
+ size = parts[i].size * 1024L * 1024L
+
+ # If we have alignment set for partition we need to enlarge the
+ # drive, so that the alignment changes fits there as well
+ if parts[i].align:
+ size += parts[i].align * 1024L
+
+ found = False
+ for j in range(len(self._diskinfo)):
+ if self._diskinfo[j]['name'] == disk:
+ self._diskinfo[j]['size'] = self._diskinfo[j]['size'] + size
+ found = True
+ break
+ else:
+ found = False
+
+ if not found:
+ self._diskinfo.append({ 'name': disk, 'size': size })
+
+ return self._diskinfo
+
+ #
+ # Actual implemention
+ #
+ def _mount_instroot(self, base_on = None):
+ self.__imgdir = self._mkdtemp()
+
+ parts = self._get_parts()
+
+ #create disk
+ for item in self.get_diskinfo():
+ msger.debug("Adding disk %s as %s/%s-%s.raw with size %s bytes" %
+ (item['name'], self.__imgdir, self.name, item['name'],
+ item['size']))
+
+ disk = fs_related.SparseLoopbackDisk("%s/%s-%s.raw" % (
+ self.__imgdir,
+ self.name,
+ item['name']),
+ item['size'])
+ self.__disks[item['name']] = disk
+
+ self.__instloop = PartitionedMount(self.__disks, self._instroot)
+
+ for p in parts:
+ self.__instloop.add_partition(int(p.size),
+ p.disk,
+ p.mountpoint,
+ p.fstype,
+ p.label,
+ fsopts = p.fsopts,
+ boot = p.active,
+ align = p.align)
+
+ self.__instloop.mount()
+ self._create_mkinitrd_config()
+
+ def _get_required_packages(self):
+ required_packages = BaseImageCreator._get_required_packages(self)
+ if not self.target_arch or not self.target_arch.startswith("arm"):
+ required_packages += ["syslinux", "syslinux-extlinux"]
+ return required_packages
+
+ def _get_excluded_packages(self):
+ return BaseImageCreator._get_excluded_packages(self)
+
+ def _get_syslinux_boot_config(self):
+ bootdevnum = None
+ rootdevnum = None
+ rootdev = None
+ for p in self.__instloop.partitions:
+ if p['mountpoint'] == "/boot":
+ bootdevnum = p['num'] - 1
+ elif p['mountpoint'] == "/" and bootdevnum is None:
+ bootdevnum = p['num'] - 1
+
+ if p['mountpoint'] == "/":
+ rootdevnum = p['num'] - 1
+ rootdev = "/dev/%s%-d" % (p['disk'], p['num'])
+
+ prefix = ""
+ if bootdevnum == rootdevnum:
+ prefix = "/boot"
+
+ return (bootdevnum, rootdevnum, rootdev, prefix)
+
+ def _create_syslinux_config(self):
+ #Copy splash
+ splash = "%s/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" \
+ % self._instroot
+
+ if os.path.exists(splash):
+ shutil.copy(splash, "%s%s/splash.jpg" \
+ % (self._instroot, "/boot/extlinux"))
+ splashline = "menu background splash.jpg"
+ else:
+ splashline = ""
+
+ (bootdevnum, rootdevnum, rootdev, prefix) = \
+ self._get_syslinux_boot_config()
+ options = self.ks.handler.bootloader.appendLine
+
+ #XXX don't hardcode default kernel - see livecd code
+ syslinux_conf = ""
+ syslinux_conf += "prompt 0\n"
+ syslinux_conf += "timeout 1\n"
+ syslinux_conf += "\n"
+ syslinux_conf += "default vesamenu.c32\n"
+ syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name
+ syslinux_conf += "menu hidden\n"
+ syslinux_conf += "\n"
+ syslinux_conf += "%s\n" % splashline
+ syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name
+ syslinux_conf += "menu color border 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n"
+ syslinux_conf += "menu color title 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n"
+ syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n"
+ syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n"
+ syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n"
+
+ versions = []
+ kernels = self._get_kernel_versions()
+ symkern = "%s/boot/vmlinuz" % self._instroot
+
+ if os.path.lexists(symkern):
+ v = os.path.realpath(symkern).replace('%s-' % symkern, "")
+ syslinux_conf += "label %s\n" % self.distro_name.lower()
+ syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v)
+ syslinux_conf += "\tlinux /vmlinuz\n"
+ syslinux_conf += "\tappend ro root=%s %s\n" % (rootdev, options)
+ syslinux_conf += "\tmenu default\n"
+ else:
+ for kernel in kernels:
+ for version in kernels[kernel]:
+ versions.append(version)
+
+ footlabel = 0
+ for v in versions:
+ shutil.copy("%s/boot/vmlinuz-%s" %(self._instroot, v),
+ "%s%s/vmlinuz-%s" % (self._instroot,
+ "/boot/extlinux/", v))
+ syslinux_conf += "label %s%d\n" \
+ % (self.distro_name.lower(), footlabel)
+ syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v)
+ syslinux_conf += "\tlinux vmlinuz-%s\n" % v
+ syslinux_conf += "\tappend ro root=%s %s\n" \
+ % (rootdev, options)
+ if footlabel == 0:
+ syslinux_conf += "\tmenu default\n"
+ footlabel += 1;
+
+ msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \
+ % self._instroot)
+ cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w")
+ cfg.write(syslinux_conf)
+ cfg.close()
+
+ def _install_syslinux(self):
+ i = 0
+ for name in self.__disks.keys():
+ loopdev = self.__disks[name].device
+ i =i+1
+
+ msger.debug("Installing syslinux bootloader to %s" % loopdev)
+
+ (bootdevnum, rootdevnum, rootdev, prefix) = \
+ self._get_syslinux_boot_config()
+
+
+ #Set MBR
+ mbrsize = os.stat("%s/usr/share/syslinux/mbr.bin" \
+ % self._instroot)[stat.ST_SIZE]
+ rc = runner.show(['dd',
+ 'if=%s/usr/share/syslinux/mbr.bin' % self._instroot,
+ 'of=' + loopdev])
+ if rc != 0:
+ raise MountError("Unable to set MBR to %s" % loopdev)
+
+ #Set Bootable flag
+ parted = fs_related.find_binary_path("parted")
+ rc = runner.quiet([parted,
+ "-s",
+ loopdev,
+ "set",
+ "%d" % (bootdevnum + 1),
+ "boot",
+ "on"])
+ #XXX disabled return code check because parted always fails to
+ #reload part table with loop devices. Annoying because we can't
+ #distinguish this failure from real partition failures :-(
+ if rc != 0 and 1 == 0:
+ raise MountError("Unable to set bootable flag to %sp%d" \
+ % (loopdev, (bootdevnum + 1)))
+
+ #Ensure all data is flushed to disk before doing syslinux install
+ runner.quiet('sync')
+
+ fullpathsyslinux = fs_related.find_binary_path("extlinux")
+ rc = runner.show([fullpathsyslinux,
+ "-i",
+ "%s/boot/extlinux" % self._instroot])
+ if rc != 0:
+ raise MountError("Unable to install syslinux bootloader to %sp%d" \
+ % (loopdev, (bootdevnum + 1)))
+
+ def _create_bootconfig(self):
+ #If syslinux is available do the required configurations.
+ if os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \
+ and os.path.exists("%s/boot/extlinux/" % (self._instroot)):
+ self._create_syslinux_config()
+ self._install_syslinux()
+
+ def _unmount_instroot(self):
+ if not self.__instloop is None:
+ self.__instloop.cleanup()
+
+ def _resparse(self, size = None):
+ return self.__instloop.resparse(size)
+
+ def _stage_final_image(self):
+ """Stage the final system image in _outdir.
+ write meta data
+ """
+ self._resparse()
+
+ if self.compress_image:
+ for imgfile in os.listdir(self.__imgdir):
+ if imgfile.endswith('.raw') or imgfile.endswith('bin'):
+ imgpath = os.path.join(self.__imgdir, imgfile)
+ misc.compressing(imgpath, self.compress_image)
+
+ if self.pack_to:
+ dst = os.path.join(self._outdir, self.pack_to)
+ msger.info("Pack all raw images to %s" % dst)
+ misc.packing(dst, self.__imgdir)
+ else:
+ msger.debug("moving disks to stage location")
+ for imgfile in os.listdir(self.__imgdir):
+ src = os.path.join(self.__imgdir, imgfile)
+ dst = os.path.join(self._outdir, imgfile)
+ msger.debug("moving %s to %s" % (src,dst))
+ shutil.move(src,dst)
+ self._write_image_xml()
+
+ def _write_image_xml(self):
+ imgarch = "i686"
+ if self.target_arch and self.target_arch.startswith("arm"):
+ imgarch = "arm"
+ xml = "<image>\n"
+
+ name_attributes = ""
+ if self.appliance_version:
+ name_attributes += " version='%s'" % self.appliance_version
+ if self.appliance_release:
+ name_attributes += " release='%s'" % self.appliance_release
+ xml += " <name%s>%s</name>\n" % (name_attributes, self.name)
+ xml += " <domain>\n"
+ # XXX don't hardcode - determine based on the kernel we installed for
+ # grub baremetal vs xen
+ xml += " <boot type='hvm'>\n"
+ xml += " <guest>\n"
+ xml += " <arch>%s</arch>\n" % imgarch
+ xml += " </guest>\n"
+ xml += " <os>\n"
+ xml += " <loader dev='hd'/>\n"
+ xml += " </os>\n"
+
+ i = 0
+ for name in self.__disks.keys():
+ xml += " <drive disk='%s-%s.%s' target='hd%s'/>\n" \
+ % (self.name,name, self.__disk_format,chr(ord('a')+i))
+ i = i + 1
+
+ xml += " </boot>\n"
+ xml += " <devices>\n"
+ xml += " <vcpu>%s</vcpu>\n" % self.vcpu
+ xml += " <memory>%d</memory>\n" %(self.vmem * 1024)
+ for network in self.ks.handler.network.network:
+ xml += " <interface/>\n"
+ xml += " <graphics/>\n"
+ xml += " </devices>\n"
+ xml += " </domain>\n"
+ xml += " <storage>\n"
+
+ if self.checksum is True:
+ for name in self.__disks.keys():
+ diskpath = "%s/%s-%s.%s" \
+ % (self._outdir,self.name,name, self.__disk_format)
+ disk_size = os.path.getsize(diskpath)
+ meter_ct = 0
+ meter = progress.TextMeter()
+ meter.start(size=disk_size,
+ text="Generating disk signature for %s-%s.%s" \
+ % (self.name,
+ name,
+ self.__disk_format))
+ xml += " <disk file='%s-%s.%s' use='system' format='%s'>\n"\
+ % (self.name,
+ name,
+ self.__disk_format,
+ self.__disk_format)
+
+ try:
+ import hashlib
+ m1 = hashlib.sha1()
+ m2 = hashlib.sha256()
+ except:
+ import sha
+ m1 = sha.new()
+ m2 = None
+ f = open(diskpath,"r")
+ while 1:
+ chunk = f.read(65536)
+ if not chunk:
+ break
+ m1.update(chunk)
+ if m2:
+ m2.update(chunk)
+ meter.update(meter_ct)
+ meter_ct = meter_ct + 65536
+
+ sha1checksum = m1.hexdigest()
+ xml += " <checksum type='sha1'>%s</checksum>\n" \
+ % sha1checksum
+
+ if m2:
+ sha256checksum = m2.hexdigest()
+ xml += " <checksum type='sha256'>%s</checksum>\n" \
+ % sha256checksum
+
+ xml += " </disk>\n"
+ else:
+ for name in self.__disks.keys():
+ xml += " <disk file='%s-%s.%s' use='system' format='%s'/>\n"\
+ %(self.name,
+ name,
+ self.__disk_format,
+ self.__disk_format)
+
+ xml += " </storage>\n"
+ xml += "</image>\n"
+
+ msger.debug("writing image XML to %s/%s.xml" %(self._outdir, self.name))
+ cfg = open("%s/%s.xml" % (self._outdir, self.name), "w")
+ cfg.write(xml)
+ cfg.close()
diff --git a/mic/kickstart/__init__.py b/mic/kickstart/__init__.py
new file mode 100644
index 0000000..27437b7
--- /dev/null
+++ b/mic/kickstart/__init__.py
@@ -0,0 +1,915 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2007 Red Hat, Inc.
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys, re
+import shutil
+import subprocess
+import time
+import string
+
+from mic import msger
+from mic.utils import errors, misc, runner, fs_related as fs
+
+import pykickstart.sections as kssections
+import pykickstart.commands as kscommands
+import pykickstart.constants as ksconstants
+import pykickstart.errors as kserrors
+import pykickstart.parser as ksparser
+import pykickstart.version as ksversion
+from pykickstart.handlers.control import commandMap
+from pykickstart.handlers.control import dataMap
+
+import custom_commands.desktop as desktop
+import custom_commands.moblinrepo as moblinrepo
+import custom_commands.micboot as micboot
+import custom_commands.partition as partition
+
+class PrepackageSection(kssections.Section):
+ sectionOpen = "%prepackages"
+
+ def handleLine(self, line):
+ if not self.handler:
+ return
+
+ (h, s, t) = line.partition('#')
+ line = h.rstrip()
+
+ self.handler.prepackages.add([line])
+
+ def handleHeader(self, lineno, args):
+ kssections.Section.handleHeader(self, lineno, args)
+
+class AttachmentSection(kssections.Section):
+ sectionOpen = "%attachment"
+
+ def handleLine(self, line):
+ if not self.handler:
+ return
+
+ (h, s, t) = line.partition('#')
+ line = h.rstrip()
+
+ self.handler.attachment.add([line])
+
+ def handleHeader(self, lineno, args):
+ kssections.Section.handleHeader(self, lineno, args)
+
+def apply_wrapper(func):
+ def wrapper(*kargs, **kwargs):
+ try:
+ func(*kargs, **kwargs)
+ except (OSError, IOError, errors.KsError), err:
+ cfgcls = kargs[0].__class__.__name__
+ if msger.ask("Failed to apply %s, skip and continue?" % cfgcls):
+ msger.warning("%s" % err)
+ pass
+ else:
+ # just throw out the exception
+ raise
+ return wrapper
+
+def read_kickstart(path):
+ """Parse a kickstart file and return a KickstartParser instance.
+
+ This is a simple utility function which takes a path to a kickstart file,
+ parses it and returns a pykickstart KickstartParser instance which can
+ be then passed to an ImageCreator constructor.
+
+ If an error occurs, a CreatorError exception is thrown.
+ """
+
+ #version = ksversion.makeVersion()
+ #ks = ksparser.KickstartParser(version)
+
+ using_version = ksversion.DEVEL
+ commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
+ commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
+ commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
+ commandMap[using_version]["part"] = partition.MeeGo_Partition
+ commandMap[using_version]["partition"] = partition.MeeGo_Partition
+ dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
+ dataMap[using_version]["PartData"] = partition.MeeGo_PartData
+ superclass = ksversion.returnClassForVersion(version=using_version)
+
+ class KSHandlers(superclass):
+ def __init__(self, mapping={}):
+ superclass.__init__(self, mapping=commandMap[using_version])
+ self.prepackages = ksparser.Packages()
+ self.attachment = ksparser.Packages()
+
+ ks = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=False)
+ ks.registerSection(PrepackageSection(ks.handler))
+ ks.registerSection(AttachmentSection(ks.handler))
+
+ try:
+ ks.readKickstart(path)
+ except (kserrors.KickstartParseError, kserrors.KickstartError), err:
+ if msger.ask("Errors occured on kickstart file, skip and continue?"):
+ msger.warning("%s" % err)
+ pass
+ else:
+ raise errors.KsError("%s" % err)
+
+ return ks
+
+def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
+ """Construct and return an image name string.
+
+ This is a utility function to help create sensible name and fslabel
+ strings. The name is constructed using the sans-prefix-and-extension
+ kickstart filename and the supplied prefix and suffix.
+
+ If the name exceeds the maxlen length supplied, the prefix is first dropped
+ and then the kickstart filename portion is reduced until it fits. In other
+ words, the suffix takes precedence over the kickstart portion and the
+ kickstart portion takes precedence over the prefix.
+
+ kscfg -- a path to a kickstart file
+ prefix -- a prefix to prepend to the name; defaults to None, which causes
+ no prefix to be used
+ suffix -- a suffix to append to the name; defaults to None, which causes
+ a YYYYMMDDHHMM suffix to be used
+ maxlen -- the maximum length for the returned string; defaults to None,
+ which means there is no restriction on the name length
+
+ Note, if maxlen is less then the len(suffix), you get to keep both pieces.
+
+ """
+ name = os.path.basename(kscfg)
+ idx = name.rfind('.')
+ if idx >= 0:
+ name = name[:idx]
+
+ if prefix is None:
+ prefix = ""
+ if suffix is None:
+ suffix = time.strftime("%Y%m%d%H%M")
+
+ if name.startswith(prefix):
+ name = name[len(prefix):]
+
+ ret = prefix + name + "-" + suffix
+ if not maxlen is None and len(ret) > maxlen:
+ ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
+
+ return ret
+
+class KickstartConfig(object):
+ """A base class for applying kickstart configurations to a system."""
+ def __init__(self, instroot):
+ self.instroot = instroot
+
+ def path(self, subpath):
+ return self.instroot + subpath
+
+ def _check_sysconfig(self):
+ if not os.path.exists(self.path("/etc/sysconfig")):
+ fs.makedirs(self.path("/etc/sysconfig"))
+
+ def chroot(self):
+ os.chroot(self.instroot)
+ os.chdir("/")
+
+ def call(self, args):
+ if not os.path.exists("%s/%s" %(self.instroot, args[0])):
+ raise errors.KsError("Can't find %s in chroot" % args[0])
+ subprocess.call(args, preexec_fn = self.chroot)
+
+ def apply(self):
+ pass
+
+class LanguageConfig(KickstartConfig):
+ """A class to apply a kickstart language configuration to a system."""
+ @apply_wrapper
+ def apply(self, kslang):
+ self._check_sysconfig()
+ if kslang.lang:
+ f = open(self.path("/etc/sysconfig/i18n"), "w+")
+ f.write("LANG=\"" + kslang.lang + "\"\n")
+ f.close()
+
+class KeyboardConfig(KickstartConfig):
+ """A class to apply a kickstart keyboard configuration to a system."""
+ @apply_wrapper
+ def apply(self, kskeyboard):
+ #
+ # FIXME:
+ # should this impact the X keyboard config too?
+ # or do we want to make X be able to do this mapping?
+ #
+ #k = rhpl.keyboard.Keyboard()
+ #if kskeyboard.keyboard:
+ # k.set(kskeyboard.keyboard)
+ #k.write(self.instroot)
+ pass
+
+class TimezoneConfig(KickstartConfig):
+ """A class to apply a kickstart timezone configuration to a system."""
+ @apply_wrapper
+ def apply(self, kstimezone):
+ self._check_sysconfig()
+ tz = kstimezone.timezone or "America/New_York"
+ utc = str(kstimezone.isUtc)
+
+ f = open(self.path("/etc/sysconfig/clock"), "w+")
+ f.write("ZONE=\"" + tz + "\"\n")
+ f.write("UTC=" + utc + "\n")
+ f.close()
+ tz_source = "/usr/share/zoneinfo/%s" % (tz)
+ tz_dest = "/etc/localtime"
+ try:
+ cpcmd = fs.find_binary_inchroot('cp', self.instroot)
+ if cpcmd:
+ self.call([cpcmd, "-f", tz_source, tz_dest])
+ else:
+ cpcmd = fs.find_binary_path('cp')
+ subprocess.call([cpcmd, "-f",
+ self.path(tz_source),
+ self.path(tz_dest)])
+ except (IOError, OSError), (errno, msg):
+ raise errors.KsError("Timezone setting error: %s" % msg)
+
+class AuthConfig(KickstartConfig):
+ """A class to apply a kickstart authconfig configuration to a system."""
+ @apply_wrapper
+ def apply(self, ksauthconfig):
+ auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
+ args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
+ self.call(args + auth.split())
+
+class FirewallConfig(KickstartConfig):
+ """A class to apply a kickstart firewall configuration to a system."""
+ @apply_wrapper
+ def apply(self, ksfirewall):
+ #
+ # FIXME: should handle the rest of the options
+ #
+ if not os.path.exists(self.path("/usr/sbin/lokkit")):
+ return
+ if ksfirewall.enabled:
+ status = "--enabled"
+ else:
+ status = "--disabled"
+
+ self.call(["/usr/sbin/lokkit",
+ "-f", "--quiet", "--nostart", status])
+
+class RootPasswordConfig(KickstartConfig):
+ """A class to apply a kickstart root password configuration to a system."""
+ def unset(self):
+ self.call(["/usr/bin/passwd", "-d", "root"])
+
+ def set_encrypted(self, password):
+ self.call(["/usr/sbin/usermod", "-p", password, "root"])
+
+ def set_unencrypted(self, password):
+ for p in ("/bin/echo", "/usr/sbin/chpasswd"):
+ if not os.path.exists("%s/%s" %(self.instroot, p)):
+ raise errors.KsError("Unable to set unencrypted password due "
+ "to lack of %s" % p)
+
+ p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
+ stdout = subprocess.PIPE,
+ preexec_fn = self.chroot)
+ p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
+ stdin = p1.stdout,
+ stdout = subprocess.PIPE,
+ preexec_fn = self.chroot)
+ p2.communicate()
+
+ @apply_wrapper
+ def apply(self, ksrootpw):
+ if ksrootpw.isCrypted:
+ self.set_encrypted(ksrootpw.password)
+ elif ksrootpw.password != "":
+ self.set_unencrypted(ksrootpw.password)
+ else:
+ self.unset()
+
+class UserConfig(KickstartConfig):
+ def set_empty_passwd(self, user):
+ self.call(["/usr/bin/passwd", "-d", user])
+
+ def set_encrypted_passwd(self, user, password):
+ self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
+
+ def set_unencrypted_passwd(self, user, password):
+ for p in ("/bin/echo", "/usr/sbin/chpasswd"):
+ if not os.path.exists("%s/%s" %(self.instroot, p)):
+ raise errors.KsError("Unable to set unencrypted password due "
+ "to lack of %s" % p)
+
+ p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
+ stdout = subprocess.PIPE,
+ preexec_fn = self.chroot)
+ p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
+ stdin = p1.stdout,
+ stdout = subprocess.PIPE,
+ preexec_fn = self.chroot)
+ p2.communicate()
+
+ def addUser(self, userconfig):
+ args = [ "/usr/sbin/useradd" ]
+ if userconfig.groups:
+ args += [ "--groups", string.join(userconfig.groups, ",") ]
+ if userconfig.name:
+ args.append(userconfig.name)
+ try:
+ dev_null = os.open("/dev/null", os.O_WRONLY)
+ subprocess.call(args,
+ stdout = dev_null,
+ stderr = dev_null,
+ preexec_fn = self.chroot)
+ os.close(dev_null)
+ except:
+ msger.warning('Cannot add user using "useradd"')
+
+ if userconfig.password not in (None, ""):
+ if userconfig.isCrypted:
+ self.set_encrypted_passwd(userconfig.name,
+ userconfig.password)
+ else:
+ self.set_unencrypted_passwd(userconfig.name,
+ userconfig.password)
+ else:
+ self.set_empty_passwd(userconfig.name)
+ else:
+ raise errors.KsError("Invalid kickstart command: %s" \
+ % userconfig.__str__())
+
+ @apply_wrapper
+ def apply(self, user):
+ for userconfig in user.userList:
+ try:
+ self.addUser(userconfig)
+ except:
+ raise
+
+class ServicesConfig(KickstartConfig):
+ """A class to apply a kickstart services configuration to a system."""
+ @apply_wrapper
+ def apply(self, ksservices):
+ if not os.path.exists(self.path("/sbin/chkconfig")):
+ return
+ for s in ksservices.enabled:
+ self.call(["/sbin/chkconfig", s, "on"])
+ for s in ksservices.disabled:
+ self.call(["/sbin/chkconfig", s, "off"])
+
+class XConfig(KickstartConfig):
+ """A class to apply a kickstart X configuration to a system."""
+ @apply_wrapper
+ def apply(self, ksxconfig):
+ if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
+ f = open(self.path("/etc/inittab"), "rw+")
+ buf = f.read()
+ buf = buf.replace("id:3:initdefault", "id:5:initdefault")
+ f.seek(0)
+ f.write(buf)
+ f.close()
+ if ksxconfig.defaultdesktop:
+ self._check_sysconfig()
+ f = open(self.path("/etc/sysconfig/desktop"), "w")
+ f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
+ f.close()
+
+class DesktopConfig(KickstartConfig):
+ """A class to apply a kickstart desktop configuration to a system."""
+ @apply_wrapper
+ def apply(self, ksdesktop):
+ if ksdesktop.defaultdesktop:
+ self._check_sysconfig()
+ f = open(self.path("/etc/sysconfig/desktop"), "w")
+ f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
+ f.close()
+ if os.path.exists(self.path("/etc/gdm/custom.conf")):
+ f = open(self.path("/etc/skel/.dmrc"), "w")
+ f.write("[Desktop]\n")
+ f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
+ f.close()
+ if ksdesktop.session:
+ if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
+ f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
+ f.write("session="+ksdesktop.session.lower()+"\n")
+ f.close()
+ if ksdesktop.autologinuser:
+ self._check_sysconfig()
+ f = open(self.path("/etc/sysconfig/desktop"), "a+")
+ f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
+ f.close()
+ if os.path.exists(self.path("/etc/gdm/custom.conf")):
+ f = open(self.path("/etc/gdm/custom.conf"), "w")
+ f.write("[daemon]\n")
+ f.write("AutomaticLoginEnable=true\n")
+ f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
+ f.close()
+
+class MoblinRepoConfig(KickstartConfig):
+ """A class to apply a kickstart desktop configuration to a system."""
+ def __create_repo_section(self, repo, type, fd):
+ baseurl = None
+ mirrorlist = None
+ reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
+ reponame = repo.name + reposuffix[type]
+ if type == "base":
+ if repo.baseurl:
+ baseurl = repo.baseurl
+ if repo.mirrorlist:
+ mirrorlist = repo.mirrorlist
+
+ elif type == "debuginfo":
+ if repo.baseurl:
+ if repo.baseurl.endswith("/"):
+ baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
+ else:
+ baseurl = os.path.dirname(repo.baseurl)
+ baseurl += "/debug"
+
+ if repo.mirrorlist:
+ variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
+ mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
+ mirrorlist += "debug" + "-" + variant
+
+ elif type == "source":
+ if repo.baseurl:
+ if repo.baseurl.endswith("/"):
+ baseurl = os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(repo.baseurl)))
+ else:
+ baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
+ baseurl += "/source"
+
+ if repo.mirrorlist:
+ variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
+ mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
+ mirrorlist += "source" + "-" + variant
+
+ fd.write("[" + reponame + "]\n")
+ fd.write("name=" + reponame + "\n")
+ fd.write("failovermethod=priority\n")
+ if baseurl:
+ fd.write("baseurl=" + baseurl + "\n")
+ if mirrorlist:
+ fd.write("mirrorlist=" + mirrorlist + "\n")
+ """ Skip saving proxy settings """
+ #if repo.proxy:
+ # fd.write("proxy=" + repo.proxy + "\n")
+ #if repo.proxy_username:
+ # fd.write("proxy_username=" + repo.proxy_username + "\n")
+ #if repo.proxy_password:
+ # fd.write("proxy_password=" + repo.proxy_password + "\n")
+ if repo.gpgkey:
+ fd.write("gpgkey=" + repo.gpgkey + "\n")
+ fd.write("gpgcheck=1\n")
+ else:
+ fd.write("gpgcheck=0\n")
+ if type == "source" or type == "debuginfo" or repo.disable:
+ fd.write("enabled=0\n")
+ else:
+ fd.write("enabled=1\n")
+ fd.write("\n")
+
+ def __create_repo_file(self, repo, repodir):
+ fs.makedirs(self.path(repodir))
+ f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
+ self.__create_repo_section(repo, "base", f)
+ if repo.debuginfo:
+ self.__create_repo_section(repo, "debuginfo", f)
+ if repo.source:
+ self.__create_repo_section(repo, "source", f)
+ f.close()
+
+ @apply_wrapper
+ def apply(self, ksrepo, repodata):
+ for repo in ksrepo.repoList:
+ if repo.save:
+ #self.__create_repo_file(repo, "/etc/yum.repos.d")
+ self.__create_repo_file(repo, "/etc/zypp/repos.d")
+ """ Import repo gpg keys """
+ if repodata:
+ for repo in repodata:
+ if repo['repokey']:
+ runner.quiet(['rpm',
+ "--root=%s" % self.instroot,
+ "--import",
+ repo['repokey']])
+
+class RPMMacroConfig(KickstartConfig):
+ """A class to apply the specified rpm macros to the filesystem"""
+ @apply_wrapper
+ def apply(self, ks):
+ if not ks:
+ return
+ if not os.path.exists(self.path("/etc/rpm")):
+ os.mkdir(self.path("/etc/rpm"))
+ f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
+ if exclude_docs(ks):
+ f.write("%_excludedocs 1\n")
+ f.write("%__file_context_path %{nil}\n")
+ if inst_langs(ks) != None:
+ f.write("%_install_langs ")
+ f.write(inst_langs(ks))
+ f.write("\n")
+ f.close()
+
+class NetworkConfig(KickstartConfig):
+ """A class to apply a kickstart network configuration to a system."""
+ def write_ifcfg(self, network):
+ p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
+
+ f = file(p, "w+")
+ os.chmod(p, 0644)
+
+ f.write("DEVICE=%s\n" % network.device)
+ f.write("BOOTPROTO=%s\n" % network.bootProto)
+
+ if network.bootProto.lower() == "static":
+ if network.ip:
+ f.write("IPADDR=%s\n" % network.ip)
+ if network.netmask:
+ f.write("NETMASK=%s\n" % network.netmask)
+
+ if network.onboot:
+ f.write("ONBOOT=on\n")
+ else:
+ f.write("ONBOOT=off\n")
+
+ if network.essid:
+ f.write("ESSID=%s\n" % network.essid)
+
+ if network.ethtool:
+ if network.ethtool.find("autoneg") == -1:
+ network.ethtool = "autoneg off " + network.ethtool
+ f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
+
+ if network.bootProto.lower() == "dhcp":
+ if network.hostname:
+ f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
+ if network.dhcpclass:
+ f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
+
+ if network.mtu:
+ f.write("MTU=%s\n" % network.mtu)
+
+ f.close()
+
+ def write_wepkey(self, network):
+ if not network.wepkey:
+ return
+
+ p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
+ f = file(p, "w+")
+ os.chmod(p, 0600)
+ f.write("KEY=%s\n" % network.wepkey)
+ f.close()
+
+ def write_sysconfig(self, useipv6, hostname, gateway):
+ path = self.path("/etc/sysconfig/network")
+ f = file(path, "w+")
+ os.chmod(path, 0644)
+
+ f.write("NETWORKING=yes\n")
+
+ if useipv6:
+ f.write("NETWORKING_IPV6=yes\n")
+ else:
+ f.write("NETWORKING_IPV6=no\n")
+
+ if hostname:
+ f.write("HOSTNAME=%s\n" % hostname)
+ else:
+ f.write("HOSTNAME=localhost.localdomain\n")
+
+ if gateway:
+ f.write("GATEWAY=%s\n" % gateway)
+
+ f.close()
+
+ def write_hosts(self, hostname):
+ localline = ""
+ if hostname and hostname != "localhost.localdomain":
+ localline += hostname + " "
+ l = hostname.split(".")
+ if len(l) > 1:
+ localline += l[0] + " "
+ localline += "localhost.localdomain localhost"
+
+ path = self.path("/etc/hosts")
+ f = file(path, "w+")
+ os.chmod(path, 0644)
+ f.write("127.0.0.1\t\t%s\n" % localline)
+ f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
+ f.close()
+
+ def write_resolv(self, nodns, nameservers):
+ if nodns or not nameservers:
+ return
+
+ path = self.path("/etc/resolv.conf")
+ f = file(path, "w+")
+ os.chmod(path, 0644)
+
+ for ns in (nameservers):
+ if ns:
+ f.write("nameserver %s\n" % ns)
+
+ f.close()
+
+ @apply_wrapper
+ def apply(self, ksnet):
+ fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
+
+ useipv6 = False
+ nodns = False
+ hostname = None
+ gateway = None
+ nameservers = None
+
+ for network in ksnet.network:
+ if not network.device:
+ raise errors.KsError("No --device specified with "
+ "network kickstart command")
+
+ if (network.onboot and network.bootProto.lower() != "dhcp" and
+ not (network.ip and network.netmask)):
+ raise errors.KsError("No IP address and/or netmask "
+ "specified with static "
+ "configuration for '%s'" %
+ network.device)
+
+ self.write_ifcfg(network)
+ self.write_wepkey(network)
+
+ if network.ipv6:
+ useipv6 = True
+ if network.nodns:
+ nodns = True
+
+ if network.hostname:
+ hostname = network.hostname
+ if network.gateway:
+ gateway = network.gateway
+
+ if network.nameserver:
+ nameservers = network.nameserver.split(",")
+
+ self.write_sysconfig(useipv6, hostname, gateway)
+ self.write_hosts(hostname)
+ self.write_resolv(nodns, nameservers)
+
+
+def get_image_size(ks, default = None):
+ __size = 0
+ for p in ks.handler.partition.partitions:
+ if p.mountpoint == "/" and p.size:
+ __size = p.size
+ if __size > 0:
+ return int(__size) * 1024L * 1024L
+ else:
+ return default
+
+def get_image_fstype(ks, default = None):
+ for p in ks.handler.partition.partitions:
+ if p.mountpoint == "/" and p.fstype:
+ return p.fstype
+ return default
+
+def get_image_fsopts(ks, default = None):
+ for p in ks.handler.partition.partitions:
+ if p.mountpoint == "/" and p.fsopts:
+ return p.fsopts
+ return default
+
+def get_modules(ks):
+ devices = []
+ if isinstance(ks.handler.device, kscommands.device.FC3_Device):
+ devices.append(ks.handler.device)
+ else:
+ devices.extend(ks.handler.device.deviceList)
+
+ modules = []
+ for device in devices:
+ if not device.moduleName:
+ continue
+ modules.extend(device.moduleName.split(":"))
+
+ return modules
+
+def get_timeout(ks, default = None):
+ if not hasattr(ks.handler.bootloader, "timeout"):
+ return default
+ if ks.handler.bootloader.timeout is None:
+ return default
+ return int(ks.handler.bootloader.timeout)
+
+def get_kernel_args(ks, default = "ro liveimg"):
+ if not hasattr(ks.handler.bootloader, "appendLine"):
+ return default
+ if ks.handler.bootloader.appendLine is None:
+ return default
+ return "%s %s" %(default, ks.handler.bootloader.appendLine)
+
+def get_menu_args(ks, default = ""):
+ if not hasattr(ks.handler.bootloader, "menus"):
+ return default
+ if ks.handler.bootloader.menus in (None, ""):
+ return default
+ return "%s" % ks.handler.bootloader.menus
+
+def get_default_kernel(ks, default = None):
+ if not hasattr(ks.handler.bootloader, "default"):
+ return default
+ if not ks.handler.bootloader.default:
+ return default
+ return ks.handler.bootloader.default
+
+def get_repos(ks, repo_urls = {}):
+ repos = {}
+ for repo in ks.handler.repo.repoList:
+ inc = []
+ if hasattr(repo, "includepkgs"):
+ inc.extend(repo.includepkgs)
+
+ exc = []
+ if hasattr(repo, "excludepkgs"):
+ exc.extend(repo.excludepkgs)
+
+ baseurl = repo.baseurl
+ mirrorlist = repo.mirrorlist
+
+ if repo.name in repo_urls:
+ baseurl = repo_urls[repo.name]
+ mirrorlist = None
+
+ if repos.has_key(repo.name):
+ msger.warning("Overriding already specified repo %s" %(repo.name,))
+
+ proxy = None
+ if hasattr(repo, "proxy"):
+ proxy = repo.proxy
+ proxy_username = None
+ if hasattr(repo, "proxy_username"):
+ proxy_username = repo.proxy_username
+ proxy_password = None
+ if hasattr(repo, "proxy_password"):
+ proxy_password = repo.proxy_password
+ if hasattr(repo, "debuginfo"):
+ debuginfo = repo.debuginfo
+ if hasattr(repo, "source"):
+ source = repo.source
+ if hasattr(repo, "gpgkey"):
+ gpgkey = repo.gpgkey
+ if hasattr(repo, "disable"):
+ disable = repo.disable
+ ssl_verify = True
+ if hasattr(repo, "ssl_verify"):
+ ssl_verify = repo.ssl_verify == "yes"
+ cost = None
+ if hasattr(repo, "cost"):
+ cost = repo.cost
+ priority = None
+ if hasattr(repo, "priority"):
+ priority = repo.priority
+
+ repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
+ proxy, proxy_username, proxy_password, debuginfo,
+ source, gpgkey, disable, ssl_verify, cost, priority)
+
+ return repos.values()
+
+def convert_method_to_repo(ks):
+ try:
+ ks.handler.repo.methodToRepo()
+ except (AttributeError, kserrors.KickstartError):
+ pass
+
+def get_attachment(ks, required = []):
+ return ks.handler.attachment.packageList + required
+
+def get_pre_packages(ks, required = []):
+ return ks.handler.prepackages.packageList + required
+
+def get_packages(ks, required = []):
+ return ks.handler.packages.packageList + required
+
+def get_groups(ks, required = []):
+ return ks.handler.packages.groupList + required
+
+def get_excluded(ks, required = []):
+ return ks.handler.packages.excludedList + required
+
+def get_partitions(ks, required = []):
+ return ks.handler.partition.partitions
+
+def ignore_missing(ks):
+ return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
+
+def exclude_docs(ks):
+ return ks.handler.packages.excludeDocs
+
+def inst_langs(ks):
+ if hasattr(ks.handler.packages, "instLange"):
+ return ks.handler.packages.instLange
+ elif hasattr(ks.handler.packages, "instLangs"):
+ return ks.handler.packages.instLangs
+ return ""
+
+def get_post_scripts(ks):
+ scripts = []
+ for s in ks.handler.scripts:
+ if s.type != ksparser.KS_SCRIPT_POST:
+ continue
+ scripts.append(s)
+ return scripts
+
+def add_repo(ks, repostr):
+ args = repostr.split()
+ repoobj = ks.handler.repo.parse(args[1:])
+ if repoobj and repoobj not in ks.handler.repo.repoList:
+ ks.handler.repo.repoList.append(repoobj)
+
+def remove_all_repos(ks):
+ while len(ks.handler.repo.repoList) != 0:
+ del ks.handler.repo.repoList[0]
+
+def remove_duplicate_repos(ks):
+ i = 0
+ j = i + 1
+ while True:
+ if len(ks.handler.repo.repoList) < 2:
+ break
+ if i >= len(ks.handler.repo.repoList) - 1:
+ break
+ name = ks.handler.repo.repoList[i].name
+ baseurl = ks.handler.repo.repoList[i].baseurl
+ if j < len(ks.handler.repo.repoList):
+ if (ks.handler.repo.repoList[j].name == name or \
+ ks.handler.repo.repoList[j].baseurl == baseurl):
+ del ks.handler.repo.repoList[j]
+ else:
+ j += 1
+ if j >= len(ks.handler.repo.repoList):
+ i += 1
+ j = i + 1
+ else:
+ i += 1
+ j = i + 1
+
+def resolve_groups(creatoropts, repometadata):
+ iszypp = False
+ if 'zypp' == creatoropts['pkgmgr']:
+ iszypp = True
+ ks = creatoropts['ks']
+
+ for repo in repometadata:
+ """ Mustn't replace group with package list if repo is ready for the
+ corresponding package manager.
+ """
+
+ if iszypp and repo["patterns"]:
+ continue
+ if not iszypp and repo["comps"]:
+ continue
+
+ # But we also must handle such cases, use zypp but repo only has comps,
+ # use yum but repo only has patterns, use zypp but use_comps is true,
+ # use yum but use_comps is false.
+ groupfile = None
+ if iszypp and repo["comps"]:
+ groupfile = repo["comps"]
+ get_pkglist_handler = misc.get_pkglist_in_comps
+ if not iszypp and repo["patterns"]:
+ groupfile = repo["patterns"]
+ get_pkglist_handler = misc.get_pkglist_in_patterns
+
+ if groupfile:
+ i = 0
+ while True:
+ if i >= len(ks.handler.packages.groupList):
+ break
+ pkglist = get_pkglist_handler(
+ ks.handler.packages.groupList[i].name,
+ groupfile)
+ if pkglist:
+ del ks.handler.packages.groupList[i]
+ for pkg in pkglist:
+ if pkg not in ks.handler.packages.packageList:
+ ks.handler.packages.packageList.append(pkg)
+ else:
+ i = i + 1
diff --git a/mic/kickstart/custom_commands/__init__.py b/mic/kickstart/custom_commands/__init__.py
new file mode 100644
index 0000000..dcfb56e
--- /dev/null
+++ b/mic/kickstart/custom_commands/__init__.py
@@ -0,0 +1,10 @@
+import desktop
+import moblinrepo
+import partition
+
+__all__ = (
+ "Moblin_Desktop",
+ "Moblin_Repo",
+ "Moblin_RepoData",
+ "MeeGo_Partition",
+)
diff --git a/mic/kickstart/custom_commands/desktop.py b/mic/kickstart/custom_commands/desktop.py
new file mode 100644
index 0000000..ab3f82f
--- /dev/null
+++ b/mic/kickstart/custom_commands/desktop.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2008, 2009, 2010 Intel, Inc.
+#
+# Yi Yang <yi.y.yang@intel.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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+
+class Moblin_Desktop(KickstartCommand):
+ def __init__(self, writePriority=0,
+ defaultdesktop=None,
+ defaultdm=None,
+ autologinuser="meego",
+ session=None):
+
+ KickstartCommand.__init__(self, writePriority)
+
+ self.__new_version = False
+ self.op = self._getParser()
+
+ self.defaultdesktop = defaultdesktop
+ self.autologinuser = autologinuser
+ self.defaultdm = defaultdm
+ self.session = session
+
+ def __str__(self):
+ retval = ""
+
+ if self.defaultdesktop != None:
+ retval += " --defaultdesktop=%s" % self.defaultdesktop
+ if self.session != None:
+ retval += " --session=\"%s\"" % self.session
+ if self.autologinuser != None:
+ retval += " --autologinuser=%s" % self.autologinuser
+ if self.defaultdm != None:
+ retval += " --defaultdm=%s" % self.defaultdm
+
+ if retval != "":
+ retval = "# Default Desktop Settings\ndesktop %s\n" % retval
+
+ return retval
+
+ def _getParser(self):
+ try:
+ op = KSOptionParser(lineno=self.lineno)
+ except TypeError:
+ # the latest version has not lineno argument
+ op = KSOptionParser()
+ self.__new_version = True
+
+ op.add_option("--defaultdesktop", dest="defaultdesktop",
+ action="store",
+ type="string",
+ nargs=1)
+ op.add_option("--autologinuser", dest="autologinuser",
+ action="store",
+ type="string",
+ nargs=1)
+ op.add_option("--defaultdm", dest="defaultdm",
+ action="store",
+ type="string",
+ nargs=1)
+ op.add_option("--session", dest="session",
+ action="store",
+ type="string",
+ nargs=1)
+ return op
+
+ def parse(self, args):
+ if self.__new_version:
+ (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
+ else:
+ (opts, extra) = self.op.parse_args(args=args)
+
+ if extra:
+ m = _("Unexpected arguments to %(command)s command: %(options)s") \
+ % {"command": "desktop", "options": extra}
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg=m)
+
+ self._setToSelf(self.op, opts)
diff --git a/mic/kickstart/custom_commands/micboot.py b/mic/kickstart/custom_commands/micboot.py
new file mode 100644
index 0000000..33071f7
--- /dev/null
+++ b/mic/kickstart/custom_commands/micboot.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2008, 2009, 2010 Intel, Inc.
+#
+# Anas Nashif
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+from pykickstart.commands.bootloader import *
+
+class Moblin_Bootloader(F8_Bootloader):
+ def __init__(self, writePriority=10, appendLine="", driveorder=None,
+ forceLBA=False, location="", md5pass="", password="",
+ upgrade=False, menus=""):
+ F8_Bootloader.__init__(self, writePriority, appendLine, driveorder,
+ forceLBA, location, md5pass, password, upgrade)
+
+ self.menus = ""
+
+ def _getArgsAsStr(self):
+ ret = F8_Bootloader._getArgsAsStr(self)
+
+ if self.menus == "":
+ ret += " --menus=%s" %(self.menus,)
+ return ret
+
+ def _getParser(self):
+ op = F8_Bootloader._getParser(self)
+ op.add_option("--menus", dest="menus")
+ return op
+
diff --git a/mic/kickstart/custom_commands/moblinrepo.py b/mic/kickstart/custom_commands/moblinrepo.py
new file mode 100644
index 0000000..f2da6fe
--- /dev/null
+++ b/mic/kickstart/custom_commands/moblinrepo.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2008, 2009, 2010 Intel, Inc.
+#
+# Yi Yang <yi.y.yang@intel.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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from pykickstart.base import *
+from pykickstart.errors import *
+from pykickstart.options import *
+from pykickstart.commands.repo import *
+
+class Moblin_RepoData(F8_RepoData):
+ def __init__(self, baseurl="", mirrorlist="", name="", priority=None,
+ includepkgs=[], excludepkgs=[], save=False, proxy=None,
+ proxy_username=None, proxy_password=None, debuginfo=False,
+ source=False, gpgkey=None, disable=False, ssl_verify="yes"):
+ F8_RepoData.__init__(self, baseurl=baseurl, mirrorlist=mirrorlist,
+ name=name, includepkgs=includepkgs,
+ excludepkgs=excludepkgs)
+ self.save = save
+ self.proxy = proxy
+ self.proxy_username = proxy_username
+ self.proxy_password = proxy_password
+ self.debuginfo = debuginfo
+ self.disable = disable
+ self.source = source
+ self.gpgkey = gpgkey
+ self.ssl_verify = ssl_verify.lower()
+ self.priority = priority
+
+ def _getArgsAsStr(self):
+ retval = F8_RepoData._getArgsAsStr(self)
+
+ if self.save:
+ retval += " --save"
+ if self.proxy:
+ retval += " --proxy=%s" % self.proxy
+ if self.proxy_username:
+ retval += " --proxyuser=%s" % self.proxy_username
+ if self.proxy_password:
+ retval += " --proxypasswd=%s" % self.proxy_password
+ if self.debuginfo:
+ retval += " --debuginfo"
+ if self.source:
+ retval += " --source"
+ if self.gpgkey:
+ retval += " --gpgkey=%s" % self.gpgkey
+ if self.disable:
+ retval += " --disable"
+ if self.ssl_verify:
+ retval += " --ssl_verify=%s" % self.ssl_verify
+ if self.priority:
+ retval += " --priority=%s" % self.priority
+
+ return retval
+
+class Moblin_Repo(F8_Repo):
+ def __init__(self, writePriority=0, repoList=None):
+ F8_Repo.__init__(self, writePriority, repoList)
+
+ def __str__(self):
+ retval = ""
+ for repo in self.repoList:
+ retval += repo.__str__()
+
+ return retval
+
+ def _getParser(self):
+ def list_cb (option, opt_str, value, parser):
+ for d in value.split(','):
+ parser.values.ensure_value(option.dest, []).append(d)
+
+ op = F8_Repo._getParser(self)
+ op.add_option("--save", action="store_true", dest="save",
+ default=False)
+ op.add_option("--proxy", type="string", action="store", dest="proxy",
+ default=None, nargs=1)
+ op.add_option("--proxyuser", type="string", action="store",
+ dest="proxy_username", default=None, nargs=1)
+ op.add_option("--proxypasswd", type="string", action="store",
+ dest="proxy_password", default=None, nargs=1)
+ op.add_option("--debuginfo", action="store_true", dest="debuginfo",
+ default=False)
+ op.add_option("--source", action="store_true", dest="source",
+ default=False)
+ op.add_option("--disable", action="store_true", dest="disable",
+ default=False)
+ op.add_option("--gpgkey", type="string", action="store", dest="gpgkey",
+ default=None, nargs=1)
+ op.add_option("--ssl_verify", type="string", action="store",
+ dest="ssl_verify", default="yes")
+ op.add_option("--priority", type="int", action="store", dest="priority",
+ default=None)
+ return op
diff --git a/mic/kickstart/custom_commands/partition.py b/mic/kickstart/custom_commands/partition.py
new file mode 100644
index 0000000..7b78319
--- /dev/null
+++ b/mic/kickstart/custom_commands/partition.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python -tt
+#
+# Marko Saukko <marko.saukko@cybercom.com>
+#
+# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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 pykickstart.commands.partition import *
+
+class MeeGo_PartData(FC4_PartData):
+ removedKeywords = FC4_PartData.removedKeywords
+ removedAttrs = FC4_PartData.removedAttrs
+
+ def __init__(self, *args, **kwargs):
+ FC4_PartData.__init__(self, *args, **kwargs)
+ self.deleteRemovedAttrs()
+ self.align = kwargs.get("align", None)
+
+ def _getArgsAsStr(self):
+ retval = FC4_PartData._getArgsAsStr(self)
+
+ if self.align:
+ retval += " --align"
+
+ return retval
+
+class MeeGo_Partition(FC4_Partition):
+ removedKeywords = FC4_Partition.removedKeywords
+ removedAttrs = FC4_Partition.removedAttrs
+
+ def _getParser(self):
+ op = FC4_Partition._getParser(self)
+ # The alignment value is given in kBytes. e.g., value 8 means that
+ # the partition is aligned to start from 8096 byte boundary.
+ op.add_option("--align", type="int", action="store", dest="align",
+ default=None)
+ return op
diff --git a/mic/msger.py b/mic/msger.py
new file mode 100644
index 0000000..ab94e9e
--- /dev/null
+++ b/mic/msger.py
@@ -0,0 +1,309 @@
+#!/usr/bin/python -tt
+# vim: ai ts=4 sts=4 et sw=4
+#
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os,sys
+import re
+import time
+
+__ALL__ = ['set_mode',
+ 'get_loglevel',
+ 'set_loglevel',
+ 'set_logfile',
+ 'raw',
+ 'debug',
+ 'verbose',
+ 'info',
+ 'warning',
+ 'error',
+ 'ask',
+ 'pause',
+ ]
+
+# COLORs in ANSI
+INFO_COLOR = 32 # green
+WARN_COLOR = 33 # yellow
+ERR_COLOR = 31 # red
+ASK_COLOR = 34 # blue
+NO_COLOR = 0
+
+HOST_TIMEZONE = time.timezone
+
+PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S)
+
+INTERACTIVE = True
+
+LOG_LEVEL = 1
+LOG_LEVELS = {
+ 'quiet': 0,
+ 'normal': 1,
+ 'verbose': 2,
+ 'debug': 3,
+ 'never': 4,
+ }
+
+LOG_FILE_FP = None
+LOG_CONTENT = ''
+CATCHERR_BUFFILE_FD = -1
+CATCHERR_BUFFILE_PATH = None
+CATCHERR_SAVED_2 = -1
+
+def _general_print(head, color, msg = None, stream = None, level = 'normal'):
+ global LOG_CONTENT
+ if not stream:
+ stream = sys.stdout
+
+ if LOG_LEVELS[level] > LOG_LEVEL:
+ # skip
+ return
+
+ # encode raw 'unicode' str to utf8 encoded str
+ if msg and isinstance(msg, unicode):
+ msg = msg.encode('utf-8', 'ignore')
+
+ errormsg = ''
+ if CATCHERR_BUFFILE_FD > 0:
+ size = os.lseek(CATCHERR_BUFFILE_FD , 0, os.SEEK_END)
+ os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET)
+ errormsg = os.read(CATCHERR_BUFFILE_FD, size)
+ os.ftruncate(CATCHERR_BUFFILE_FD, 0)
+
+ # append error msg to LOG
+ if errormsg:
+ LOG_CONTENT += errormsg
+
+ # append normal msg to LOG
+ save_msg = msg.strip() if msg else None
+ if save_msg:
+ global HOST_TIMEZONE
+ timestr = time.strftime("[%m/%d %H:%M:%S] ",
+ time.gmtime(time.time() - HOST_TIMEZONE))
+ LOG_CONTENT += timestr + save_msg + '\n'
+
+ if errormsg:
+ _color_print('', NO_COLOR, errormsg, stream, level)
+
+ _color_print(head, color, msg, stream, level)
+
+def _color_print(head, color, msg, stream, level):
+ colored = True
+ if color == NO_COLOR or \
+ not stream.isatty() or \
+ os.getenv('ANSI_COLORS_DISABLED') is not None:
+ colored = False
+
+ if head.startswith('\r'):
+ # need not \n at last
+ newline = False
+ else:
+ newline = True
+
+ if colored:
+ head = '\033[%dm%s:\033[0m ' %(color, head)
+ if not newline:
+ # ESC cmd to clear line
+ head = '\033[2K' + head
+ else:
+ if head:
+ head += ': '
+ if head.startswith('\r'):
+ head = head.lstrip()
+ newline = True
+
+ if msg is not None:
+ if isinstance(msg, unicode):
+ msg = msg.encode('utf8', 'ignore')
+
+ stream.write('%s%s' % (head, msg))
+ if newline:
+ stream.write('\n')
+
+ stream.flush()
+
+def _color_perror(head, color, msg, level = 'normal'):
+ if CATCHERR_BUFFILE_FD > 0:
+ _general_print(head, color, msg, sys.stdout, level)
+ else:
+ _general_print(head, color, msg, sys.stderr, level)
+
+def _split_msg(head, msg):
+ if isinstance(msg, list):
+ msg = '\n'.join(map(str, msg))
+
+ if msg.startswith('\n'):
+ # means print \n at first
+ msg = msg.lstrip()
+ head = '\n' + head
+
+ elif msg.startswith('\r'):
+ # means print \r at first
+ msg = msg.lstrip()
+ head = '\r' + head
+
+ m = PREFIX_RE.match(msg)
+ if m:
+ head += ' <%s>' % m.group(1)
+ msg = m.group(2)
+
+ return head, msg
+
+def get_loglevel():
+ return (k for k,v in LOG_LEVELS.items() if v==LOG_LEVEL).next()
+
+def set_loglevel(level):
+ global LOG_LEVEL
+ if level not in LOG_LEVELS:
+ # no effect
+ return
+
+ LOG_LEVEL = LOG_LEVELS[level]
+
+def set_interactive(mode=True):
+ global INTERACTIVE
+ if mode:
+ INTERACTIVE = True
+ else:
+ INTERACTIVE = False
+
+def raw(msg=''):
+ _general_print('', NO_COLOR, msg)
+
+def info(msg):
+ head, msg = _split_msg('Info', msg)
+ _general_print(head, INFO_COLOR, msg)
+
+def verbose(msg):
+ head, msg = _split_msg('Verbose', msg)
+ _general_print(head, INFO_COLOR, msg, level = 'verbose')
+
+def warning(msg):
+ head, msg = _split_msg('Warning', msg)
+ _color_perror(head, WARN_COLOR, msg)
+
+def debug(msg):
+ head, msg = _split_msg('Debug', msg)
+ _color_perror(head, ERR_COLOR, msg, level = 'debug')
+
+def error(msg):
+ head, msg = _split_msg('Error', msg)
+ _color_perror(head, ERR_COLOR, msg)
+ sys.exit(1)
+
+def ask(msg, default=True):
+ _general_print('\rQ', ASK_COLOR, '')
+ try:
+ if default:
+ msg += '(Y/n) '
+ else:
+ msg += '(y/N) '
+ if INTERACTIVE:
+ while True:
+ repl = raw_input(msg)
+ if repl.lower() == 'y':
+ return True
+ elif repl.lower() == 'n':
+ return False
+ elif not repl.strip():
+ # <Enter>
+ return default
+
+ # else loop
+ else:
+ if default:
+ msg += ' Y'
+ else:
+ msg += ' N'
+ _general_print('', NO_COLOR, msg)
+
+ return default
+ except KeyboardInterrupt:
+ sys.stdout.write('\n')
+ sys.exit(2)
+
+def choice(msg, choices, default=0):
+ if default >= len(choices):
+ return None
+ _general_print('\rQ', ASK_COLOR, '')
+ try:
+ msg += " [%s] " % '/'.join(choices)
+ if INTERACTIVE:
+ while True:
+ repl = raw_input(msg)
+ if repl in choices:
+ return repl
+ elif not repl.strip():
+ return choices[default]
+ else:
+ msg += choices[default]
+ _general_print('', NO_COLOR, msg)
+
+ return choices[default]
+ except KeyboardInterrupt:
+ sys.stdout.write('\n')
+ sys.exit(2)
+
+def pause(msg=None):
+ if INTERACTIVE:
+ _general_print('\rQ', ASK_COLOR, '')
+ if msg is None:
+ msg = 'press <ENTER> to continue ...'
+ raw_input(msg)
+
+def set_logfile(fpath):
+ global LOG_FILE_FP
+
+ def _savelogf():
+ if LOG_FILE_FP:
+ if not os.path.exists(os.path.dirname(LOG_FILE_FP)):
+ os.makedirs(os.path.dirname(LOG_FILE_FP))
+ fp = open(LOG_FILE_FP, 'w')
+ fp.write(LOG_CONTENT)
+ fp.close()
+
+ if LOG_FILE_FP is not None:
+ warning('duplicate log file configuration')
+
+ LOG_FILE_FP = os.path.abspath(os.path.expanduser(fpath))
+
+ import atexit
+ atexit.register(_savelogf)
+
+def enable_logstderr(fpath):
+ global CATCHERR_BUFFILE_FD
+ global CATCHERR_BUFFILE_PATH
+ global CATCHERR_SAVED_2
+
+ if os.path.exists(fpath):
+ os.remove(fpath)
+ CATCHERR_BUFFILE_PATH = fpath
+ CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT)
+ CATCHERR_SAVED_2 = os.dup(2)
+ os.dup2(CATCHERR_BUFFILE_FD, 2)
+
+def disable_logstderr():
+ global CATCHERR_BUFFILE_FD
+ global CATCHERR_BUFFILE_PATH
+ global CATCHERR_SAVED_2
+
+ raw(msg = None) # flush message buffer and print it.
+ os.dup2(CATCHERR_SAVED_2, 2)
+ os.close(CATCHERR_SAVED_2)
+ os.close(CATCHERR_BUFFILE_FD)
+ os.unlink(CATCHERR_BUFFILE_PATH)
+ CATCHERR_BUFFILE_FD = -1
+ CATCHERR_BUFFILE_PATH = None
+ CATCHERR_SAVED_2 = -1
diff --git a/mic/plugin.py b/mic/plugin.py
new file mode 100644
index 0000000..41c5665
--- /dev/null
+++ b/mic/plugin.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys
+import msger
+import pluginbase
+from mic.conf import configmgr
+from mic.utils import errors
+
+__ALL__ = ['PluginMgr', 'pluginmgr']
+
+PLUGIN_TYPES = ["imager", "backend"] # TODO "hook"
+
+class PluginMgr(object):
+ plugin_dirs = {}
+
+ # make the manager class as singleton
+ _instance = None
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = super(PluginMgr, cls).__new__(cls, *args, **kwargs)
+
+ return cls._instance
+
+ def __init__(self):
+ self.plugin_dir = configmgr.common['plugin_dir']
+
+ def append_dirs(self, dirs):
+ for path in dirs:
+ self._add_plugindir(path)
+
+ # load all the plugins AGAIN
+ self._load_all()
+
+ def _add_plugindir(self, path):
+ path = os.path.abspath(os.path.expanduser(path))
+
+ if not os.path.isdir(path):
+ msger.warning("Plugin dir is not a directory or does not exist: %s"\
+ % path)
+ return
+
+ if path not in self.plugin_dirs:
+ self.plugin_dirs[path] = False
+ # the value True/False means "loaded"
+
+ def _load_all(self):
+ for (pdir, loaded) in self.plugin_dirs.iteritems():
+ if loaded: continue
+
+ sys.path.insert(0, pdir)
+ for mod in [x[:-3] for x in os.listdir(pdir) if x.endswith(".py")]:
+ if mod and mod != '__init__':
+ if mod in sys.modules:
+ #self.plugin_dirs[pdir] = True
+ msger.warning("Module %s already exists, skip" % mod)
+ else:
+ try:
+ pymod = __import__(mod)
+ self.plugin_dirs[pdir] = True
+ msger.debug("Plugin module %s:%s imported"\
+ % (mod, pymod.__file__))
+ except ImportError, e:
+ msger.warning('Loading failed, skip plugin %s/%s'\
+ % (os.path.basename(pdir), mod))
+
+ del(sys.path[0])
+
+ def get_plugins(self, ptype):
+ """ the return value is dict of name:class pairs """
+
+ if ptype not in PLUGIN_TYPES:
+ raise errors.CreatorError('%s is not valid plugin type' % ptype)
+
+ self._add_plugindir(os.path.join(self.plugin_dir, ptype))
+ self._load_all()
+
+ return pluginbase.get_plugins(ptype)
+
+pluginmgr = PluginMgr()
diff --git a/mic/pluginbase.py b/mic/pluginbase.py
new file mode 100644
index 0000000..cf98cd7
--- /dev/null
+++ b/mic/pluginbase.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+from mic import msger
+from mic.utils import errors
+
+class _Plugin(object):
+ class __metaclass__(type):
+ def __init__(cls, name, bases, attrs):
+ if not hasattr(cls, 'plugins'):
+ cls.plugins = {}
+
+ elif 'mic_plugin_type' in attrs:
+ if attrs['mic_plugin_type'] not in cls.plugins:
+ cls.plugins[attrs['mic_plugin_type']] = {}
+
+ elif hasattr(cls, 'mic_plugin_type') and 'name' in attrs:
+ cls.plugins[cls.mic_plugin_type][attrs['name']] = cls
+
+ def show_plugins(cls):
+ for cls in cls.plugins[cls.mic_plugin_type]:
+ print cls
+
+ def get_plugins(cls):
+ return cls.plugins
+
+class ImagerPlugin(_Plugin):
+ mic_plugin_type = "imager"
+
+ @classmethod
+ def check_image_exists(self, destdir, apacking=None,
+ images=[],
+ release=None):
+
+ # if it's a packing file, reset images
+ if apacking:
+ images = [apacking]
+
+ # release option will override images
+ if release is not None:
+ images = [os.path.basename(destdir.rstrip('/'))]
+ destdir = os.path.dirname(destdir.rstrip('/'))
+
+ for name in images:
+ if not name:
+ continue
+
+ image = os.path.join(destdir, name)
+ if not os.path.exists(image):
+ continue
+
+ if msger.ask("Target image/dir: %s already exists, "
+ "clean up and continue?" % image):
+ if os.path.isdir(image):
+ shutil.rmtree(image)
+ else:
+ os.unlink(image)
+ else:
+ raise errors.Abort("Cancled")
+
+ def do_create(self):
+ pass
+
+ def do_chroot(self):
+ pass
+
+class BackendPlugin(_Plugin):
+ mic_plugin_type="backend"
+
+ # suppress the verbose rpm warnings
+ if msger.get_loglevel() != 'debug':
+ import rpm
+ rpm.setVerbosity(rpm.RPMLOG_ERR)
+
+ def addRepository(self):
+ pass
+
+def get_plugins(typen):
+ ps = ImagerPlugin.get_plugins()
+ if typen in ps:
+ return ps[typen]
+ else:
+ return None
+
+__all__ = ['ImagerPlugin', 'BackendPlugin', 'get_plugins']
diff --git a/mic/rt_util.py b/mic/rt_util.py
new file mode 100644
index 0000000..04a5291
--- /dev/null
+++ b/mic/rt_util.py
@@ -0,0 +1,240 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys
+import string
+import shutil
+import re
+
+from mic import bootstrap
+from mic import msger
+from mic.conf import configmgr
+from mic.utils import errors
+import mic.utils.misc as misc
+from mic.utils.proxy import get_proxy_for
+
+BOOTSTRAP_URL="http://download.tizen.org/tools/micbootstrap"
+
+def runmic_in_runtime(runmode, opts, ksfile, argv=None):
+ dist = misc.get_distro()[0]
+ if not runmode or not dist or "MeeGo" == dist:
+ return
+
+ if not argv:
+ argv = sys.argv
+ else:
+ argv = argv[:]
+
+ if runmode == 'bootstrap':
+ msger.info("Use bootstrap runtime environment")
+ name = "micbootstrap"
+ try:
+ repostrs = configmgr.bootstraps[name]
+ except:
+ repostrs = "name:%s,baseurl:%s," (name, BOOTSTRAP_URL)
+ proxy = get_proxy_for(BOOTSTRAP_URL)
+ if proxy:
+ repostrs += "proxy:%s" % proxy
+
+ repolist = []
+ if not name:
+ # use ks repo to create bootstrap
+ # so far it can't be effective for mic not in repo
+ #name = os.path.basename(ksfile)
+ #repostrs = misc.get_repostrs_from_ks(opts['ks'])
+ #for item in repostrs:
+ # repolist.append(convert_repostr(item))
+ msger.info("cannot find valid bootstrap, please check the config")
+ msger.info("Back to native running")
+ return
+ else:
+ for reponame, repostr in repostrs.items():
+ repolist.append(convert_repostr(repostr))
+ runmic_in_bootstrap(name, argv, opts, ksfile, repolist)
+ else:
+ raise errors.RuntimeError('Invalid runmode: %s ' % runmode)
+
+ sys.exit(0)
+
+def compare_rpmversion(ver1, ver2):
+ return ver1.split('.')[0] == ver2.split('.')[0] and \
+ ver1.split('.')[1] == ver2.split('.')[1]
+
+def convert_repostr(repostr):
+ repo = {}
+ for item in repostr.split(','):
+ loc = item.find(':')
+ opt = item[0:loc]
+ if opt in ('name', 'baseurl', 'mirrolist', 'proxy', \
+ 'proxy_username', 'proxy_password', 'debuginfo', \
+ 'source', 'gpgkey', 'disable'):
+ if len(item) > loc:
+ repo[opt] = item[loc+1:]
+ else:
+ repo[opt] = None
+ return repo
+
+def select_bootstrap(repomd, cachedir, bootstrapdir):
+ cfgmgr = configmgr
+ lvl = msger.get_loglevel()
+ msger.set_loglevel('quiet')
+ repo_rpmver = misc.get_rpmver_in_repo(repomd)
+ if not repo_rpmver:
+ msger.set_loglevel(lvl)
+ return (None, None)
+
+ # Check avaliable bootstrap
+ bootstrap_env = bootstrap.Bootstrap(homedir = bootstrapdir)
+ for bs in bootstrap_env.list():
+ if compare_rpmversion(repo_rpmver, bs['rpm']):
+ return (bs['name'], {})
+
+ for bsname, bsrepo in cfgmgr.bootstraps.items():
+ repolist = []
+ for repo in bsrepo.keys():
+ repolist.append(bsrepo[repo])
+
+ rpmver = None
+ try:
+ repomd = misc.get_metadata_from_repos(repolist, cachedir)
+ rpmver = misc.get_rpmver_in_repo(repomd)
+ except errors.CreatorError, e:
+ msger.set_loglevel(lvl)
+ raise
+
+ if not rpmver:
+ continue
+ if compare_rpmversion(repo_rpmver, rpmver):
+ msger.set_loglevel(lvl)
+ return (bsname, bsrepo)
+ msger.set_loglevel(lvl)
+ return (None, None)
+
+def runmic_in_bootstrap(name, argv, opts, ksfile, repolist):
+ bootstrap_env = bootstrap.Bootstrap(homedir = opts['bootstrapdir'])
+ bootstrap_lst = bootstrap_env.bootstraps
+ setattr(bootstrap_env, 'rootdir', name)
+ if not bootstrap_lst or not name in bootstrap_lst:
+ msger.info("Creating bootstrap %s under %s" % \
+ (name, bootstrap_env.homedir))
+ bootstrap_env.create(name, repolist)
+
+ msger.info("Use bootstrap: %s" % bootstrap_env.rootdir)
+ # copy mic
+ msger.info("Sync native mic to bootstrap")
+ copy_mic(bootstrap_env.rootdir)
+
+ # bind mounts , opts['cachedir'], opts['tmpdir']
+ cwd = os.getcwd()
+ lst = [cwd, opts['outdir']]
+ if ksfile:
+ ksfp = os.path.abspath(os.path.expanduser(ksfile))
+ lst.append(os.path.dirname(ksfp))
+ if opts['logfile']:
+ logfile = os.path.abspath(os.path.expanduser(opts['logfile']))
+ lst.append(os.path.dirname(logfile))
+ if opts['local_pkgs_path']:
+ lppdir = os.path.abspath(os.path.expanduser(opts['local_pkgs_path']))
+ lst.append(lppdir)
+
+ # TBD local repo
+
+ # make unique and remain the original order
+ lst = sorted(set(lst), key=lst.index)
+
+ bindmounts = ';'.join(map(lambda p: os.path.abspath(os.path.expanduser(p)),
+ lst))
+
+ msger.info("Start mic command in bootstrap")
+ bootstrap_env.run(name, argv, cwd, bindmounts)
+
+def get_mic_modpath():
+ try:
+ import mic
+ except ImportError:
+ raise errors.BootstrapError('Can\'t find mic module in host OS.')
+ else:
+ path = os.path.abspath(mic.__file__)
+ return os.path.dirname(path)
+
+def get_mic_binpath():
+ # FIXME: please use mic.find_binary_path()
+ path = os.environ['PATH']
+ paths = string.split(path, os.pathsep)
+ for pth in paths:
+ fn = os.path.join(pth, 'mic')
+ if os.path.isfile(fn):
+ return fn
+
+ msger.warning("Can't find mic command")
+ # FIXME: how to handle unfound case?
+
+def get_mic_libpath():
+ # so far mic lib path is hard coded
+ # TBD
+ return "/usr/lib/mic"
+
+# the hard code path is prepared for bootstrap
+def copy_mic(bootstrap_pth, bin_pth = '/usr/bin', lib_pth='/usr/lib', \
+ pylib_pth = '/usr/lib/python2.7/site-packages'):
+ # copy python lib files
+ mic_pylib = get_mic_modpath()
+ bs_mic_pylib = bootstrap_pth + os.path.join(pylib_pth, 'mic')
+ if os.path.commonprefix([mic_pylib, bs_mic_pylib]) == mic_pylib:
+ raise errors.BootstrapError('Invalid Bootstrap: %s' % bootstrap_pth)
+ shutil.rmtree(bs_mic_pylib, ignore_errors = True)
+ shutil.copytree(mic_pylib, bs_mic_pylib)
+ clean_files(".*\.py[co]$", bs_mic_pylib)
+
+ # copy lib files
+ mic_libpth = get_mic_libpath()
+ bs_mic_libpth = bootstrap_pth + os.path.join(lib_pth, 'mic')
+ if os.path.commonprefix([mic_libpth, bs_mic_libpth]) == mic_libpth:
+ raise errors.BootstrapError('Invalid Bootstrap: %s' % bootstrap_pth)
+ shutil.rmtree(bs_mic_libpth, ignore_errors = True)
+ shutil.copytree(mic_libpth, bs_mic_libpth)
+ os.system('cp -af %s %s' % (mic_libpth, os.path.dirname(bs_mic_libpth)))
+
+ # copy bin files
+ mic_binpth = get_mic_binpath()
+ bs_mic_binpth = bootstrap_pth + os.path.join(bin_pth, 'mic')
+ shutil.rmtree(bs_mic_binpth, ignore_errors = True)
+ shutil.copy2(mic_binpth, bs_mic_binpth)
+
+ # copy mic.conf
+ mic_cfgpth = '/etc/mic/mic.conf'
+ bs_mic_cfgpth = bootstrap_pth + mic_cfgpth
+ if not os.path.exists(os.path.dirname(bs_mic_cfgpth)):
+ os.makedirs(os.path.dirname(bs_mic_cfgpth))
+ shutil.copy2(mic_cfgpth, bs_mic_cfgpth)
+
+ # remove yum backend
+ try:
+ yumpth = "/usr/lib/mic/plugins/backend/yumpkgmgr.py"
+ os.unlink(bootstrap_pth + yumpth)
+ except:
+ pass
+
+def clean_files(pattern, dir):
+ if not os.path.exists(dir):
+ return
+ for f in os.listdir(dir):
+ entry = os.path.join(dir, f)
+ if os.path.isdir(entry):
+ clean_files(pattern, entry)
+ elif re.match(pattern, entry):
+ os.unlink(entry)
diff --git a/mic/utils/__init__.py b/mic/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mic/utils/__init__.py
diff --git a/mic/utils/cmdln.py b/mic/utils/cmdln.py
new file mode 100644
index 0000000..b099473
--- /dev/null
+++ b/mic/utils/cmdln.py
@@ -0,0 +1,1586 @@
+#!/usr/bin/env python
+# Copyright (c) 2002-2007 ActiveState Software Inc.
+# License: MIT (see LICENSE.txt for license details)
+# Author: Trent Mick
+# Home: http://trentm.com/projects/cmdln/
+
+"""An improvement on Python's standard cmd.py module.
+
+As with cmd.py, this module provides "a simple framework for writing
+line-oriented command intepreters." This module provides a 'RawCmdln'
+class that fixes some design flaws in cmd.Cmd, making it more scalable
+and nicer to use for good 'cvs'- or 'svn'-style command line interfaces
+or simple shells. And it provides a 'Cmdln' class that add
+optparse-based option processing. Basically you use it like this:
+
+ import cmdln
+
+ class MySVN(cmdln.Cmdln):
+ name = "svn"
+
+ @cmdln.alias('stat', 'st')
+ @cmdln.option('-v', '--verbose', action='store_true'
+ help='print verbose information')
+ def do_status(self, subcmd, opts, *paths):
+ print "handle 'svn status' command"
+
+ #...
+
+ if __name__ == "__main__":
+ shell = MySVN()
+ retval = shell.main()
+ sys.exit(retval)
+
+See the README.txt or <http://trentm.com/projects/cmdln/> for more
+details.
+"""
+
+__version_info__ = (1, 1, 2)
+__version__ = '.'.join(map(str, __version_info__))
+
+import os
+import sys
+import re
+import cmd
+import optparse
+from pprint import pprint
+import sys
+
+
+
+
+#---- globals
+
+LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3)
+
+# An unspecified optional argument when None is a meaningful value.
+_NOT_SPECIFIED = ("Not", "Specified")
+
+# Pattern to match a TypeError message from a call that
+# failed because of incorrect number of arguments (see
+# Python/getargs.c).
+_INCORRECT_NUM_ARGS_RE = re.compile(
+ r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
+
+
+
+#---- exceptions
+
+class CmdlnError(Exception):
+ """A cmdln.py usage error."""
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+class CmdlnUserError(Exception):
+ """An error by a user of a cmdln-based tool/shell."""
+ pass
+
+
+
+#---- public methods and classes
+
+def alias(*aliases):
+ """Decorator to add aliases for Cmdln.do_* command handlers.
+
+ Example:
+ class MyShell(cmdln.Cmdln):
+ @cmdln.alias("!", "sh")
+ def do_shell(self, argv):
+ #...implement 'shell' command
+ """
+ def decorate(f):
+ if not hasattr(f, "aliases"):
+ f.aliases = []
+ f.aliases += aliases
+ return f
+ return decorate
+
+
+class RawCmdln(cmd.Cmd):
+ """An improved (on cmd.Cmd) framework for building multi-subcommand
+ scripts (think "svn" & "cvs") and simple shells (think "pdb" and
+ "gdb").
+
+ A simple example:
+
+ import cmdln
+
+ class MySVN(cmdln.RawCmdln):
+ name = "svn"
+
+ @cmdln.aliases('stat', 'st')
+ def do_status(self, argv):
+ print "handle 'svn status' command"
+
+ if __name__ == "__main__":
+ shell = MySVN()
+ retval = shell.main()
+ sys.exit(retval)
+
+ See <http://trentm.com/projects/cmdln> for more information.
+ """
+ name = None # if unset, defaults basename(sys.argv[0])
+ prompt = None # if unset, defaults to self.name+"> "
+ version = None # if set, default top-level options include --version
+
+ # Default messages for some 'help' command error cases.
+ # They are interpolated with one arg: the command.
+ nohelp = "no help on '%s'"
+ unknowncmd = "unknown command: '%s'"
+
+ helpindent = '' # string with which to indent help output
+
+ def __init__(self, completekey='tab',
+ stdin=None, stdout=None, stderr=None):
+ """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
+
+ The optional argument 'completekey' is the readline name of a
+ completion key; it defaults to the Tab key. If completekey is
+ not None and the readline module is available, command completion
+ is done automatically.
+
+ The optional arguments 'stdin', 'stdout' and 'stderr' specify
+ alternate input, output and error output file objects; if not
+ specified, sys.* are used.
+
+ If 'stdout' but not 'stderr' is specified, stdout is used for
+ error output. This is to provide least surprise for users used
+ to only the 'stdin' and 'stdout' options with cmd.Cmd.
+ """
+ import sys
+ if self.name is None:
+ self.name = os.path.basename(sys.argv[0])
+ if self.prompt is None:
+ self.prompt = self.name+"> "
+ self._name_str = self._str(self.name)
+ self._prompt_str = self._str(self.prompt)
+ if stdin is not None:
+ self.stdin = stdin
+ else:
+ self.stdin = sys.stdin
+ if stdout is not None:
+ self.stdout = stdout
+ else:
+ self.stdout = sys.stdout
+ if stderr is not None:
+ self.stderr = stderr
+ elif stdout is not None:
+ self.stderr = stdout
+ else:
+ self.stderr = sys.stderr
+ self.cmdqueue = []
+ self.completekey = completekey
+ self.cmdlooping = False
+
+ def get_optparser(self):
+ """Hook for subclasses to set the option parser for the
+ top-level command/shell.
+
+ This option parser is used retrieved and used by `.main()' to
+ handle top-level options.
+
+ The default implements a single '-h|--help' option. Sub-classes
+ can return None to have no options at the top-level. Typically
+ an instance of CmdlnOptionParser should be returned.
+ """
+ version = (self.version is not None
+ and "%s %s" % (self._name_str, self.version)
+ or None)
+ return CmdlnOptionParser(self, version=version)
+
+ def postoptparse(self):
+ """Hook method executed just after `.main()' parses top-level
+ options.
+
+ When called `self.options' holds the results of the option parse.
+ """
+ pass
+
+ def main(self, argv=None, loop=LOOP_NEVER):
+ """A possible mainline handler for a script, like so:
+
+ import cmdln
+ class MyCmd(cmdln.Cmdln):
+ name = "mycmd"
+ ...
+
+ if __name__ == "__main__":
+ MyCmd().main()
+
+ By default this will use sys.argv to issue a single command to
+ 'MyCmd', then exit. The 'loop' argument can be use to control
+ interactive shell behaviour.
+
+ Arguments:
+ "argv" (optional, default sys.argv) is the command to run.
+ It must be a sequence, where the first element is the
+ command name and subsequent elements the args for that
+ command.
+ "loop" (optional, default LOOP_NEVER) is a constant
+ indicating if a command loop should be started (i.e. an
+ interactive shell). Valid values (constants on this module):
+ LOOP_ALWAYS start loop and run "argv", if any
+ LOOP_NEVER run "argv" (or .emptyline()) and exit
+ LOOP_IF_EMPTY run "argv", if given, and exit;
+ otherwise, start loop
+ """
+ if argv is None:
+ import sys
+ argv = sys.argv
+ else:
+ argv = argv[:] # don't modify caller's list
+
+ self.optparser = self.get_optparser()
+ if self.optparser: # i.e. optparser=None means don't process for opts
+ try:
+ self.options, args = self.optparser.parse_args(argv[1:])
+ except CmdlnUserError, ex:
+ msg = "%s: %s\nTry '%s help' for info.\n"\
+ % (self.name, ex, self.name)
+ self.stderr.write(self._str(msg))
+ self.stderr.flush()
+ return 1
+ except StopOptionProcessing, ex:
+ return 0
+ else:
+ self.options, args = None, argv[1:]
+ self.postoptparse()
+
+ if loop == LOOP_ALWAYS:
+ if args:
+ self.cmdqueue.append(args)
+ return self.cmdloop()
+ elif loop == LOOP_NEVER:
+ if args:
+ return self.cmd(args)
+ else:
+ return self.emptyline()
+ elif loop == LOOP_IF_EMPTY:
+ if args:
+ return self.cmd(args)
+ else:
+ return self.cmdloop()
+
+ def cmd(self, argv):
+ """Run one command and exit.
+
+ "argv" is the arglist for the command to run. argv[0] is the
+ command to run. If argv is an empty list then the
+ 'emptyline' handler is run.
+
+ Returns the return value from the command handler.
+ """
+ assert isinstance(argv, (list, tuple)), \
+ "'argv' is not a sequence: %r" % argv
+ retval = None
+ try:
+ argv = self.precmd(argv)
+ retval = self.onecmd(argv)
+ self.postcmd(argv)
+ except:
+ if not self.cmdexc(argv):
+ raise
+ retval = 1
+ return retval
+
+ def _str(self, s):
+ """Safely convert the given str/unicode to a string for printing."""
+ try:
+ return str(s)
+ except UnicodeError:
+ #XXX What is the proper encoding to use here? 'utf-8' seems
+ # to work better than "getdefaultencoding" (usually
+ # 'ascii'), on OS X at least.
+ #import sys
+ #return s.encode(sys.getdefaultencoding(), "replace")
+ return s.encode("utf-8", "replace")
+
+ def cmdloop(self, intro=None):
+ """Repeatedly issue a prompt, accept input, parse into an argv, and
+ dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
+ the argv. In other words, start a shell.
+
+ "intro" (optional) is a introductory message to print when
+ starting the command loop. This overrides the class
+ "intro" attribute, if any.
+ """
+ self.cmdlooping = True
+ self.preloop()
+ if self.use_rawinput and self.completekey:
+ try:
+ import readline
+ self.old_completer = readline.get_completer()
+ readline.set_completer(self.complete)
+ readline.parse_and_bind(self.completekey+": complete")
+ except ImportError:
+ pass
+ try:
+ if intro is None:
+ intro = self.intro
+ if intro:
+ intro_str = self._str(intro)
+ self.stdout.write(intro_str+'\n')
+ self.stop = False
+ retval = None
+ while not self.stop:
+ if self.cmdqueue:
+ argv = self.cmdqueue.pop(0)
+ assert isinstance(argv, (list, tuple)), \
+ "item on 'cmdqueue' is not a sequence: %r" % argv
+ else:
+ if self.use_rawinput:
+ try:
+ line = raw_input(self._prompt_str)
+ except EOFError:
+ line = 'EOF'
+ else:
+ self.stdout.write(self._prompt_str)
+ self.stdout.flush()
+ line = self.stdin.readline()
+ if not len(line):
+ line = 'EOF'
+ else:
+ line = line[:-1] # chop '\n'
+ argv = line2argv(line)
+ try:
+ argv = self.precmd(argv)
+ retval = self.onecmd(argv)
+ self.postcmd(argv)
+ except:
+ if not self.cmdexc(argv):
+ raise
+ retval = 1
+ self.lastretval = retval
+ self.postloop()
+ finally:
+ if self.use_rawinput and self.completekey:
+ try:
+ import readline
+ readline.set_completer(self.old_completer)
+ except ImportError:
+ pass
+ self.cmdlooping = False
+ return retval
+
+ def precmd(self, argv):
+ """Hook method executed just before the command argv is
+ interpreted, but after the input prompt is generated and issued.
+
+ "argv" is the cmd to run.
+
+ Returns an argv to run (i.e. this method can modify the command
+ to run).
+ """
+ return argv
+
+ def postcmd(self, argv):
+ """Hook method executed just after a command dispatch is finished.
+
+ "argv" is the command that was run.
+ """
+ pass
+
+ def cmdexc(self, argv):
+ """Called if an exception is raised in any of precmd(), onecmd(),
+ or postcmd(). If True is returned, the exception is deemed to have
+ been dealt with. Otherwise, the exception is re-raised.
+
+ The default implementation handles CmdlnUserError's, which
+ typically correspond to user error in calling commands (as
+ opposed to programmer error in the design of the script using
+ cmdln.py).
+ """
+ import sys
+ type, exc, traceback = sys.exc_info()
+ if isinstance(exc, CmdlnUserError):
+ msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
+ % (self.name, argv[0], exc, self.name, argv[0])
+ self.stderr.write(self._str(msg))
+ self.stderr.flush()
+ return True
+
+ def onecmd(self, argv):
+ if not argv:
+ return self.emptyline()
+ self.lastcmd = argv
+ cmdname = self._get_canonical_cmd_name(argv[0])
+ if cmdname:
+ handler = self._get_cmd_handler(cmdname)
+ if handler:
+ return self._dispatch_cmd(handler, argv)
+ return self.default(argv)
+
+ def _dispatch_cmd(self, handler, argv):
+ return handler(argv)
+
+ def default(self, argv):
+ """Hook called to handle a command for which there is no handler.
+
+ "argv" is the command and arguments to run.
+
+ The default implementation writes and error message to stderr
+ and returns an error exit status.
+
+ Returns a numeric command exit status.
+ """
+ errmsg = self._str(self.unknowncmd % (argv[0],))
+ if self.cmdlooping:
+ self.stderr.write(errmsg+"\n")
+ else:
+ self.stderr.write("%s: %s\nTry '%s help' for info.\n"
+ % (self._name_str, errmsg, self._name_str))
+ self.stderr.flush()
+ return 1
+
+ def parseline(self, line):
+ # This is used by Cmd.complete (readline completer function) to
+ # massage the current line buffer before completion processing.
+ # We override to drop special '!' handling.
+ line = line.strip()
+ if not line:
+ return None, None, line
+ elif line[0] == '?':
+ line = 'help ' + line[1:]
+ i, n = 0, len(line)
+ while i < n and line[i] in self.identchars: i = i+1
+ cmd, arg = line[:i], line[i:].strip()
+ return cmd, arg, line
+
+ def helpdefault(self, cmd, known):
+ """Hook called to handle help on a command for which there is no
+ help handler.
+
+ "cmd" is the command name on which help was requested.
+ "known" is a boolean indicating if this command is known
+ (i.e. if there is a handler for it).
+
+ Returns a return code.
+ """
+ if known:
+ msg = self._str(self.nohelp % (cmd,))
+ if self.cmdlooping:
+ self.stderr.write(msg + '\n')
+ else:
+ self.stderr.write("%s: %s\n" % (self.name, msg))
+ else:
+ msg = self.unknowncmd % (cmd,)
+ if self.cmdlooping:
+ self.stderr.write(msg + '\n')
+ else:
+ self.stderr.write("%s: %s\n"
+ "Try '%s help' for info.\n"
+ % (self.name, msg, self.name))
+ self.stderr.flush()
+ return 1
+
+ def do_help(self, argv):
+ """${cmd_name}: give detailed help on a specific sub-command
+
+ Usage:
+ ${name} help [COMMAND]
+ """
+ if len(argv) > 1: # asking for help on a particular command
+ doc = None
+ cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1]
+ if not cmdname:
+ return self.helpdefault(argv[1], False)
+ else:
+ helpfunc = getattr(self, "help_"+cmdname, None)
+ if helpfunc:
+ doc = helpfunc()
+ else:
+ handler = self._get_cmd_handler(cmdname)
+ if handler:
+ doc = handler.__doc__
+ if doc is None:
+ return self.helpdefault(argv[1], handler != None)
+ else: # bare "help" command
+ doc = self.__class__.__doc__ # try class docstring
+ if doc is None:
+ # Try to provide some reasonable useful default help.
+ if self.cmdlooping: prefix = ""
+ else: prefix = self.name+' '
+ doc = """Usage:
+ %sCOMMAND [ARGS...]
+ %shelp [COMMAND]
+
+ ${option_list}
+ ${command_list}
+ ${help_list}
+ """ % (prefix, prefix)
+ cmdname = None
+
+ if doc: # *do* have help content, massage and print that
+ doc = self._help_reindent(doc)
+ doc = self._help_preprocess(doc, cmdname)
+ doc = doc.rstrip() + '\n' # trim down trailing space
+ self.stdout.write(self._str(doc))
+ self.stdout.flush()
+ do_help.aliases = ["?"]
+
+ def _help_reindent(self, help, indent=None):
+ """Hook to re-indent help strings before writing to stdout.
+
+ "help" is the help content to re-indent
+ "indent" is a string with which to indent each line of the
+ help content after normalizing. If unspecified or None
+ then the default is use: the 'self.helpindent' class
+ attribute. By default this is the empty string, i.e.
+ no indentation.
+
+ By default, all common leading whitespace is removed and then
+ the lot is indented by 'self.helpindent'. When calculating the
+ common leading whitespace the first line is ignored -- hence
+ help content for Conan can be written as follows and have the
+ expected indentation:
+
+ def do_crush(self, ...):
+ '''${cmd_name}: crush your enemies, see them driven before you...
+
+ c.f. Conan the Barbarian'''
+ """
+ if indent is None:
+ indent = self.helpindent
+ lines = help.splitlines(0)
+ _dedentlines(lines, skip_first_line=True)
+ lines = [(indent+line).rstrip() for line in lines]
+ return '\n'.join(lines)
+
+ def _help_preprocess(self, help, cmdname):
+ """Hook to preprocess a help string before writing to stdout.
+
+ "help" is the help string to process.
+ "cmdname" is the canonical sub-command name for which help
+ is being given, or None if the help is not specific to a
+ command.
+
+ By default the following template variables are interpolated in
+ help content. (Note: these are similar to Python 2.4's
+ string.Template interpolation but not quite.)
+
+ ${name}
+ The tool's/shell's name, i.e. 'self.name'.
+ ${option_list}
+ A formatted table of options for this shell/tool.
+ ${command_list}
+ A formatted table of available sub-commands.
+ ${help_list}
+ A formatted table of additional help topics (i.e. 'help_*'
+ methods with no matching 'do_*' method).
+ ${cmd_name}
+ The name (and aliases) for this sub-command formatted as:
+ "NAME (ALIAS1, ALIAS2, ...)".
+ ${cmd_usage}
+ A formatted usage block inferred from the command function
+ signature.
+ ${cmd_option_list}
+ A formatted table of options for this sub-command. (This is
+ only available for commands using the optparse integration,
+ i.e. using @cmdln.option decorators or manually setting the
+ 'optparser' attribute on the 'do_*' method.)
+
+ Returns the processed help.
+ """
+ preprocessors = {
+ "${name}": self._help_preprocess_name,
+ "${option_list}": self._help_preprocess_option_list,
+ "${command_list}": self._help_preprocess_command_list,
+ "${help_list}": self._help_preprocess_help_list,
+ "${cmd_name}": self._help_preprocess_cmd_name,
+ "${cmd_usage}": self._help_preprocess_cmd_usage,
+ "${cmd_option_list}": self._help_preprocess_cmd_option_list,
+ }
+
+ for marker, preprocessor in preprocessors.items():
+ if marker in help:
+ help = preprocessor(help, cmdname)
+ return help
+
+ def _help_preprocess_name(self, help, cmdname=None):
+ return help.replace("${name}", self.name)
+
+ def _help_preprocess_option_list(self, help, cmdname=None):
+ marker = "${option_list}"
+ indent, indent_width = _get_indent(marker, help)
+ suffix = _get_trailing_whitespace(marker, help)
+
+ if self.optparser:
+ # Setup formatting options and format.
+ # - Indentation of 4 is better than optparse default of 2.
+ # C.f. Damian Conway's discussion of this in Perl Best
+ # Practices.
+ self.optparser.formatter.indent_increment = 4
+ self.optparser.formatter.current_indent = indent_width
+ block = self.optparser.format_option_help() + '\n'
+ else:
+ block = ""
+
+ help = help.replace(indent+marker+suffix, block, 1)
+ return help
+
+
+ def _help_preprocess_command_list(self, help, cmdname=None):
+ marker = "${command_list}"
+ indent, indent_width = _get_indent(marker, help)
+ suffix = _get_trailing_whitespace(marker, help)
+
+ # Find any aliases for commands.
+ token2canonical = self._get_canonical_map()
+ aliases = {}
+ for token, cmdname in token2canonical.items():
+ if token == cmdname: continue
+ aliases.setdefault(cmdname, []).append(token)
+
+ # Get the list of (non-hidden) commands and their
+ # documentation, if any.
+ cmdnames = {} # use a dict to strip duplicates
+ for attr in self.get_names():
+ if attr.startswith("do_"):
+ cmdnames[attr[3:]] = True
+ cmdnames = cmdnames.keys()
+ cmdnames.sort()
+ linedata = []
+ for cmdname in cmdnames:
+ if aliases.get(cmdname):
+ a = aliases[cmdname]
+ a.sort()
+ cmdstr = "%s (%s)" % (cmdname, ", ".join(a))
+ else:
+ cmdstr = cmdname
+ doc = None
+ try:
+ helpfunc = getattr(self, 'help_'+cmdname)
+ except AttributeError:
+ handler = self._get_cmd_handler(cmdname)
+ if handler:
+ doc = handler.__doc__
+ else:
+ doc = helpfunc()
+
+ # Strip "${cmd_name}: " from the start of a command's doc. Best
+ # practice dictates that command help strings begin with this, but
+ # it isn't at all wanted for the command list.
+ to_strip = "${cmd_name}:"
+ if doc and doc.startswith(to_strip):
+ #log.debug("stripping %r from start of %s's help string",
+ # to_strip, cmdname)
+ doc = doc[len(to_strip):].lstrip()
+ linedata.append( (cmdstr, doc) )
+
+ if linedata:
+ subindent = indent + ' '*4
+ lines = _format_linedata(linedata, subindent, indent_width+4)
+ block = indent + "Commands:\n" \
+ + '\n'.join(lines) + "\n\n"
+ help = help.replace(indent+marker+suffix, block, 1)
+ return help
+
+ def _gen_names_and_attrs(self):
+ # Inheritance says we have to look in class and
+ # base classes; order is not important.
+ names = []
+ classes = [self.__class__]
+ while classes:
+ aclass = classes.pop(0)
+ if aclass.__bases__:
+ classes = classes + list(aclass.__bases__)
+ for name in dir(aclass):
+ yield (name, getattr(aclass, name))
+
+ def _help_preprocess_help_list(self, help, cmdname=None):
+ marker = "${help_list}"
+ indent, indent_width = _get_indent(marker, help)
+ suffix = _get_trailing_whitespace(marker, help)
+
+ # Determine the additional help topics, if any.
+ helpnames = {}
+ token2cmdname = self._get_canonical_map()
+ for attrname, attr in self._gen_names_and_attrs():
+ if not attrname.startswith("help_"): continue
+ helpname = attrname[5:]
+ if helpname not in token2cmdname:
+ helpnames[helpname] = attr
+
+ if helpnames:
+ linedata = [(n, a.__doc__ or "") for n, a in helpnames.items()]
+ linedata.sort()
+
+ subindent = indent + ' '*4
+ lines = _format_linedata(linedata, subindent, indent_width+4)
+ block = (indent
+ + "Additional help topics (run `%s help TOPIC'):\n" % self.name
+ + '\n'.join(lines)
+ + "\n\n")
+ else:
+ block = ''
+ help = help.replace(indent+marker+suffix, block, 1)
+ return help
+
+ def _help_preprocess_cmd_name(self, help, cmdname=None):
+ marker = "${cmd_name}"
+ handler = self._get_cmd_handler(cmdname)
+ if not handler:
+ raise CmdlnError("cannot preprocess '%s' into help string: "
+ "could not find command handler for %r"
+ % (marker, cmdname))
+ s = cmdname
+ if hasattr(handler, "aliases"):
+ s += " (%s)" % (", ".join(handler.aliases))
+ help = help.replace(marker, s)
+ return help
+
+ #TODO: this only makes sense as part of the Cmdln class.
+ # Add hooks to add help preprocessing template vars and put
+ # this one on that class.
+ def _help_preprocess_cmd_usage(self, help, cmdname=None):
+ marker = "${cmd_usage}"
+ handler = self._get_cmd_handler(cmdname)
+ if not handler:
+ raise CmdlnError("cannot preprocess '%s' into help string: "
+ "could not find command handler for %r"
+ % (marker, cmdname))
+ indent, indent_width = _get_indent(marker, help)
+ suffix = _get_trailing_whitespace(marker, help)
+
+ # Extract the introspection bits we need.
+ func = handler.im_func
+ if func.func_defaults:
+ func_defaults = list(func.func_defaults)
+ else:
+ func_defaults = []
+ co_argcount = func.func_code.co_argcount
+ co_varnames = func.func_code.co_varnames
+ co_flags = func.func_code.co_flags
+ CO_FLAGS_ARGS = 4
+ CO_FLAGS_KWARGS = 8
+
+ # Adjust argcount for possible *args and **kwargs arguments.
+ argcount = co_argcount
+ if co_flags & CO_FLAGS_ARGS: argcount += 1
+ if co_flags & CO_FLAGS_KWARGS: argcount += 1
+
+ # Determine the usage string.
+ usage = "%s %s" % (self.name, cmdname)
+ if argcount <= 2: # handler ::= do_FOO(self, argv)
+ usage += " [ARGS...]"
+ elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...)
+ argnames = list(co_varnames[3:argcount])
+ tail = ""
+ if co_flags & CO_FLAGS_KWARGS:
+ name = argnames.pop(-1)
+ import warnings
+ # There is no generally accepted mechanism for passing
+ # keyword arguments from the command line. Could
+ # *perhaps* consider: arg=value arg2=value2 ...
+ warnings.warn("argument '**%s' on '%s.%s' command "
+ "handler will never get values"
+ % (name, self.__class__.__name__,
+ func.func_name))
+ if co_flags & CO_FLAGS_ARGS:
+ name = argnames.pop(-1)
+ tail = "[%s...]" % name.upper()
+ while func_defaults:
+ func_defaults.pop(-1)
+ name = argnames.pop(-1)
+ tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail)
+ while argnames:
+ name = argnames.pop(-1)
+ tail = "%s %s" % (name.upper(), tail)
+ usage += ' ' + tail
+
+ block_lines = [
+ self.helpindent + "Usage:",
+ self.helpindent + ' '*4 + usage
+ ]
+ block = '\n'.join(block_lines) + '\n\n'
+
+ help = help.replace(indent+marker+suffix, block, 1)
+ return help
+
+ #TODO: this only makes sense as part of the Cmdln class.
+ # Add hooks to add help preprocessing template vars and put
+ # this one on that class.
+ def _help_preprocess_cmd_option_list(self, help, cmdname=None):
+ marker = "${cmd_option_list}"
+ handler = self._get_cmd_handler(cmdname)
+ if not handler:
+ raise CmdlnError("cannot preprocess '%s' into help string: "
+ "could not find command handler for %r"
+ % (marker, cmdname))
+ indent, indent_width = _get_indent(marker, help)
+ suffix = _get_trailing_whitespace(marker, help)
+ if hasattr(handler, "optparser"):
+ # Setup formatting options and format.
+ # - Indentation of 4 is better than optparse default of 2.
+ # C.f. Damian Conway's discussion of this in Perl Best
+ # Practices.
+ handler.optparser.formatter.indent_increment = 4
+ handler.optparser.formatter.current_indent = indent_width
+ block = handler.optparser.format_option_help() + '\n'
+ else:
+ block = ""
+
+ help = help.replace(indent+marker+suffix, block, 1)
+ return help
+
+ def _get_canonical_cmd_name(self, token):
+ map = self._get_canonical_map()
+ return map.get(token, None)
+
+ def _get_canonical_map(self):
+ """Return a mapping of available command names and aliases to
+ their canonical command name.
+ """
+ cacheattr = "_token2canonical"
+ if not hasattr(self, cacheattr):
+ # Get the list of commands and their aliases, if any.
+ token2canonical = {}
+ cmd2funcname = {} # use a dict to strip duplicates
+ for attr in self.get_names():
+ if attr.startswith("do_"): cmdname = attr[3:]
+ elif attr.startswith("_do_"): cmdname = attr[4:]
+ else:
+ continue
+ cmd2funcname[cmdname] = attr
+ token2canonical[cmdname] = cmdname
+ for cmdname, funcname in cmd2funcname.items(): # add aliases
+ func = getattr(self, funcname)
+ aliases = getattr(func, "aliases", [])
+ for alias in aliases:
+ if alias in cmd2funcname:
+ import warnings
+ warnings.warn("'%s' alias for '%s' command conflicts "
+ "with '%s' handler"
+ % (alias, cmdname, cmd2funcname[alias]))
+ continue
+ token2canonical[alias] = cmdname
+ setattr(self, cacheattr, token2canonical)
+ return getattr(self, cacheattr)
+
+ def _get_cmd_handler(self, cmdname):
+ handler = None
+ try:
+ handler = getattr(self, 'do_' + cmdname)
+ except AttributeError:
+ try:
+ # Private command handlers begin with "_do_".
+ handler = getattr(self, '_do_' + cmdname)
+ except AttributeError:
+ pass
+ return handler
+
+ def _do_EOF(self, argv):
+ # Default EOF handler
+ # Note: an actual EOF is redirected to this command.
+ #TODO: separate name for this. Currently it is available from
+ # command-line. Is that okay?
+ self.stdout.write('\n')
+ self.stdout.flush()
+ self.stop = True
+
+ def emptyline(self):
+ # Different from cmd.Cmd: don't repeat the last command for an
+ # emptyline.
+ if self.cmdlooping:
+ pass
+ else:
+ return self.do_help(["help"])
+
+
+#---- optparse.py extension to fix (IMO) some deficiencies
+#
+# See the class _OptionParserEx docstring for details.
+#
+
+class StopOptionProcessing(Exception):
+ """Indicate that option *and argument* processing should stop
+ cleanly. This is not an error condition. It is similar in spirit to
+ StopIteration. This is raised by _OptionParserEx's default "help"
+ and "version" option actions and can be raised by custom option
+ callbacks too.
+
+ Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
+ usage is:
+
+ parser = CmdlnOptionParser(mycmd)
+ parser.add_option("-f", "--force", dest="force")
+ ...
+ try:
+ opts, args = parser.parse_args()
+ except StopOptionProcessing:
+ # normal termination, "--help" was probably given
+ sys.exit(0)
+ """
+
+class _OptionParserEx(optparse.OptionParser):
+ """An optparse.OptionParser that uses exceptions instead of sys.exit.
+
+ This class is an extension of optparse.OptionParser that differs
+ as follows:
+ - Correct (IMO) the default OptionParser error handling to never
+ sys.exit(). Instead OptParseError exceptions are passed through.
+ - Add the StopOptionProcessing exception (a la StopIteration) to
+ indicate normal termination of option processing.
+ See StopOptionProcessing's docstring for details.
+
+ I'd also like to see the following in the core optparse.py, perhaps
+ as a RawOptionParser which would serve as a base class for the more
+ generally used OptionParser (that works as current):
+ - Remove the implicit addition of the -h|--help and --version
+ options. They can get in the way (e.g. if want '-?' and '-V' for
+ these as well) and it is not hard to do:
+ optparser.add_option("-h", "--help", action="help")
+ optparser.add_option("--version", action="version")
+ These are good practices, just not valid defaults if they can
+ get in the way.
+ """
+ def error(self, msg):
+ raise optparse.OptParseError(msg)
+
+ def exit(self, status=0, msg=None):
+ if status == 0:
+ raise StopOptionProcessing(msg)
+ else:
+ #TODO: don't lose status info here
+ raise optparse.OptParseError(msg)
+
+
+
+#---- optparse.py-based option processing support
+
+class CmdlnOptionParser(_OptionParserEx):
+ """An optparse.OptionParser class more appropriate for top-level
+ Cmdln options. For parsing of sub-command options, see
+ SubCmdOptionParser.
+
+ Changes:
+ - disable_interspersed_args() by default, because a Cmdln instance
+ has sub-commands which may themselves have options.
+ - Redirect print_help() to the Cmdln.do_help() which is better
+ equiped to handle the "help" action.
+ - error() will raise a CmdlnUserError: OptionParse.error() is meant
+ to be called for user errors. Raising a well-known error here can
+ make error handling clearer.
+ - Also see the changes in _OptionParserEx.
+ """
+ def __init__(self, cmdln, **kwargs):
+ self.cmdln = cmdln
+ kwargs["prog"] = self.cmdln.name
+ _OptionParserEx.__init__(self, **kwargs)
+ self.disable_interspersed_args()
+
+ def print_help(self, file=None):
+ self.cmdln.onecmd(["help"])
+
+ def error(self, msg):
+ raise CmdlnUserError(msg)
+
+
+class SubCmdOptionParser(_OptionParserEx):
+ def set_cmdln_info(self, cmdln, subcmd):
+ """Called by Cmdln to pass relevant info about itself needed
+ for print_help().
+ """
+ self.cmdln = cmdln
+ self.subcmd = subcmd
+
+ def print_help(self, file=None):
+ self.cmdln.onecmd(["help", self.subcmd])
+
+ def error(self, msg):
+ raise CmdlnUserError(msg)
+
+
+def option(*args, **kwargs):
+ """Decorator to add an option to the optparser argument of a Cmdln
+ subcommand.
+
+ Example:
+ class MyShell(cmdln.Cmdln):
+ @cmdln.option("-f", "--force", help="force removal")
+ def do_remove(self, subcmd, opts, *args):
+ #...
+ """
+ #XXX Is there a possible optimization for many options to not have a
+ # large stack depth here?
+ def decorate(f):
+ if not hasattr(f, "optparser"):
+ f.optparser = SubCmdOptionParser()
+ f.optparser.add_option(*args, **kwargs)
+ return f
+ return decorate
+
+
+class Cmdln(RawCmdln):
+ """An improved (on cmd.Cmd) framework for building multi-subcommand
+ scripts (think "svn" & "cvs") and simple shells (think "pdb" and
+ "gdb").
+
+ A simple example:
+
+ import cmdln
+
+ class MySVN(cmdln.Cmdln):
+ name = "svn"
+
+ @cmdln.aliases('stat', 'st')
+ @cmdln.option('-v', '--verbose', action='store_true'
+ help='print verbose information')
+ def do_status(self, subcmd, opts, *paths):
+ print "handle 'svn status' command"
+
+ #...
+
+ if __name__ == "__main__":
+ shell = MySVN()
+ retval = shell.main()
+ sys.exit(retval)
+
+ 'Cmdln' extends 'RawCmdln' by providing optparse option processing
+ integration. See this class' _dispatch_cmd() docstring and
+ <http://trentm.com/projects/cmdln> for more information.
+ """
+ def _dispatch_cmd(self, handler, argv):
+ """Introspect sub-command handler signature to determine how to
+ dispatch the command. The raw handler provided by the base
+ 'RawCmdln' class is still supported:
+
+ def do_foo(self, argv):
+ # 'argv' is the vector of command line args, argv[0] is
+ # the command name itself (i.e. "foo" or an alias)
+ pass
+
+ In addition, if the handler has more than 2 arguments option
+ processing is automatically done (using optparse):
+
+ @cmdln.option('-v', '--verbose', action='store_true')
+ def do_bar(self, subcmd, opts, *args):
+ # subcmd = <"bar" or an alias>
+ # opts = <an optparse.Values instance>
+ if opts.verbose:
+ print "lots of debugging output..."
+ # args = <tuple of arguments>
+ for arg in args:
+ bar(arg)
+
+ TODO: explain that "*args" can be other signatures as well.
+
+ The `cmdln.option` decorator corresponds to an `add_option()`
+ method call on an `optparse.OptionParser` instance.
+
+ You can declare a specific number of arguments:
+
+ @cmdln.option('-v', '--verbose', action='store_true')
+ def do_bar2(self, subcmd, opts, bar_one, bar_two):
+ #...
+
+ and an appropriate error message will be raised/printed if the
+ command is called with a different number of args.
+ """
+ co_argcount = handler.im_func.func_code.co_argcount
+ if co_argcount == 2: # handler ::= do_foo(self, argv)
+ return handler(argv)
+ elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
+ try:
+ optparser = handler.optparser
+ except AttributeError:
+ optparser = handler.im_func.optparser = SubCmdOptionParser()
+ assert isinstance(optparser, SubCmdOptionParser)
+ optparser.set_cmdln_info(self, argv[0])
+ try:
+ opts, args = optparser.parse_args(argv[1:])
+ except StopOptionProcessing:
+ #TODO: this doesn't really fly for a replacement of
+ # optparse.py behaviour, does it?
+ return 0 # Normal command termination
+
+ try:
+ return handler(argv[0], opts, *args)
+ except TypeError, ex:
+ # Some TypeError's are user errors:
+ # do_foo() takes at least 4 arguments (3 given)
+ # do_foo() takes at most 5 arguments (6 given)
+ # do_foo() takes exactly 5 arguments (6 given)
+ # Raise CmdlnUserError for these with a suitably
+ # massaged error message.
+ import sys
+ tb = sys.exc_info()[2] # the traceback object
+ if tb.tb_next is not None:
+ # If the traceback is more than one level deep, then the
+ # TypeError do *not* happen on the "handler(...)" call
+ # above. In that we don't want to handle it specially
+ # here: it would falsely mask deeper code errors.
+ raise
+ msg = ex.args[0]
+ match = _INCORRECT_NUM_ARGS_RE.search(msg)
+ if match:
+ msg = list(match.groups())
+ msg[1] = int(msg[1]) - 3
+ if msg[1] == 1:
+ msg[2] = msg[2].replace("arguments", "argument")
+ msg[3] = int(msg[3]) - 3
+ msg = ''.join(map(str, msg))
+ raise CmdlnUserError(msg)
+ else:
+ raise
+ else:
+ raise CmdlnError("incorrect argcount for %s(): takes %d, must "
+ "take 2 for 'argv' signature or 3+ for 'opts' "
+ "signature" % (handler.__name__, co_argcount))
+
+
+
+#---- internal support functions
+
+def _format_linedata(linedata, indent, indent_width):
+ """Format specific linedata into a pleasant layout.
+
+ "linedata" is a list of 2-tuples of the form:
+ (<item-display-string>, <item-docstring>)
+ "indent" is a string to use for one level of indentation
+ "indent_width" is a number of columns by which the
+ formatted data will be indented when printed.
+
+ The <item-display-string> column is held to 15 columns.
+ """
+ lines = []
+ WIDTH = 78 - indent_width
+ SPACING = 2
+ NAME_WIDTH_LOWER_BOUND = 13
+ NAME_WIDTH_UPPER_BOUND = 16
+ NAME_WIDTH = max([len(s) for s,d in linedata])
+ if NAME_WIDTH < NAME_WIDTH_LOWER_BOUND:
+ NAME_WIDTH = NAME_WIDTH_LOWER_BOUND
+ else:
+ NAME_WIDTH = NAME_WIDTH_UPPER_BOUND
+
+ DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING
+ for namestr, doc in linedata:
+ line = indent + namestr
+ if len(namestr) <= NAME_WIDTH:
+ line += ' ' * (NAME_WIDTH + SPACING - len(namestr))
+ else:
+ lines.append(line)
+ line = indent + ' ' * (NAME_WIDTH + SPACING)
+ line += _summarize_doc(doc, DOC_WIDTH)
+ lines.append(line.rstrip())
+ return lines
+
+def _summarize_doc(doc, length=60):
+ r"""Parse out a short one line summary from the given doclines.
+
+ "doc" is the doc string to summarize.
+ "length" is the max length for the summary
+
+ >>> _summarize_doc("this function does this")
+ 'this function does this'
+ >>> _summarize_doc("this function does this", 10)
+ 'this fu...'
+ >>> _summarize_doc("this function does this\nand that")
+ 'this function does this and that'
+ >>> _summarize_doc("this function does this\n\nand that")
+ 'this function does this'
+ """
+ import re
+ if doc is None:
+ return ""
+ assert length > 3, "length <= 3 is absurdly short for a doc summary"
+ doclines = doc.strip().splitlines(0)
+ if not doclines:
+ return ""
+
+ summlines = []
+ for i, line in enumerate(doclines):
+ stripped = line.strip()
+ if not stripped:
+ break
+ summlines.append(stripped)
+ if len(''.join(summlines)) >= length:
+ break
+
+ summary = ' '.join(summlines)
+ if len(summary) > length:
+ summary = summary[:length-3] + "..."
+ return summary
+
+
+def line2argv(line):
+ r"""Parse the given line into an argument vector.
+
+ "line" is the line of input to parse.
+
+ This may get niggly when dealing with quoting and escaping. The
+ current state of this parsing may not be completely thorough/correct
+ in this respect.
+
+ >>> from cmdln import line2argv
+ >>> line2argv("foo")
+ ['foo']
+ >>> line2argv("foo bar")
+ ['foo', 'bar']
+ >>> line2argv("foo bar ")
+ ['foo', 'bar']
+ >>> line2argv(" foo bar")
+ ['foo', 'bar']
+
+ Quote handling:
+
+ >>> line2argv("'foo bar'")
+ ['foo bar']
+ >>> line2argv('"foo bar"')
+ ['foo bar']
+ >>> line2argv(r'"foo\"bar"')
+ ['foo"bar']
+ >>> line2argv("'foo bar' spam")
+ ['foo bar', 'spam']
+ >>> line2argv("'foo 'bar spam")
+ ['foo bar', 'spam']
+
+ >>> line2argv('some\tsimple\ttests')
+ ['some', 'simple', 'tests']
+ >>> line2argv('a "more complex" test')
+ ['a', 'more complex', 'test']
+ >>> line2argv('a more="complex test of " quotes')
+ ['a', 'more=complex test of ', 'quotes']
+ >>> line2argv('a more" complex test of " quotes')
+ ['a', 'more complex test of ', 'quotes']
+ >>> line2argv('an "embedded \\"quote\\""')
+ ['an', 'embedded "quote"']
+
+ # Komodo bug 48027
+ >>> line2argv('foo bar C:\\')
+ ['foo', 'bar', 'C:\\']
+
+ # Komodo change 127581
+ >>> line2argv(r'"\test\slash" "foo bar" "foo\"bar"')
+ ['\\test\\slash', 'foo bar', 'foo"bar']
+
+ # Komodo change 127629
+ >>> if sys.platform == "win32":
+ ... line2argv(r'\foo\bar') == ['\\foo\\bar']
+ ... line2argv(r'\\foo\\bar') == ['\\\\foo\\\\bar']
+ ... line2argv('"foo') == ['foo']
+ ... else:
+ ... line2argv(r'\foo\bar') == ['foobar']
+ ... line2argv(r'\\foo\\bar') == ['\\foo\\bar']
+ ... try:
+ ... line2argv('"foo')
+ ... except ValueError, ex:
+ ... "not terminated" in str(ex)
+ True
+ True
+ True
+ """
+ import string
+ line = line.strip()
+ argv = []
+ state = "default"
+ arg = None # the current argument being parsed
+ i = -1
+ while 1:
+ i += 1
+ if i >= len(line): break
+ ch = line[i]
+
+ if ch == "\\" and i+1 < len(line):
+ # escaped char always added to arg, regardless of state
+ if arg is None: arg = ""
+ if (sys.platform == "win32"
+ or state in ("double-quoted", "single-quoted")
+ ) and line[i+1] not in tuple('"\''):
+ arg += ch
+ i += 1
+ arg += line[i]
+ continue
+
+ if state == "single-quoted":
+ if ch == "'":
+ state = "default"
+ else:
+ arg += ch
+ elif state == "double-quoted":
+ if ch == '"':
+ state = "default"
+ else:
+ arg += ch
+ elif state == "default":
+ if ch == '"':
+ if arg is None: arg = ""
+ state = "double-quoted"
+ elif ch == "'":
+ if arg is None: arg = ""
+ state = "single-quoted"
+ elif ch in string.whitespace:
+ if arg is not None:
+ argv.append(arg)
+ arg = None
+ else:
+ if arg is None: arg = ""
+ arg += ch
+ if arg is not None:
+ argv.append(arg)
+ if not sys.platform == "win32" and state != "default":
+ raise ValueError("command line is not terminated: unfinished %s "
+ "segment" % state)
+ return argv
+
+
+def argv2line(argv):
+ r"""Put together the given argument vector into a command line.
+
+ "argv" is the argument vector to process.
+
+ >>> from cmdln import argv2line
+ >>> argv2line(['foo'])
+ 'foo'
+ >>> argv2line(['foo', 'bar'])
+ 'foo bar'
+ >>> argv2line(['foo', 'bar baz'])
+ 'foo "bar baz"'
+ >>> argv2line(['foo"bar'])
+ 'foo"bar'
+ >>> print argv2line(['foo" bar'])
+ 'foo" bar'
+ >>> print argv2line(["foo' bar"])
+ "foo' bar"
+ >>> argv2line(["foo'bar"])
+ "foo'bar"
+ """
+ escapedArgs = []
+ for arg in argv:
+ if ' ' in arg and '"' not in arg:
+ arg = '"'+arg+'"'
+ elif ' ' in arg and "'" not in arg:
+ arg = "'"+arg+"'"
+ elif ' ' in arg:
+ arg = arg.replace('"', r'\"')
+ arg = '"'+arg+'"'
+ escapedArgs.append(arg)
+ return ' '.join(escapedArgs)
+
+
+# Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
+def _dedentlines(lines, tabsize=8, skip_first_line=False):
+ """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
+
+ "lines" is a list of lines to dedent.
+ "tabsize" is the tab width to use for indent width calculations.
+ "skip_first_line" is a boolean indicating if the first line should
+ be skipped for calculating the indent width and for dedenting.
+ This is sometimes useful for docstrings and similar.
+
+ Same as dedent() except operates on a sequence of lines. Note: the
+ lines list is modified **in-place**.
+ """
+ DEBUG = False
+ if DEBUG:
+ print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
+ % (tabsize, skip_first_line)
+ indents = []
+ margin = None
+ for i, line in enumerate(lines):
+ if i == 0 and skip_first_line: continue
+ indent = 0
+ for ch in line:
+ if ch == ' ':
+ indent += 1
+ elif ch == '\t':
+ indent += tabsize - (indent % tabsize)
+ elif ch in '\r\n':
+ continue # skip all-whitespace lines
+ else:
+ break
+ else:
+ continue # skip all-whitespace lines
+ if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
+ if margin is None:
+ margin = indent
+ else:
+ margin = min(margin, indent)
+ if DEBUG: print "dedent: margin=%r" % margin
+
+ if margin is not None and margin > 0:
+ for i, line in enumerate(lines):
+ if i == 0 and skip_first_line: continue
+ removed = 0
+ for j, ch in enumerate(line):
+ if ch == ' ':
+ removed += 1
+ elif ch == '\t':
+ removed += tabsize - (removed % tabsize)
+ elif ch in '\r\n':
+ if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
+ lines[i] = lines[i][j:]
+ break
+ else:
+ raise ValueError("unexpected non-whitespace char %r in "
+ "line %r while removing %d-space margin"
+ % (ch, line, margin))
+ if DEBUG:
+ print "dedent: %r: %r -> removed %d/%d"\
+ % (line, ch, removed, margin)
+ if removed == margin:
+ lines[i] = lines[i][j+1:]
+ break
+ elif removed > margin:
+ lines[i] = ' '*(removed-margin) + lines[i][j+1:]
+ break
+ return lines
+
+def _dedent(text, tabsize=8, skip_first_line=False):
+ """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text
+
+ "text" is the text to dedent.
+ "tabsize" is the tab width to use for indent width calculations.
+ "skip_first_line" is a boolean indicating if the first line should
+ be skipped for calculating the indent width and for dedenting.
+ This is sometimes useful for docstrings and similar.
+
+ textwrap.dedent(s), but don't expand tabs to spaces
+ """
+ lines = text.splitlines(1)
+ _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line)
+ return ''.join(lines)
+
+
+def _get_indent(marker, s, tab_width=8):
+ """_get_indent(marker, s, tab_width=8) ->
+ (<indentation-of-'marker'>, <indentation-width>)"""
+ # Figure out how much the marker is indented.
+ INDENT_CHARS = tuple(' \t')
+ start = s.index(marker)
+ i = start
+ while i > 0:
+ if s[i-1] not in INDENT_CHARS:
+ break
+ i -= 1
+ indent = s[i:start]
+ indent_width = 0
+ for ch in indent:
+ if ch == ' ':
+ indent_width += 1
+ elif ch == '\t':
+ indent_width += tab_width - (indent_width % tab_width)
+ return indent, indent_width
+
+def _get_trailing_whitespace(marker, s):
+ """Return the whitespace content trailing the given 'marker' in string 's',
+ up to and including a newline.
+ """
+ suffix = ''
+ start = s.index(marker) + len(marker)
+ i = start
+ while i < len(s):
+ if s[i] in ' \t':
+ suffix += s[i]
+ elif s[i] in '\r\n':
+ suffix += s[i]
+ if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n':
+ suffix += s[i+1]
+ break
+ else:
+ break
+ i += 1
+ return suffix
+
+
+
+#---- bash completion support
+# Note: This is still experimental. I expect to change this
+# significantly.
+#
+# To get Bash completion for a cmdln.Cmdln class, run the following
+# bash command:
+# $ complete -C 'python -m cmdln /path/to/script.py CmdlnClass' cmdname
+# For example:
+# $ complete -C 'python -m cmdln ~/bin/svn.py SVN' svn
+#
+#TODO: Simplify the above so don't have to given path to script (try to
+# find it on PATH, if possible). Could also make class name
+# optional if there is only one in the module (common case).
+
+if __name__ == "__main__" and len(sys.argv) == 6:
+ def _log(s):
+ return # no-op, comment out for debugging
+ from os.path import expanduser
+ fout = open(expanduser("~/tmp/bashcpln.log"), 'a')
+ fout.write(str(s) + '\n')
+ fout.close()
+
+ # Recipe: module_from_path (1.0.1+)
+ def _module_from_path(path):
+ import imp, os, sys
+ path = os.path.expanduser(path)
+ dir = os.path.dirname(path) or os.curdir
+ name = os.path.splitext(os.path.basename(path))[0]
+ sys.path.insert(0, dir)
+ try:
+ iinfo = imp.find_module(name, [dir])
+ return imp.load_module(name, *iinfo)
+ finally:
+ sys.path.remove(dir)
+
+ def _get_bash_cplns(script_path, class_name, cmd_name,
+ token, preceding_token):
+ _log('--')
+ _log('get_cplns(%r, %r, %r, %r, %r)'
+ % (script_path, class_name, cmd_name, token, preceding_token))
+ comp_line = os.environ["COMP_LINE"]
+ comp_point = int(os.environ["COMP_POINT"])
+ _log("COMP_LINE: %r" % comp_line)
+ _log("COMP_POINT: %r" % comp_point)
+
+ try:
+ script = _module_from_path(script_path)
+ except ImportError, ex:
+ _log("error importing `%s': %s" % (script_path, ex))
+ return []
+ shell = getattr(script, class_name)()
+ cmd_map = shell._get_canonical_map()
+ del cmd_map["EOF"]
+
+ # Determine if completing the sub-command name.
+ parts = comp_line[:comp_point].split(None, 1)
+ _log(parts)
+ if len(parts) == 1 or not (' ' in parts[1] or '\t' in parts[1]):
+ #TODO: if parts[1].startswith('-'): handle top-level opts
+ _log("complete sub-command names")
+ matches = {}
+ for name, canon_name in cmd_map.items():
+ if name.startswith(token):
+ matches[name] = canon_name
+ if not matches:
+ return []
+ elif len(matches) == 1:
+ return matches.keys()
+ elif len(set(matches.values())) == 1:
+ return [matches.values()[0]]
+ else:
+ return matches.keys()
+
+ # Otherwise, complete options for the given sub-command.
+ #TODO: refine this so it does the right thing with option args
+ if token.startswith('-'):
+ cmd_name = comp_line.split(None, 2)[1]
+ try:
+ cmd_canon_name = cmd_map[cmd_name]
+ except KeyError:
+ return []
+ handler = shell._get_cmd_handler(cmd_canon_name)
+ optparser = getattr(handler, "optparser", None)
+ if optparser is None:
+ optparser = SubCmdOptionParser()
+ opt_strs = []
+ for option in optparser.option_list:
+ for opt_str in option._short_opts + option._long_opts:
+ if opt_str.startswith(token):
+ opt_strs.append(opt_str)
+ return opt_strs
+
+ return []
+
+ for cpln in _get_bash_cplns(*sys.argv[1:]):
+ print cpln
+
diff --git a/mic/utils/errors.py b/mic/utils/errors.py
new file mode 100644
index 0000000..5e7777b
--- /dev/null
+++ b/mic/utils/errors.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2007 Red Hat, Inc.
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+class CreatorError(Exception):
+ """An exception base class for all imgcreate errors."""
+ keyword = '<creator>'
+
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.keyword + str(self.msg)
+
+class Usage(CreatorError):
+ keyword = '<usage>'
+
+ def __str__(self):
+ return self.keyword + str(self.msg) + ', please use "--help" for more info'
+
+class Abort(CreatorError):
+ keyword = ''
+
+class ConfigError(CreatorError):
+ keyword = '<config>'
+
+class KsError(CreatorError):
+ keyword = '<kickstart>'
+
+class RepoError(CreatorError):
+ keyword = '<repo>'
+
+class RpmError(CreatorError):
+ keyword = '<rpm>'
+
+class MountError(CreatorError):
+ keyword = '<mount>'
+
+class SnapshotError(CreatorError):
+ keyword = '<snapshot>'
+
+class SquashfsError(CreatorError):
+ keyword = '<squashfs>'
+
+class BootstrapError(CreatorError):
+ keyword = '<bootstrap>'
+
+class RuntimeError(CreatorError):
+ keyword = '<runtime>'
diff --git a/mic/utils/fs_related.py b/mic/utils/fs_related.py
new file mode 100644
index 0000000..d1bf43f
--- /dev/null
+++ b/mic/utils/fs_related.py
@@ -0,0 +1,944 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2007, Red Hat, Inc.
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+import os
+import sys
+import errno
+import stat
+import random
+import string
+import time
+
+from errors import *
+from mic import msger
+import runner
+
+def find_binary_inchroot(binary, chroot):
+ paths = ["/usr/sbin",
+ "/usr/bin",
+ "/sbin",
+ "/bin"
+ ]
+
+ for path in paths:
+ bin_path = "%s/%s" % (path, binary)
+ if os.path.exists("%s/%s" % (chroot, bin_path)):
+ return bin_path
+ return None
+
+def find_binary_path(binary):
+ if os.environ.has_key("PATH"):
+ paths = os.environ["PATH"].split(":")
+ else:
+ paths = []
+ if os.environ.has_key("HOME"):
+ paths += [os.environ["HOME"] + "/bin"]
+ paths += ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"]
+
+ for path in paths:
+ bin_path = "%s/%s" % (path, binary)
+ if os.path.exists(bin_path):
+ return bin_path
+ raise CreatorError("Command '%s' is not available." % binary)
+
+def makedirs(dirname):
+ """A version of os.makedirs() that doesn't throw an
+ exception if the leaf directory already exists.
+ """
+ try:
+ os.makedirs(dirname)
+ except OSError, err:
+ if err.errno != errno.EEXIST:
+ raise
+
+def mksquashfs(in_img, out_img):
+ fullpathmksquashfs = find_binary_path("mksquashfs")
+ args = [fullpathmksquashfs, in_img, out_img]
+
+ if not sys.stdout.isatty():
+ args.append("-no-progress")
+
+ ret = runner.show(args)
+ if ret != 0:
+ raise SquashfsError("'%s' exited with error (%d)" % (' '.join(args), ret))
+
+def resize2fs(fs, size):
+ resize2fs = find_binary_path("resize2fs")
+ if size == 0:
+ # it means to minimalize it
+ return runner.show([resize2fs, '-M', fs])
+ else:
+ return runner.show([resize2fs, fs, "%sK" % (size / 1024,)])
+
+def my_fuser(fp):
+ fuser = find_binary_path("fuser")
+ if not os.path.exists(fp):
+ return False
+
+ rc = runner.quiet([fuser, "-s", fp])
+ if rc == 0:
+ for pid in runner.outs([fuser, fp]).split():
+ fd = open("/proc/%s/cmdline" % pid, "r")
+ cmdline = fd.read()
+ fd.close()
+ if cmdline[:-1] == "/bin/bash":
+ return True
+
+ # not found
+ return False
+
+class BindChrootMount:
+ """Represents a bind mount of a directory into a chroot."""
+ def __init__(self, src, chroot, dest = None, option = None):
+ self.src = src
+ self.root = os.path.abspath(os.path.expanduser(chroot))
+ self.option = option
+
+ if not dest:
+ dest = src
+ self.dest = self.root + "/" + dest
+
+ self.mounted = False
+ self.mountcmd = find_binary_path("mount")
+ self.umountcmd = find_binary_path("umount")
+
+ def ismounted(self):
+ with open('/proc/mounts') as f:
+ for line in f:
+ if line.split()[1] == os.path.abspath(self.dest):
+ return True
+
+ return False
+
+ def has_chroot_instance(self):
+ lock = os.path.join(self.root, ".chroot.lock")
+ return my_fuser(lock)
+
+ def mount(self):
+ if self.mounted or self.ismounted():
+ return
+
+ makedirs(self.dest)
+ rc = runner.show([self.mountcmd, "--bind", self.src, self.dest])
+ if rc != 0:
+ raise MountError("Bind-mounting '%s' to '%s' failed" %
+ (self.src, self.dest))
+ if self.option:
+ rc = runner.show([self.mountcmd, "--bind", "-o", "remount,%s" % self.option, self.dest])
+ if rc != 0:
+ raise MountError("Bind-remounting '%s' failed" % self.dest)
+ self.mounted = True
+
+ def unmount(self):
+ if self.has_chroot_instance():
+ return
+
+ if self.ismounted():
+ runner.show([self.umountcmd, "-l", self.dest])
+ self.mounted = False
+
+class LoopbackMount:
+ """LoopbackMount compatibility layer for old API"""
+ def __init__(self, lofile, mountdir, fstype = None):
+ self.diskmount = DiskMount(LoopbackDisk(lofile,size = 0),mountdir,fstype,rmmountdir = True)
+ self.losetup = False
+ self.losetupcmd = find_binary_path("losetup")
+
+ def cleanup(self):
+ self.diskmount.cleanup()
+
+ def unmount(self):
+ self.diskmount.unmount()
+
+ def lounsetup(self):
+ if self.losetup:
+ runner.show([self.losetupcmd, "-d", self.loopdev])
+ self.losetup = False
+ self.loopdev = None
+
+ def loopsetup(self):
+ if self.losetup:
+ return
+
+ self.loopdev = get_loop_device(self.losetupcmd, self.lofile)
+ self.losetup = True
+
+ def mount(self):
+ self.diskmount.mount()
+
+class SparseLoopbackMount(LoopbackMount):
+ """SparseLoopbackMount compatibility layer for old API"""
+ def __init__(self, lofile, mountdir, size, fstype = None):
+ self.diskmount = DiskMount(SparseLoopbackDisk(lofile,size),mountdir,fstype,rmmountdir = True)
+
+ def expand(self, create = False, size = None):
+ self.diskmount.disk.expand(create, size)
+
+ def truncate(self, size = None):
+ self.diskmount.disk.truncate(size)
+
+ def create(self):
+ self.diskmount.disk.create()
+
+class SparseExtLoopbackMount(SparseLoopbackMount):
+ """SparseExtLoopbackMount compatibility layer for old API"""
+ def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel):
+ self.diskmount = ExtDiskMount(SparseLoopbackDisk(lofile,size), mountdir, fstype, blocksize, fslabel, rmmountdir = True)
+
+
+ def __format_filesystem(self):
+ self.diskmount.__format_filesystem()
+
+ def create(self):
+ self.diskmount.disk.create()
+
+ def resize(self, size = None):
+ return self.diskmount.__resize_filesystem(size)
+
+ def mount(self):
+ self.diskmount.mount()
+
+ def __fsck(self):
+ self.extdiskmount.__fsck()
+
+ def __get_size_from_filesystem(self):
+ return self.diskmount.__get_size_from_filesystem()
+
+ def __resize_to_minimal(self):
+ return self.diskmount.__resize_to_minimal()
+
+ def resparse(self, size = None):
+ return self.diskmount.resparse(size)
+
+class Disk:
+ """Generic base object for a disk
+
+ The 'create' method must make the disk visible as a block device - eg
+ by calling losetup. For RawDisk, this is obviously a no-op. The 'cleanup'
+ method must undo the 'create' operation.
+ """
+ def __init__(self, size, device = None):
+ self._device = device
+ self._size = size
+
+ def create(self):
+ pass
+
+ def cleanup(self):
+ pass
+
+ def get_device(self):
+ return self._device
+ def set_device(self, path):
+ self._device = path
+ device = property(get_device, set_device)
+
+ def get_size(self):
+ return self._size
+ size = property(get_size)
+
+
+class RawDisk(Disk):
+ """A Disk backed by a block device.
+ Note that create() is a no-op.
+ """
+ def __init__(self, size, device):
+ Disk.__init__(self, size, device)
+
+ def fixed(self):
+ return True
+
+ def exists(self):
+ return True
+
+class LoopbackDisk(Disk):
+ """A Disk backed by a file via the loop module."""
+ def __init__(self, lofile, size):
+ Disk.__init__(self, size)
+ self.lofile = lofile
+ self.losetupcmd = find_binary_path("losetup")
+
+ def fixed(self):
+ return False
+
+ def exists(self):
+ return os.path.exists(self.lofile)
+
+ def create(self):
+ if self.device is not None:
+ return
+
+ self.device = get_loop_device(self.losetupcmd, self.lofile)
+
+ def cleanup(self):
+ if self.device is None:
+ return
+ msger.debug("Losetup remove %s" % self.device)
+ rc = runner.show([self.losetupcmd, "-d", self.device])
+ self.device = None
+
+class SparseLoopbackDisk(LoopbackDisk):
+ """A Disk backed by a sparse file via the loop module."""
+ def __init__(self, lofile, size):
+ LoopbackDisk.__init__(self, lofile, size)
+
+ def expand(self, create = False, size = None):
+ flags = os.O_WRONLY
+ if create:
+ flags |= os.O_CREAT
+ if not os.path.exists(self.lofile):
+ makedirs(os.path.dirname(self.lofile))
+
+ if size is None:
+ size = self.size
+
+ msger.debug("Extending sparse file %s to %d" % (self.lofile, size))
+ if create:
+ fd = os.open(self.lofile, flags, 0644)
+ else:
+ fd = os.open(self.lofile, flags)
+
+ os.lseek(fd, size, os.SEEK_SET)
+ os.write(fd, '\x00')
+ os.close(fd)
+
+ def truncate(self, size = None):
+ if size is None:
+ size = self.size
+
+ msger.debug("Truncating sparse file %s to %d" % (self.lofile, size))
+ fd = os.open(self.lofile, os.O_WRONLY)
+ os.ftruncate(fd, size)
+ os.close(fd)
+
+ def create(self):
+ self.expand(create = True)
+ LoopbackDisk.create(self)
+
+class Mount:
+ """A generic base class to deal with mounting things."""
+ def __init__(self, mountdir):
+ self.mountdir = mountdir
+
+ def cleanup(self):
+ self.unmount()
+
+ def mount(self, options = None):
+ pass
+
+ def unmount(self):
+ pass
+
+class DiskMount(Mount):
+ """A Mount object that handles mounting of a Disk."""
+ def __init__(self, disk, mountdir, fstype = None, rmmountdir = True):
+ Mount.__init__(self, mountdir)
+
+ self.disk = disk
+ self.fstype = fstype
+ self.rmmountdir = rmmountdir
+
+ self.mounted = False
+ self.rmdir = False
+ if fstype:
+ self.mkfscmd = find_binary_path("mkfs." + self.fstype)
+ else:
+ self.mkfscmd = None
+ self.mountcmd = find_binary_path("mount")
+ self.umountcmd = find_binary_path("umount")
+
+ def cleanup(self):
+ Mount.cleanup(self)
+ self.disk.cleanup()
+
+ def unmount(self):
+ if self.mounted:
+ msger.debug("Unmounting directory %s" % self.mountdir)
+ runner.quiet('sync') # sync the data on this mount point
+ rc = runner.show([self.umountcmd, "-l", self.mountdir])
+ if rc == 0:
+ self.mounted = False
+ else:
+ raise MountError("Failed to umount %s" % self.mountdir)
+ if self.rmdir and not self.mounted:
+ try:
+ os.rmdir(self.mountdir)
+ except OSError, e:
+ pass
+ self.rmdir = False
+
+
+ def __create(self):
+ self.disk.create()
+
+
+ def mount(self, options = None):
+ if self.mounted:
+ return
+
+ if not os.path.isdir(self.mountdir):
+ msger.debug("Creating mount point %s" % self.mountdir)
+ os.makedirs(self.mountdir)
+ self.rmdir = self.rmmountdir
+
+ self.__create()
+
+ msger.debug("Mounting %s at %s" % (self.disk.device, self.mountdir))
+ if options:
+ args = [ self.mountcmd, "-o", options, self.disk.device, self.mountdir ]
+ else:
+ args = [ self.mountcmd, self.disk.device, self.mountdir ]
+ if self.fstype:
+ args.extend(["-t", self.fstype])
+
+ rc = runner.show(args)
+ if rc != 0:
+ raise MountError("Failed to mount '%s' to '%s' with command '%s'. Retval: %s" %
+ (self.disk.device, self.mountdir, " ".join(args), rc))
+
+ self.mounted = True
+
+class ExtDiskMount(DiskMount):
+ """A DiskMount object that is able to format/resize ext[23] filesystems."""
+ def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
+ DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
+ self.blocksize = blocksize
+ self.fslabel = fslabel.replace("/", "")
+ self.uuid = None
+ self.skipformat = skipformat
+ self.fsopts = fsopts
+ self.dumpe2fs = find_binary_path("dumpe2fs")
+ self.tune2fs = find_binary_path("tune2fs")
+
+ def __parse_field(self, output, field):
+ for line in output.split("\n"):
+ if line.startswith(field + ":"):
+ return line[len(field) + 1:].strip()
+
+ raise KeyError("Failed to find field '%s' in output" % field)
+
+ def __format_filesystem(self):
+ if self.skipformat:
+ msger.debug("Skip filesystem format.")
+ return
+
+ msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
+ rc = runner.show([self.mkfscmd,
+ "-F", "-L", self.fslabel,
+ "-m", "1", "-b", str(self.blocksize),
+ self.disk.device]) # str(self.disk.size / self.blocksize)])
+ if rc != 0:
+ raise MountError("Error creating %s filesystem on disk %s" % (self.fstype, self.disk.device))
+
+ out = runner.outs([self.dumpe2fs, '-h', self.disk.device])
+
+ self.uuid = self.__parse_field(out, "Filesystem UUID")
+ msger.debug("Tuning filesystem on %s" % self.disk.device)
+ runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device])
+
+ def __resize_filesystem(self, size = None):
+ current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
+
+ if size is None:
+ size = self.disk.size
+
+ if size == current_size:
+ return
+
+ if size > current_size:
+ self.disk.expand(size)
+
+ self.__fsck()
+
+ resize2fs(self.disk.lofile, size)
+ return size
+
+ def __create(self):
+ resize = False
+ if not self.disk.fixed() and self.disk.exists():
+ resize = True
+
+ self.disk.create()
+
+ if resize:
+ self.__resize_filesystem()
+ else:
+ self.__format_filesystem()
+
+ def mount(self, options = None):
+ self.__create()
+ DiskMount.mount(self, options)
+
+ def __fsck(self):
+ msger.info("Checking filesystem %s" % self.disk.lofile)
+ runner.quiet(["/sbin/e2fsck", "-f", "-y", self.disk.lofile])
+
+ def __get_size_from_filesystem(self):
+ return int(self.__parse_field(runner.outs([self.dumpe2fs, '-h', self.disk.lofile]),
+ "Block count")) * self.blocksize
+
+ def __resize_to_minimal(self):
+ self.__fsck()
+
+ #
+ # Use a binary search to find the minimal size
+ # we can resize the image to
+ #
+ bot = 0
+ top = self.__get_size_from_filesystem()
+ while top != (bot + 1):
+ t = bot + ((top - bot) / 2)
+
+ if not resize2fs(self.disk.lofile, t):
+ top = t
+ else:
+ bot = t
+ return top
+
+ def resparse(self, size = None):
+ self.cleanup()
+ if size == 0:
+ minsize = 0
+ else:
+ minsize = self.__resize_to_minimal()
+ self.disk.truncate(minsize)
+
+ self.__resize_filesystem(size)
+ return minsize
+
+class VfatDiskMount(DiskMount):
+ """A DiskMount object that is able to format vfat/msdos filesystems."""
+ def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
+ DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
+ self.blocksize = blocksize
+ self.fslabel = fslabel.replace("/", "")
+ self.uuid = "%08X" % int(time.time())
+ self.skipformat = skipformat
+ self.fsopts = fsopts
+ self.fsckcmd = find_binary_path("fsck." + self.fstype)
+
+ def __format_filesystem(self):
+ if self.skipformat:
+ msger.debug("Skip filesystem format.")
+ return
+
+ msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
+ rc = runner.show([self.mkfscmd, "-n", self.fslabel, "-i", self.uuid, self.disk.device])
+ if rc != 0:
+ raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
+
+ msger.verbose("Tuning filesystem on %s" % self.disk.device)
+
+ def __resize_filesystem(self, size = None):
+ current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
+
+ if size is None:
+ size = self.disk.size
+
+ if size == current_size:
+ return
+
+ if size > current_size:
+ self.disk.expand(size)
+
+ self.__fsck()
+
+ #resize2fs(self.disk.lofile, size)
+ return size
+
+ def __create(self):
+ resize = False
+ if not self.disk.fixed() and self.disk.exists():
+ resize = True
+
+ self.disk.create()
+
+ if resize:
+ self.__resize_filesystem()
+ else:
+ self.__format_filesystem()
+
+ def mount(self, options = None):
+ self.__create()
+ DiskMount.mount(self, options)
+
+ def __fsck(self):
+ msger.debug("Checking filesystem %s" % self.disk.lofile)
+ runner.show([self.fsckcmd, "-y", self.disk.lofile])
+
+ def __get_size_from_filesystem(self):
+ return self.disk.size
+
+ def __resize_to_minimal(self):
+ self.__fsck()
+
+ #
+ # Use a binary search to find the minimal size
+ # we can resize the image to
+ #
+ bot = 0
+ top = self.__get_size_from_filesystem()
+ return top
+
+ def resparse(self, size = None):
+ self.cleanup()
+ minsize = self.__resize_to_minimal()
+ self.disk.truncate(minsize)
+ self.__resize_filesystem(size)
+ return minsize
+
+class BtrfsDiskMount(DiskMount):
+ """A DiskMount object that is able to format/resize btrfs filesystems."""
+ def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
+ self.__check_btrfs()
+ DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
+ self.blocksize = blocksize
+ self.fslabel = fslabel.replace("/", "")
+ self.uuid = None
+ self.skipformat = skipformat
+ self.fsopts = fsopts
+ self.blkidcmd = find_binary_path("blkid")
+ self.btrfsckcmd = find_binary_path("btrfsck")
+
+ def __check_btrfs(self):
+ found = False
+ """ Need to load btrfs module to mount it """
+ load_module("btrfs")
+ for line in open("/proc/filesystems").xreadlines():
+ if line.find("btrfs") > -1:
+ found = True
+ break
+ if not found:
+ raise MountError("Your system can't mount btrfs filesystem, please make sure your kernel has btrfs support and the module btrfs.ko has been loaded.")
+
+ # disable selinux, selinux will block write
+ if os.path.exists("/usr/sbin/setenforce"):
+ runner.show(["/usr/sbin/setenforce", "0"])
+
+ def __parse_field(self, output, field):
+ for line in output.split(" "):
+ if line.startswith(field + "="):
+ return line[len(field) + 1:].strip().replace("\"", "")
+
+ raise KeyError("Failed to find field '%s' in output" % field)
+
+ def __format_filesystem(self):
+ if self.skipformat:
+ msger.debug("Skip filesystem format.")
+ return
+
+ msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
+ rc = runner.show([self.mkfscmd, "-L", self.fslabel, self.disk.device])
+ if rc != 0:
+ raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
+
+ self.uuid = self.__parse_field(runner.outs([self.blkidcmd, self.disk.device]), "UUID")
+
+ def __resize_filesystem(self, size = None):
+ current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
+
+ if size is None:
+ size = self.disk.size
+
+ if size == current_size:
+ return
+
+ if size > current_size:
+ self.disk.expand(size)
+
+ self.__fsck()
+ return size
+
+ def __create(self):
+ resize = False
+ if not self.disk.fixed() and self.disk.exists():
+ resize = True
+
+ self.disk.create()
+
+ if resize:
+ self.__resize_filesystem()
+ else:
+ self.__format_filesystem()
+
+ def mount(self, options = None):
+ self.__create()
+ DiskMount.mount(self, options)
+
+ def __fsck(self):
+ msger.debug("Checking filesystem %s" % self.disk.lofile)
+ runner.quiet([self.btrfsckcmd, self.disk.lofile])
+
+ def __get_size_from_filesystem(self):
+ return self.disk.size
+
+ def __resize_to_minimal(self):
+ self.__fsck()
+
+ return self.__get_size_from_filesystem()
+
+ def resparse(self, size = None):
+ self.cleanup()
+ minsize = self.__resize_to_minimal()
+ self.disk.truncate(minsize)
+ self.__resize_filesystem(size)
+ return minsize
+
+class DeviceMapperSnapshot(object):
+ def __init__(self, imgloop, cowloop):
+ self.imgloop = imgloop
+ self.cowloop = cowloop
+
+ self.__created = False
+ self.__name = None
+ self.dmsetupcmd = find_binary_path("dmsetup")
+
+ """Load dm_snapshot if it isn't loaded"""
+ load_module("dm_snapshot")
+
+ def get_path(self):
+ if self.__name is None:
+ return None
+ return os.path.join("/dev/mapper", self.__name)
+ path = property(get_path)
+
+ def create(self):
+ if self.__created:
+ return
+
+ self.imgloop.create()
+ self.cowloop.create()
+
+ self.__name = "imgcreate-%d-%d" % (os.getpid(),
+ random.randint(0, 2**16))
+
+ size = os.stat(self.imgloop.lofile)[stat.ST_SIZE]
+
+ table = "0 %d snapshot %s %s p 8" % (size / 512,
+ self.imgloop.device,
+ self.cowloop.device)
+
+ args = [self.dmsetupcmd, "create", self.__name, "--table", table]
+ if runner.show(args) != 0:
+ self.cowloop.cleanup()
+ self.imgloop.cleanup()
+ raise SnapshotError("Could not create snapshot device using: " + ' '.join(args))
+
+ self.__created = True
+
+ def remove(self, ignore_errors = False):
+ if not self.__created:
+ return
+
+ time.sleep(2)
+ rc = runner.show([self.dmsetupcmd, "remove", self.__name])
+ if not ignore_errors and rc != 0:
+ raise SnapshotError("Could not remove snapshot device")
+
+ self.__name = None
+ self.__created = False
+
+ self.cowloop.cleanup()
+ self.imgloop.cleanup()
+
+ def get_cow_used(self):
+ if not self.__created:
+ return 0
+
+ #
+ # dmsetup status on a snapshot returns e.g.
+ # "0 8388608 snapshot 416/1048576"
+ # or, more generally:
+ # "A B snapshot C/D"
+ # where C is the number of 512 byte sectors in use
+ #
+ out = runner.outs([self.dmsetupcmd, "status", self.__name])
+ try:
+ return int((out.split()[3]).split('/')[0]) * 512
+ except ValueError:
+ raise SnapshotError("Failed to parse dmsetup status: " + out)
+
+def create_image_minimizer(path, image, minimal_size):
+ """
+ Builds a copy-on-write image which can be used to
+ create a device-mapper snapshot of an image where
+ the image's filesystem is as small as possible
+
+ The steps taken are:
+ 1) Create a sparse COW
+ 2) Loopback mount the image and the COW
+ 3) Create a device-mapper snapshot of the image
+ using the COW
+ 4) Resize the filesystem to the minimal size
+ 5) Determine the amount of space used in the COW
+ 6) Restroy the device-mapper snapshot
+ 7) Truncate the COW, removing unused space
+ 8) Create a squashfs of the COW
+ """
+ imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter
+
+ cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"),
+ 64L * 1024L * 1024L)
+
+ snapshot = DeviceMapperSnapshot(imgloop, cowloop)
+
+ try:
+ snapshot.create()
+
+ resize2fs(snapshot.path, minimal_size)
+
+ cow_used = snapshot.get_cow_used()
+ finally:
+ snapshot.remove(ignore_errors = (not sys.exc_info()[0] is None))
+
+ cowloop.truncate(cow_used)
+
+ mksquashfs(cowloop.lofile, path)
+
+ os.unlink(cowloop.lofile)
+
+def load_module(module):
+ found = False
+ for line in open('/proc/modules').xreadlines():
+ if line.startswith("%s " % module):
+ found = True
+ break
+ if not found:
+ msger.info("Loading %s..." % module)
+ runner.quiet(['modprobe', module])
+
+class LoopDevice(object):
+ def __init__(self, loopid=None):
+ self.device = None
+ self.loopid = loopid
+ self.created = False
+ self.kpartxcmd = find_binary_path("kpartx")
+ self.losetupcmd = find_binary_path("losetup")
+
+ import atexit
+ atexit.register(self.close)
+
+ def _genloopid(self):
+ import glob
+ fint = lambda x: x[9:].isdigit() and int(x[9:]) or 0
+ maxid = 1 + max(filter(lambda x: x<100,
+ map(fint, glob.glob("/dev/loop[0-9]*"))))
+ if maxid < 10: maxid = 10
+ if maxid >= 100: raise
+ return maxid
+
+ def _kpseek(self, device):
+ rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device])
+ if rc != 0:
+ raise MountError("Can't query dm snapshot on %s" % device)
+ for line in out.splitlines():
+ if line and line.startswith("loop"):
+ return True
+ return False
+
+ def _loseek(self, device):
+ import re
+ rc, out = runner.runtool([self.losetupcmd, '-a'])
+ if rc != 0:
+ raise MountError("Failed to run 'losetup -a'")
+ for line in out.splitlines():
+ m = re.match("([^:]+): .*", line)
+ if m and m.group(1) == device:
+ return True
+ return False
+
+ def create(self):
+ if not self.created:
+ if not self.loopid:
+ self.loopid = self._genloopid()
+ self.device = "/dev/loop%d" % self.loopid
+ if os.path.exists(self.device):
+ if self._loseek(self.device):
+ raise MountError("Device busy: %s" % self.device)
+ else:
+ self.created = True
+ return
+ try:
+ os.mknod(self.device,
+ 0664 | stat.S_IFBLK,
+ os.makedev(7, self.loopid))
+ except:
+ raise MountError("Failed to create device %s" % self.device)
+ else:
+ self.created = True
+
+ def close(self):
+ if self.created:
+ try:
+ self.cleanup()
+ self.device = None
+ except MountError, e:
+ msger.error("%s" % e)
+
+ def cleanup(self):
+
+ if self.device is None:
+ return
+
+
+ if self._kpseek(self.device):
+ if self.created:
+ for i in range(3, os.sysconf("SC_OPEN_MAX")):
+ try:
+ os.close(i)
+ except:
+ pass
+ runner.quiet([self.kpartxcmd, "-d", self.device])
+ if self._loseek(self.device):
+ runner.quiet([self.losetupcmd, "-d", self.device])
+ # FIXME: should sleep a while between two loseek
+ if self._loseek(self.device):
+ msger.warning("Can't cleanup loop device %s" % self.device)
+ else:
+ os.unlink(self.device)
+
+def get_loop_device(losetupcmd, lofile):
+ import fcntl
+ fp = open("/var/lock/__mic_loopdev.lock", 'w')
+ fcntl.flock(fp, fcntl.LOCK_EX)
+ try:
+ devinst = LoopDevice()
+ devinst.create()
+ except:
+ rc, out = runner.runtool([losetupcmd, "-f"])
+ if rc != 0:
+ raise MountError("1-Failed to allocate loop device for '%s'" % lofile)
+ loopdev = out.split()[0]
+ else:
+ loopdev = devinst.device
+ finally:
+ try:
+ fcntl.flock(fp, fcntl.LOCK_UN)
+ fp.close()
+ os.unlink('/var/lock/__mic_loopdev.lock')
+ except:
+ pass
+
+ rc = runner.show([losetupcmd, loopdev, lofile])
+ if rc != 0:
+ raise MountError("2-Failed to allocate loop device for '%s'" % lofile)
+
+ return loopdev
+
+
diff --git a/mic/utils/misc.py b/mic/utils/misc.py
new file mode 100644
index 0000000..932d180
--- /dev/null
+++ b/mic/utils/misc.py
@@ -0,0 +1,925 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2010, 2011 Intel Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+import os
+import sys
+import tempfile
+import re
+import shutil
+import glob
+import hashlib
+import subprocess
+import platform
+import rpmmisc
+
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
+
+try:
+ import sqlite3 as sqlite
+except ImportError:
+ import sqlite
+
+try:
+ from xml.etree import cElementTree
+except ImportError:
+ import cElementTree
+xmlparse = cElementTree.parse
+
+from errors import *
+from fs_related import *
+from rpmmisc import myurlgrab
+from proxy import get_proxy_for
+import runner
+
+from mic import msger
+
+RPM_RE = re.compile("(.*)\.(.*) (.*)-(.*)")
+RPM_FMT = "%(name)s.%(arch)s %(ver_rel)s"
+SRPM_RE = re.compile("(.*)-(\d+.*)-(\d+\.\d+).src.rpm")
+
+def get_distro():
+ """Detect linux distribution, support "meego"
+ """
+
+ support_dists = ('SuSE',
+ 'debian',
+ 'fedora',
+ 'redhat',
+ 'centos',
+ 'meego',
+ 'moblin',
+ 'tizen')
+ try:
+ (dist, ver, id) = platform.linux_distribution( \
+ supported_dists = support_dists)
+ except:
+ (dist, ver, id) = platform.dist( \
+ supported_dists = support_dists)
+
+ return (dist, ver, id)
+
+def get_distro_str():
+ """Get composited string for current linux distribution
+ """
+ (dist, ver, id) = get_distro()
+
+ if not dist:
+ return 'Unknown Linux Distro'
+ else:
+ return ' '.join(map(str.strip, (dist, ver, id)))
+
+_LOOP_RULE_PTH = "/etc/udev/rules.d/80-prevent-loop-present.rules"
+def hide_loopdev_presentation():
+ try:
+ with open(_LOOP_RULE_PTH, 'w') as wf:
+ wf.write('KERNEL=="loop*", ENV{UDISKS_PRESENTATION_HIDE}="1"')
+
+ runner.quiet('udevadm trigger')
+ except:
+ pass
+
+def unhide_loopdev_presentation():
+ try:
+ os.unlink(_LOOP_RULE_PTH)
+ runner.quiet('udevadm trigger')
+ except:
+ pass
+
+def extract_rpm(rpmfile, targetdir):
+ rpm2cpio = find_binary_path("rpm2cpio")
+ cpio = find_binary_path("cpio")
+
+ olddir = os.getcwd()
+ os.chdir(targetdir)
+
+ msger.verbose("Extract rpm file with cpio: %s" % rpmfile)
+ p1 = subprocess.Popen([rpm2cpio, rpmfile], stdout=subprocess.PIPE)
+ p2 = subprocess.Popen([cpio, "-idv"], stdin=p1.stdout,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (sout, serr) = p2.communicate()
+ msger.verbose(sout or serr)
+
+ os.chdir(olddir)
+
+def compressing(fpath, method):
+ comp_map = {
+ "gz": "gzip",
+ "bz2": "bzip2"
+ }
+ if method not in comp_map:
+ raise CreatorError("Unsupport compress format: %s, valid values: %s"
+ % (method, ','.join(comp_map.keys())))
+ cmd = find_binary_path(comp_map[method])
+ rc = runner.show([cmd, "-f", fpath])
+ if rc:
+ raise CreatorError("Failed to %s file: %s" % (comp_map[method], fpath))
+
+def taring(dstfile, target):
+ import tarfile
+ basen, ext = os.path.splitext(dstfile)
+ comp = {".tar": None,
+ ".gz": "gz", # for .tar.gz
+ ".bz2": "bz2", # for .tar.bz2
+ ".tgz": "gz",
+ ".tbz": "bz2"}[ext]
+
+ # specify tarball file path
+ if not comp:
+ tarpath = dstfile
+ elif basen.endswith(".tar"):
+ tarpath = basen
+ else:
+ tarpath = basen + ".tar"
+ wf = tarfile.open(tarpath, 'w')
+
+ if os.path.isdir(target):
+ for item in os.listdir(target):
+ wf.add(os.path.join(target, item), item)
+ else:
+ wf.add(target, os.path.basename(target))
+ wf.close()
+
+ if comp:
+ compressing(tarpath, comp)
+ # when dstfile ext is ".tgz" and ".tbz", should rename
+ if not basen.endswith(".tar"):
+ shutil.move("%s.%s" % (tarpath, comp), dstfile)
+
+def ziping(dstfile, target):
+ import zipfile
+ wf = zipfile.ZipFile(dstfile, 'w', compression=zipfile.ZIP_DEFLATED)
+ if os.path.isdir(target):
+ for item in os.listdir(target):
+ fpath = os.path.join(target, item)
+ if not os.path.isfile(fpath):
+ continue
+ wf.write(fpath, item, zipfile.ZIP_DEFLATED)
+ else:
+ wf.write(target, os.path.basename(target), zipfile.ZIP_DEFLATED)
+ wf.close()
+
+pack_formats = {
+ ".tar": taring,
+ ".tar.gz": taring,
+ ".tar.bz2": taring,
+ ".tgz": taring,
+ ".tbz": taring,
+ ".zip": ziping,
+}
+
+def packing(dstfile, target):
+ (base, ext) = os.path.splitext(dstfile)
+ if ext in (".gz", ".bz2") and base.endswith(".tar"):
+ ext = ".tar" + ext
+ if ext not in pack_formats:
+ raise CreatorError("Unsupport pack format: %s, valid values: %s"
+ % (ext, ','.join(pack_formats.keys())))
+ func = pack_formats[ext]
+ # func should be callable
+ func(dstfile, target)
+
+def human_size(size):
+ """Return human readable string for Bytes size
+ """
+
+ if size <= 0:
+ return "0M"
+ import math
+ measure = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
+ expo = int(math.log(size, 1024))
+ mant = float(size/math.pow(1024, expo))
+ return "{0:.1f}{1:s}".format(mant, measure[expo])
+
+def check_space_pre_cp(src, dst):
+ """Check whether disk space is enough before 'cp' like
+ operations, else exception will be raised.
+ """
+
+ srcsize = get_file_size(src) * 1024 * 1024
+ freesize = get_filesystem_avail(dst)
+ if srcsize > freesize:
+ raise CreatorError("space on %s(%s) is not enough for about %s files"
+ % (dst, human_size(freesize), human_size(srcsize)))
+
+def get_md5sum(fpath):
+ blksize = 65536 # should be optimized enough
+
+ md5sum = md5()
+ with open(fpath, 'rb') as f:
+ while True:
+ data = f.read(blksize)
+ if not data:
+ break
+ md5sum.update(data)
+ return md5sum.hexdigest()
+
+def normalize_ksfile(ksconf, release, arch):
+ def _clrtempks():
+ try:
+ os.unlink(ksconf)
+ except:
+ pass
+
+ if not os.path.exists(ksconf):
+ return
+
+ if not release:
+ release = "latest"
+ if not arch or re.match(r'i.86', arch):
+ arch = "ia32"
+
+ with open(ksconf) as f:
+ ksc = f.read()
+
+ if "@ARCH@" in ksc or "@BUILD_ID@" in ksc:
+ msger.info("Substitute macro variable @BUILD_ID@/@ARCH in ks: %s" % ksconf)
+ ksc = ksc.replace("@ARCH@", arch)
+ ksc = ksc.replace("@BUILD_ID@", release)
+ fd, ksconf = tempfile.mkstemp(prefix=os.path.basename(ksconf), dir="/tmp/")
+ os.write(fd, ksc)
+ os.close(fd)
+
+ msger.debug('new ks path %s' % ksconf)
+
+ import atexit
+ atexit.register(_clrtempks)
+
+ return ksconf
+
+def _check_meego_chroot(rootdir):
+ if not os.path.exists(rootdir + "/etc/moblin-release") and \
+ not os.path.exists(rootdir + "/etc/meego-release") and \
+ not os.path.exists(rootdir + "/etc/tizen-release"):
+ raise CreatorError("Directory %s is not a MeeGo/Tizen chroot env"\
+ % rootdir)
+
+ if not glob.glob(rootdir + "/boot/vmlinuz-*"):
+ raise CreatorError("Failed to find kernel module under %s" % rootdir)
+
+ return
+
+def selinux_check(arch, fstypes):
+ try:
+ getenforce = find_binary_path('getenforce')
+ except CreatorError:
+ return
+
+ selinux_status = runner.outs([getenforce])
+ if arch and arch.startswith("arm") and selinux_status == "Enforcing":
+ raise CreatorError("Can't create arm image if selinux is enabled, "
+ "please run 'setenforce 0' to disable selinux")
+
+ use_btrfs = filter(lambda typ: typ == 'btrfs', fstypes)
+ if use_btrfs and selinux_status == "Enforcing":
+ raise CreatorError("Can't create btrfs image if selinux is enabled,"
+ " please run 'setenforce 0' to disable selinux")
+
+def get_image_type(path):
+ def _get_extension_name(path):
+ match = re.search("(?<=\.)\w+$", path)
+ if match:
+ return match.group(0)
+ else:
+ return None
+
+ if os.path.isdir(path):
+ _check_meego_chroot(path)
+ return "fs"
+
+ maptab = {
+ "tar": "loop",
+ "raw":"raw",
+ "vmdk":"vmdk",
+ "vdi":"vdi",
+ "iso":"livecd",
+ "usbimg":"liveusb",
+ }
+
+ extension = _get_extension_name(path)
+ if extension in maptab:
+ return maptab[extension]
+
+ fd = open(path, "rb")
+ file_header = fd.read(1024)
+ fd.close()
+ vdi_flag = "<<< Sun VirtualBox Disk Image >>>"
+ if file_header[0:len(vdi_flag)] == vdi_flag:
+ return maptab["vdi"]
+
+ output = runner.outs(['file', path])
+ isoptn = re.compile(r".*ISO 9660 CD-ROM filesystem.*(bootable).*")
+ usbimgptn = re.compile(r".*x86 boot sector.*active.*")
+ rawptn = re.compile(r".*x86 boot sector.*")
+ vmdkptn = re.compile(r".*VMware. disk image.*")
+ ext3fsimgptn = re.compile(r".*Linux.*ext3 filesystem data.*")
+ ext4fsimgptn = re.compile(r".*Linux.*ext4 filesystem data.*")
+ btrfsimgptn = re.compile(r".*BTRFS.*")
+ if isoptn.match(output):
+ return maptab["iso"]
+ elif usbimgptn.match(output):
+ return maptab["usbimg"]
+ elif rawptn.match(output):
+ return maptab["raw"]
+ elif vmdkptn.match(output):
+ return maptab["vmdk"]
+ elif ext3fsimgptn.match(output):
+ return "ext3fsimg"
+ elif ext4fsimgptn.match(output):
+ return "ext4fsimg"
+ elif btrfsimgptn.match(output):
+ return "btrfsimg"
+ else:
+ raise CreatorError("Cannot detect the type of image: %s" % path)
+
+def get_file_size(file):
+ """ Return size in MB unit """
+ rc, duOutput = runner.runtool(['du', "-s", "-b", "-B", "1M", file])
+ if rc != 0:
+ raise CreatorError("Failed to run %s" % du)
+
+ size1 = int(duOutput.split()[0])
+ rc, duOutput = runner.runtool(['du', "-s", "-B", "1M", file])
+ if rc != 0:
+ raise CreatorError("Failed to run %s" % du)
+
+ size2 = int(duOutput.split()[0])
+ if size1 > size2:
+ return size1
+ else:
+ return size2
+
+def get_filesystem_avail(fs):
+ vfstat = os.statvfs(fs)
+ return vfstat.f_bavail * vfstat.f_bsize
+
+def convert_image(srcimg, srcfmt, dstimg, dstfmt):
+ #convert disk format
+ if dstfmt != "raw":
+ raise CreatorError("Invalid destination image format: %s" % dstfmt)
+ msger.debug("converting %s image to %s" % (srcimg, dstimg))
+ if srcfmt == "vmdk":
+ path = find_binary_path("qemu-img")
+ argv = [path, "convert", "-f", "vmdk", srcimg, "-O", dstfmt, dstimg]
+ elif srcfmt == "vdi":
+ path = find_binary_path("VBoxManage")
+ argv = [path, "internalcommands", "converttoraw", srcimg, dstimg]
+ else:
+ raise CreatorError("Invalid soure image format: %s" % srcfmt)
+
+ rc = runner.show(argv)
+ if rc == 0:
+ msger.debug("convert successful")
+ if rc != 0:
+ raise CreatorError("Unable to convert disk to %s" % dstfmt)
+
+def uncompress_squashfs(squashfsimg, outdir):
+ """Uncompress file system from squshfs image"""
+ unsquashfs = find_binary_path("unsquashfs")
+ args = [ unsquashfs, "-d", outdir, squashfsimg ]
+ rc = runner.show(args)
+ if (rc != 0):
+ raise SquashfsError("Failed to uncompress %s." % squashfsimg)
+
+def mkdtemp(dir = "/var/tmp", prefix = "mic-tmp-"):
+ """ FIXME: use the dir in mic.conf instead """
+
+ makedirs(dir)
+ return tempfile.mkdtemp(dir = dir, prefix = prefix)
+
+def get_repostrs_from_ks(ks):
+ def _get_temp_reponame(baseurl):
+ md5obj = hashlib.md5(baseurl)
+ tmpreponame = "%s" % md5obj.hexdigest()
+ return tmpreponame
+
+ kickstart_repos = []
+
+ for repodata in ks.handler.repo.repoList:
+ repo = {}
+ for attr in ('name',
+ 'baseurl',
+ 'mirrorlist',
+ 'includepkgs', # val is list
+ 'excludepkgs', # val is list
+ 'cost', # int
+ 'priority',# int
+ 'save',
+ 'proxy',
+ 'proxyuser',
+ 'proxypasswd',
+ 'proxypasswd',
+ 'debuginfo',
+ 'source',
+ 'gpgkey',
+ 'ssl_verify'):
+ if hasattr(repodata, attr) and getattr(repodata, attr):
+ repo[attr] = getattr(repodata, attr)
+
+ if 'name' not in repo:
+ repo['name'] = _get_temp_reponame(repodata.baseurl)
+
+ kickstart_repos.append(repo)
+
+ return kickstart_repos
+
+def _get_uncompressed_data_from_url(url, filename, proxies):
+ filename = myurlgrab(url, filename, proxies)
+ suffix = None
+ if filename.endswith(".gz"):
+ suffix = ".gz"
+ runner.quiet(['gunzip', "-f", filename])
+ elif filename.endswith(".bz2"):
+ suffix = ".bz2"
+ runner.quiet(['bunzip2', "-f", filename])
+ if suffix:
+ filename = filename.replace(suffix, "")
+ return filename
+
+def _get_metadata_from_repo(baseurl, proxies, cachedir, reponame, filename,
+ sumtype=None, checksum=None):
+ url = os.path.join(baseurl, filename)
+ filename_tmp = str("%s/%s/%s" % (cachedir, reponame, os.path.basename(filename)))
+ if os.path.splitext(filename_tmp)[1] in (".gz", ".bz2"):
+ filename = os.path.splitext(filename_tmp)[0]
+ else:
+ filename = filename_tmp
+ if sumtype and checksum and os.path.exists(filename):
+ sumcmd = "%ssum" % sumtype
+ file_checksum = runner.outs([sumcmd, filename]).split()[0]
+ if file_checksum == checksum:
+ return filename
+ return _get_uncompressed_data_from_url(url,filename_tmp,proxies)
+
+def get_metadata_from_repos(repos, cachedir):
+ my_repo_metadata = []
+ for repo in repos:
+ reponame = repo['name']
+ baseurl = repo['baseurl']
+
+
+ if 'proxy' in repo:
+ proxy = repo['proxy']
+ else:
+ proxy = get_proxy_for(baseurl)
+
+ proxies = None
+ if proxy:
+ proxies = {str(baseurl.split(":")[0]):str(proxy)}
+
+ makedirs(os.path.join(cachedir, reponame))
+ url = os.path.join(baseurl, "repodata/repomd.xml")
+ filename = os.path.join(cachedir, reponame, 'repomd.xml')
+ repomd = myurlgrab(url, filename, proxies)
+ try:
+ root = xmlparse(repomd)
+ except SyntaxError:
+ raise CreatorError("repomd.xml syntax error.")
+
+ ns = root.getroot().tag
+ ns = ns[0:ns.rindex("}")+1]
+
+ filepaths = {}
+ checksums = {}
+ sumtypes = {}
+
+ for elm in root.getiterator("%sdata" % ns):
+ if elm.attrib["type"] == "patterns":
+ filepaths['patterns'] = elm.find("%slocation" % ns).attrib['href']
+ checksums['patterns'] = elm.find("%sopen-checksum" % ns).text
+ sumtypes['patterns'] = elm.find("%sopen-checksum" % ns).attrib['type']
+ break
+
+ for elm in root.getiterator("%sdata" % ns):
+ if elm.attrib["type"] in ("group_gz", "group"):
+ filepaths['comps'] = elm.find("%slocation" % ns).attrib['href']
+ checksums['comps'] = elm.find("%sopen-checksum" % ns).text
+ sumtypes['comps'] = elm.find("%sopen-checksum" % ns).attrib['type']
+ break
+
+ primary_type = None
+ for elm in root.getiterator("%sdata" % ns):
+ if elm.attrib["type"] in ("primary_db", "primary"):
+ primary_type = elm.attrib["type"]
+ filepaths['primary'] = elm.find("%slocation" % ns).attrib['href']
+ checksums['primary'] = elm.find("%sopen-checksum" % ns).text
+ sumtypes['primary'] = elm.find("%sopen-checksum" % ns).attrib['type']
+ break
+
+ if not primary_type:
+ continue
+
+ for item in ("primary", "patterns", "comps"):
+ if item not in filepaths:
+ filepaths[item] = None
+ continue
+ if not filepaths[item]:
+ continue
+ filepaths[item] = _get_metadata_from_repo(baseurl,
+ proxies,
+ cachedir,
+ reponame,
+ filepaths[item],
+ sumtypes[item],
+ checksums[item])
+
+ """ Get repo key """
+ try:
+ repokey = _get_metadata_from_repo(baseurl,
+ proxies,
+ cachedir,
+ reponame,
+ "repodata/repomd.xml.key")
+ except CreatorError:
+ repokey = None
+ msger.debug("\ncan't get %s/%s" % (baseurl, "repodata/repomd.xml.key"))
+
+ my_repo_metadata.append({"name":reponame,
+ "baseurl":baseurl,
+ "repomd":repomd,
+ "primary":filepaths['primary'],
+ "cachedir":cachedir,
+ "proxies":proxies,
+ "patterns":filepaths['patterns'],
+ "comps":filepaths['comps'],
+ "repokey":repokey})
+
+ return my_repo_metadata
+
+def get_rpmver_in_repo(repometadata):
+ for repo in repometadata:
+ if repo["primary"].endswith(".xml"):
+ root = xmlparse(repo["primary"])
+ ns = root.getroot().tag
+ ns = ns[0:ns.rindex("}")+1]
+
+ versionlist = []
+ for elm in root.getiterator("%spackage" % ns):
+ if elm.find("%sname" % ns).text == 'rpm':
+ for node in elm.getchildren():
+ if node.tag == "%sversion" % ns:
+ versionlist.append(node.attrib['ver'])
+
+ if versionlist:
+ return reversed(
+ sorted(
+ versionlist,
+ key = lambda ver: map(int, ver.split('.')))).next()
+
+ elif repo["primary"].endswith(".sqlite"):
+ con = sqlite.connect(repo["primary"])
+ for row in con.execute("select version from packages where "
+ "name=\"rpm\" ORDER by version DESC"):
+ con.close()
+ return row[0]
+
+ return None
+
+def get_arch(repometadata):
+ archlist = []
+ for repo in repometadata:
+ if repo["primary"].endswith(".xml"):
+ root = xmlparse(repo["primary"])
+ ns = root.getroot().tag
+ ns = ns[0:ns.rindex("}")+1]
+ for elm in root.getiterator("%spackage" % ns):
+ if elm.find("%sarch" % ns).text not in ("noarch", "src"):
+ arch = elm.find("%sarch" % ns).text
+ if arch not in archlist:
+ archlist.append(arch)
+ elif repo["primary"].endswith(".sqlite"):
+ con = sqlite.connect(repo["primary"])
+ for row in con.execute("select arch from packages where arch not in (\"src\", \"noarch\")"):
+ if row[0] not in archlist:
+ archlist.append(row[0])
+
+ con.close()
+
+ uniq_arch = []
+ for i in range(len(archlist)):
+ if archlist[i] not in rpmmisc.archPolicies.keys():
+ continue
+ need_append = True
+ j = 0
+ while j < len(uniq_arch):
+ if archlist[i] in rpmmisc.archPolicies[uniq_arch[j]].split(':'):
+ need_append = False
+ break
+ if uniq_arch[j] in rpmmisc.archPolicies[archlist[i]].split(':'):
+ if need_append:
+ uniq_arch[j] = archlist[i]
+ need_append = False
+ else:
+ uniq_arch.remove(uniq_arch[j])
+ continue
+ j += 1
+ if need_append:
+ uniq_arch.append(archlist[i])
+
+ return uniq_arch, archlist
+
+def get_package(pkg, repometadata, arch = None):
+ ver = ""
+ target_repo = None
+ for repo in repometadata:
+ if repo["primary"].endswith(".xml"):
+ root = xmlparse(repo["primary"])
+ ns = root.getroot().tag
+ ns = ns[0:ns.rindex("}")+1]
+ for elm in root.getiterator("%spackage" % ns):
+ if elm.find("%sname" % ns).text == pkg:
+ if elm.find("%sarch" % ns).text != "src":
+ version = elm.find("%sversion" % ns)
+ tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
+ if tmpver > ver:
+ ver = tmpver
+ location = elm.find("%slocation" % ns)
+ pkgpath = "%s" % location.attrib['href']
+ target_repo = repo
+ break
+ if repo["primary"].endswith(".sqlite"):
+ con = sqlite.connect(repo["primary"])
+ if not arch:
+ for row in con.execute("select version, release,location_href from packages where name = \"%s\" and arch != \"src\"" % pkg):
+ tmpver = "%s-%s" % (row[0], row[1])
+ if tmpver > ver:
+ pkgpath = "%s" % row[2]
+ target_repo = repo
+ break
+ else:
+ for row in con.execute("select version, release,location_href from packages where name = \"%s\"" % pkg):
+ tmpver = "%s-%s" % (row[0], row[1])
+ if tmpver > ver:
+ pkgpath = "%s" % row[2]
+ target_repo = repo
+ break
+ con.close()
+ if target_repo:
+ makedirs("%s/%s/packages" % (target_repo["cachedir"], target_repo["name"]))
+ url = os.path.join(target_repo["baseurl"], pkgpath)
+ filename = str("%s/%s/packages/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath)))
+ pkg = myurlgrab(str(url), filename, target_repo["proxies"])
+ return pkg
+ else:
+ return None
+
+def get_source_name(pkg, repometadata):
+
+ def get_bin_name(pkg):
+ m = RPM_RE.match(pkg)
+ if m:
+ return m.group(1)
+ return None
+
+ def get_src_name(srpm):
+ m = SRPM_RE.match(srpm)
+ if m:
+ return m.group(1)
+ return None
+
+ ver = ""
+ target_repo = None
+
+ pkg_name = get_bin_name(pkg)
+ if not pkg_name:
+ return None
+
+ for repo in repometadata:
+ if repo["primary"].endswith(".xml"):
+ root = xmlparse(repo["primary"])
+ ns = root.getroot().tag
+ ns = ns[0:ns.rindex("}")+1]
+ for elm in root.getiterator("%spackage" % ns):
+ if elm.find("%sname" % ns).text == pkg_name:
+ if elm.find("%sarch" % ns).text != "src":
+ version = elm.find("%sversion" % ns)
+ tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
+ if tmpver > ver:
+ ver = tmpver
+ fmt = elm.find("%sformat" % ns)
+ if fmt:
+ fns = fmt.getchildren()[0].tag
+ fns = fns[0:fns.rindex("}")+1]
+ pkgpath = fmt.find("%ssourcerpm" % fns).text
+ target_repo = repo
+ break
+
+ if repo["primary"].endswith(".sqlite"):
+ con = sqlite.connect(repo["primary"])
+ for row in con.execute("select version, release, rpm_sourcerpm from packages where name = \"%s\" and arch != \"src\"" % pkg_name):
+ tmpver = "%s-%s" % (row[0], row[1])
+ if tmpver > ver:
+ pkgpath = "%s" % row[2]
+ target_repo = repo
+ break
+ con.close()
+ if target_repo:
+ return get_src_name(pkgpath)
+ else:
+ return None
+
+def get_pkglist_in_patterns(group, patterns):
+ found = False
+ pkglist = []
+ try:
+ root = xmlparse(patterns)
+ except SyntaxError:
+ raise SyntaxError("%s syntax error." % patterns)
+
+ for elm in list(root.getroot()):
+ ns = elm.tag
+ ns = ns[0:ns.rindex("}")+1]
+ name = elm.find("%sname" % ns)
+ summary = elm.find("%ssummary" % ns)
+ if name.text == group or summary.text == group:
+ found = True
+ break
+
+ if not found:
+ return pkglist
+
+ found = False
+ for requires in list(elm):
+ if requires.tag.endswith("requires"):
+ found = True
+ break
+
+ if not found:
+ return pkglist
+
+ for pkg in list(requires):
+ pkgname = pkg.attrib["name"]
+ if pkgname not in pkglist:
+ pkglist.append(pkgname)
+
+ return pkglist
+
+def get_pkglist_in_comps(group, comps):
+ found = False
+ pkglist = []
+ try:
+ root = xmlparse(comps)
+ except SyntaxError:
+ raise SyntaxError("%s syntax error." % comps)
+
+ for elm in root.getiterator("group"):
+ id = elm.find("id")
+ name = elm.find("name")
+ if id.text == group or name.text == group:
+ packagelist = elm.find("packagelist")
+ found = True
+ break
+
+ if not found:
+ return pkglist
+
+ for require in elm.getiterator("packagereq"):
+ if require.tag.endswith("packagereq"):
+ pkgname = require.text
+ if pkgname not in pkglist:
+ pkglist.append(pkgname)
+
+ return pkglist
+
+def is_statically_linked(binary):
+ return ", statically linked, " in runner.outs(['file', binary])
+
+def setup_qemu_emulator(rootdir, arch):
+ # mount binfmt_misc if it doesn't exist
+ if not os.path.exists("/proc/sys/fs/binfmt_misc"):
+ modprobecmd = find_binary_path("modprobe")
+ runner.show([modprobecmd, "binfmt_misc"])
+ if not os.path.exists("/proc/sys/fs/binfmt_misc/register"):
+ mountcmd = find_binary_path("mount")
+ runner.show([mountcmd, "-t", "binfmt_misc", "none", "/proc/sys/fs/binfmt_misc"])
+
+ # qemu_emulator is a special case, we can't use find_binary_path
+ # qemu emulator should be a statically-linked executable file
+ qemu_emulator = "/usr/bin/qemu-arm"
+ if not os.path.exists(qemu_emulator) or not is_statically_linked(qemu_emulator):
+ qemu_emulator = "/usr/bin/qemu-arm-static"
+ if not os.path.exists(qemu_emulator):
+ raise CreatorError("Please install a statically-linked qemu-arm")
+
+ # qemu emulator version check
+ armv7_list = [arch for arch in rpmmisc.archPolicies.keys() if arch.startswith('armv7')]
+ if arch in armv7_list: # need qemu (>=0.13.0)
+ qemuout = runner.outs([qemu_emulator, "-h"])
+ m = re.search("version\s*([.\d]+)", qemuout)
+ if m:
+ qemu_version = m.group(1)
+ if qemu_version < "0.13":
+ raise CreatorError("Requires %s version >=0.13 for %s" % (qemu_emulator, arch))
+ else:
+ msger.warning("Can't get version info of %s, please make sure it's higher than 0.13.0" % qemu_emulator)
+
+ if not os.path.exists(rootdir + "/usr/bin"):
+ makedirs(rootdir + "/usr/bin")
+ shutil.copy(qemu_emulator, rootdir + qemu_emulator)
+
+ # disable selinux, selinux will block qemu emulator to run
+ if os.path.exists("/usr/sbin/setenforce"):
+ msger.info('Try to disable selinux')
+ runner.show(["/usr/sbin/setenforce", "0"])
+
+ node = "/proc/sys/fs/binfmt_misc/arm"
+ if is_statically_linked(qemu_emulator) and os.path.exists(node):
+ return qemu_emulator
+
+ # unregister it if it has been registered and is a dynamically-linked executable
+ if not is_statically_linked(qemu_emulator) and os.path.exists(node):
+ qemu_unregister_string = "-1\n"
+ fd = open("/proc/sys/fs/binfmt_misc/arm", "w")
+ fd.write(qemu_unregister_string)
+ fd.close()
+
+ # register qemu emulator for interpreting other arch executable file
+ if not os.path.exists(node):
+ qemu_arm_string = ":arm:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfa\\xff\\xff\\xff:%s:\n" % qemu_emulator
+ fd = open("/proc/sys/fs/binfmt_misc/register", "w")
+ fd.write(qemu_arm_string)
+ fd.close()
+
+ return qemu_emulator
+
+def SrcpkgsDownload(pkgs, repometadata, instroot, cachedir):
+ def get_source_repometadata(repometadata):
+ src_repometadata=[]
+ for repo in repometadata:
+ if repo["name"].endswith("-source"):
+ src_repometadata.append(repo)
+ if src_repometadata:
+ return src_repometadata
+ return None
+
+ def get_src_name(srpm):
+ m = SRPM_RE.match(srpm)
+ if m:
+ return m.group(1)
+ return None
+
+ src_repometadata = get_source_repometadata(repometadata)
+
+ if not src_repometadata:
+ msger.warning("No source repo found")
+ return None
+
+ src_pkgs = []
+ lpkgs_dict = {}
+ lpkgs_path = []
+ for repo in src_repometadata:
+ cachepath = "%s/%s/packages/*.src.rpm" %(cachedir, repo["name"])
+ lpkgs_path += glob.glob(cachepath)
+
+ for lpkg in lpkgs_path:
+ lpkg_name = get_src_name(os.path.basename(lpkg))
+ lpkgs_dict[lpkg_name] = lpkg
+ localpkgs = lpkgs_dict.keys()
+
+ cached_count = 0
+ destdir = instroot+'/usr/src/SRPMS'
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+
+ srcpkgset = set()
+ for _pkg in pkgs:
+ srcpkg_name = get_source_name(_pkg, repometadata)
+ if not srcpkg_name:
+ continue
+ srcpkgset.add(srcpkg_name)
+
+ for pkg in list(srcpkgset):
+ if pkg in localpkgs:
+ cached_count += 1
+ shutil.copy(lpkgs_dict[pkg], destdir)
+ src_pkgs.append(os.path.basename(lpkgs_dict[pkg]))
+ else:
+ src_pkg = get_package(pkg, src_repometadata, 'src')
+ if src_pkg:
+ shutil.copy(src_pkg, destdir)
+ src_pkgs.append(src_pkg)
+ msger.info("%d source packages gotten from cache" % cached_count)
+
+ return src_pkgs
+
+def strip_end(text, suffix):
+ if not text.endswith(suffix):
+ return text
+ return text[:-len(suffix)]
diff --git a/mic/utils/partitionedfs.py b/mic/utils/partitionedfs.py
new file mode 100644
index 0000000..d6f2813
--- /dev/null
+++ b/mic/utils/partitionedfs.py
@@ -0,0 +1,657 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2009, 2010, 2011 Intel, Inc.
+# Copyright (c) 2007, 2008 Red Hat, Inc.
+# Copyright (c) 2008 Daniel P. Berrange
+# Copyright (c) 2008 David P. Huff
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+
+from mic import msger
+from mic.utils import runner
+from mic.utils.errors import MountError
+from mic.utils.fs_related import *
+
+# Lenght of MBR in sectors
+MBR_SECTOR_LEN = 1
+
+# Size of a sector in bytes
+SECTOR_SIZE = 512
+
+class PartitionedMount(Mount):
+ def __init__(self, disks, mountdir, skipformat = False):
+ Mount.__init__(self, mountdir)
+ self.disks = {}
+ for name in disks.keys():
+ self.disks[name] = { 'disk': disks[name], # Disk object
+ 'mapped': False, # True if kpartx mapping exists
+ 'numpart': 0, # Number of allocate partitions
+ 'partitions': [], # indexes to self.partitions
+ # Partitions with part num higher than 3 will
+ # be put inside extended partition.
+ 'extended': 0, # Size of extended partition
+ # Offset of next partition (in sectors)
+ 'offset': 0 }
+
+ self.partitions = []
+ self.subvolumes = []
+ self.mapped = False
+ self.mountOrder = []
+ self.unmountOrder = []
+ self.parted=find_binary_path("parted")
+ self.kpartx=find_binary_path("kpartx")
+ self.mkswap=find_binary_path("mkswap")
+ self.btrfscmd=None
+ self.mountcmd=find_binary_path("mount")
+ self.umountcmd=find_binary_path("umount")
+ self.skipformat = skipformat
+ self.snapshot_created = self.skipformat
+ # Size of a sector used in calculations
+ self.sector_size = SECTOR_SIZE
+
+ def add_partition(self, size, disk, mountpoint, fstype = None, label=None, fsopts = None, boot = False, align = None):
+ # Converting MB to sectors for parted
+ size = size * 1024 * 1024 / self.sector_size
+
+ """ We need to handle subvolumes for btrfs """
+ if fstype == "btrfs" and fsopts and fsopts.find("subvol=") != -1:
+ self.btrfscmd=find_binary_path("btrfs")
+ subvol = None
+ opts = fsopts.split(",")
+ for opt in opts:
+ if opt.find("subvol=") != -1:
+ subvol = opt.replace("subvol=", "").strip()
+ break
+ if not subvol:
+ raise MountError("No subvolume: %s" % fsopts)
+ self.subvolumes.append({'size': size, # In sectors
+ 'mountpoint': mountpoint, # Mount relative to chroot
+ 'fstype': fstype, # Filesystem type
+ 'fsopts': fsopts, # Filesystem mount options
+ 'disk': disk, # physical disk name holding partition
+ 'device': None, # kpartx device node for partition
+ 'mount': None, # Mount object
+ 'subvol': subvol, # Subvolume name
+ 'boot': boot, # Bootable flag
+ 'mounted': False # Mount flag
+ })
+
+ """ We still need partition for "/" or non-subvolume """
+ if mountpoint == "/" or not fsopts or fsopts.find("subvol=") == -1:
+ """ Don't need subvolume for "/" because it will be set as default subvolume """
+ if fsopts and fsopts.find("subvol=") != -1:
+ opts = fsopts.split(",")
+ for opt in opts:
+ if opt.strip().startswith("subvol="):
+ opts.remove(opt)
+ break
+ fsopts = ",".join(opts)
+ self.partitions.append({'size': size, # In sectors
+ 'mountpoint': mountpoint, # Mount relative to chroot
+ 'fstype': fstype, # Filesystem type
+ 'fsopts': fsopts, # Filesystem mount options
+ 'label': label, # Partition label
+ 'disk': disk, # physical disk name holding partition
+ 'device': None, # kpartx device node for partition
+ 'mount': None, # Mount object
+ 'num': None, # Partition number
+ 'boot': boot, # Bootable flag
+ 'align': align}) # Partition alignment
+
+ def __create_part_to_image(self, device, parttype, fstype, start, size):
+ # Start is included to the size so we need to substract one from the end.
+ end = start+size-1
+ msger.debug("Added '%s' part at Sector %d with size %d sectors" %
+ (parttype, start, end))
+ part_cmd = [self.parted, "-s", device, "unit", "s", "mkpart", parttype]
+ if fstype:
+ part_cmd.extend([fstype])
+ part_cmd.extend(["%d" % start, "%d" % end])
+
+ msger.debug(part_cmd)
+ rc, out = runner.runtool(part_cmd, catch=3)
+ out = out.strip()
+ if out:
+ msger.debug('"parted" output: %s' % out)
+ return rc
+
+ def __format_disks(self):
+ msger.debug("Assigning partitions to disks")
+
+ mbr_sector_skipped = False
+
+ # Go through partitions in the order they are added in .ks file
+ for n in range(len(self.partitions)):
+ p = self.partitions[n]
+
+ if not self.disks.has_key(p['disk']):
+ raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint']))
+
+ if not mbr_sector_skipped:
+ # This hack is used to remove one sector from the first partition,
+ # that is the used to the MBR.
+ p['size'] -= 1
+ mbr_sector_skipped = True
+
+ # Get the disk where the partition is located
+ d = self.disks[p['disk']]
+ d['numpart'] += 1
+
+ # alignment in sectors
+ align_sectors = None
+ # if first partition then we need to skip the first sector
+ # where the MBR is located, if the alignment isn't set
+ # See: https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey
+ if d['numpart'] == 1:
+ if p['align'] and p['align'] > 0:
+ align_sectors = p['align'] * 1024 / self.sector_size
+ else:
+ align_sectors = MBR_SECTOR_LEN
+ elif p['align']:
+ # If not first partition and we do have alignment set we need
+ # to align the partition.
+ # FIXME: This leaves a empty spaces to the disk. To fill the
+ # gaps we could enlargea the previous partition?
+
+ # Calc how much the alignment is off.
+ align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size)
+ # We need to move forward to the next alignment point
+ align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors
+
+ if align_sectors:
+ if p['align'] and p['align'] > 0:
+ msger.debug("Realignment for %s%s with %s sectors, original"
+ " offset %s, target alignment is %sK." %
+ (p['disk'], d['numpart'], align_sectors,
+ d['offset'], p['align']))
+ # p['size'] already converted in secctors
+ if p['size'] <= align_sectors:
+ raise MountError("Partition for %s is too small to handle "
+ "the alignment change." % p['mountpoint'])
+
+ # increase the offset so we actually start the partition on right alignment
+ d['offset'] += align_sectors
+
+ if d['numpart'] > 3:
+ # Increase allocation of extended partition to hold this partition
+ d['extended'] += p['size']
+ p['type'] = 'logical'
+ p['num'] = d['numpart'] + 1
+ else:
+ p['type'] = 'primary'
+ p['num'] = d['numpart']
+
+ p['start'] = d['offset']
+ d['offset'] += p['size']
+ d['partitions'].append(n)
+ msger.debug("Assigned %s to %s%d at Sector %d with size %d sectors "
+ "/ %d bytes." % (p['mountpoint'], p['disk'], p['num'],
+ p['start'], p['size'],
+ p['size'] * self.sector_size))
+
+ if self.skipformat:
+ msger.debug("Skipping disk format, because skipformat flag is set.")
+ return
+
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ msger.debug("Initializing partition table for %s" % (d['disk'].device))
+ rc, out = runner.runtool([self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3)
+ out = out.strip()
+ if out:
+ msger.debug('"parted" output: %s' % out)
+
+ if rc != 0:
+ # NOTE: We don't throw exception when return code is not 0, because
+ # parted always fails to reload part table with loop devices.
+ # This prevents us from distinguishing real errors based on return code.
+ msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (rc, d['disk'].device))
+
+ msger.debug("Creating partitions")
+
+ for p in self.partitions:
+ d = self.disks[p['disk']]
+ if p['num'] == 5:
+ self.__create_part_to_image(d['disk'].device,"extended",None,p['start'],d['extended'])
+
+ if p['fstype'] == "swap":
+ parted_fs_type = "linux-swap"
+ elif p['fstype'] == "vfat":
+ parted_fs_type = "fat32"
+ elif p['fstype'] == "msdos":
+ parted_fs_type = "fat16"
+ else:
+ # Type for ext2/ext3/ext4/btrfs
+ parted_fs_type = "ext2"
+
+ # Boot ROM of OMAP boards require vfat boot partition to have an
+ # even number of sectors.
+ if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat","msdos"] and p['size'] % 2:
+ msger.debug("Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint']))
+ p['size'] -= 1
+
+ ret = self.__create_part_to_image(d['disk'].device,p['type'],
+ parted_fs_type, p['start'],
+ p['size'])
+
+ if ret != 0:
+ # NOTE: We don't throw exception when return code is not 0, because
+ # parted always fails to reload part table with loop devices.
+ # This prevents us from distinguishing real errors based on return code.
+ msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (ret, p['mountpoint'], d['disk'].device))
+
+ if p['boot']:
+ msger.debug("Setting boot flag for partition '%s' on disk '%s'." % (p['num'],d['disk'].device))
+ boot_cmd = [self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on"]
+ msger.debug(boot_cmd)
+ rc = runner.show(boot_cmd)
+
+ if rc != 0:
+ # NOTE: We don't throw exception when return code is not 0, because
+ # parted always fails to reload part table with loop devices.
+ # This prevents us from distinguishing real errors based on return code.
+ msger.warning("parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc,p['num'],d['disk'].device))
+
+ def __map_partitions(self):
+ """Load it if dm_snapshot isn't loaded"""
+ load_module("dm_snapshot")
+
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ if d['mapped']:
+ continue
+
+ msger.debug("Running kpartx on %s" % d['disk'].device )
+ rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device])
+ kpartxOutput = kpartxOutput.splitlines()
+
+ if rc != 0:
+ raise MountError("Failed to query partition mapping for '%s'" %
+ d['disk'].device)
+
+ # Strip trailing blank and mask verbose output
+ i = 0
+ while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop":
+ i = i + 1
+ kpartxOutput = kpartxOutput[i:]
+
+ # Quick sanity check that the number of partitions matches
+ # our expectation. If it doesn't, someone broke the code
+ # further up
+ if len(kpartxOutput) != d['numpart']:
+ raise MountError("Unexpected number of partitions from kpartx: %d != %d" %
+ (len(kpartxOutput), d['numpart']))
+
+ for i in range(len(kpartxOutput)):
+ line = kpartxOutput[i]
+ newdev = line.split()[0]
+ mapperdev = "/dev/mapper/" + newdev
+ loopdev = d['disk'].device + newdev[-1]
+
+ msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev))
+ pnum = d['partitions'][i]
+ self.partitions[pnum]['device'] = loopdev
+
+ # grub's install wants partitions to be named
+ # to match their parent device + partition num
+ # kpartx doesn't work like this, so we add compat
+ # symlinks to point to /dev/mapper
+ if os.path.lexists(loopdev):
+ os.unlink(loopdev)
+ os.symlink(mapperdev, loopdev)
+
+ msger.debug("Adding partx mapping for %s" % d['disk'].device)
+ rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device])
+
+ if rc != 0:
+ # Make sure that the device maps are also removed on error case.
+ # The d['mapped'] isn't set to True if the kpartx fails so
+ # failed mapping will not be cleaned on cleanup either.
+ runner.quiet([self.kpartx, "-d", d['disk'].device])
+ raise MountError("Failed to map partitions for '%s'" %
+ d['disk'].device)
+
+ d['mapped'] = True
+
+ def __unmap_partitions(self):
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ if not d['mapped']:
+ continue
+
+ msger.debug("Removing compat symlinks")
+ for pnum in d['partitions']:
+ if self.partitions[pnum]['device'] != None:
+ os.unlink(self.partitions[pnum]['device'])
+ self.partitions[pnum]['device'] = None
+
+ msger.debug("Unmapping %s" % d['disk'].device)
+ rc = runner.quiet([self.kpartx, "-d", d['disk'].device])
+ if rc != 0:
+ raise MountError("Failed to unmap partitions for '%s'" %
+ d['disk'].device)
+
+ d['mapped'] = False
+
+ def __calculate_mountorder(self):
+ msger.debug("Calculating mount order")
+ for p in self.partitions:
+ self.mountOrder.append(p['mountpoint'])
+ self.unmountOrder.append(p['mountpoint'])
+
+ self.mountOrder.sort()
+ self.unmountOrder.sort()
+ self.unmountOrder.reverse()
+
+ def cleanup(self):
+ Mount.cleanup(self)
+ self.__unmap_partitions()
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ try:
+ d['disk'].cleanup()
+ except:
+ pass
+
+ def unmount(self):
+ self.__unmount_subvolumes()
+ for mp in self.unmountOrder:
+ if mp == 'swap':
+ continue
+ p = None
+ for p1 in self.partitions:
+ if p1['mountpoint'] == mp:
+ p = p1
+ break
+
+ if p['mount'] != None:
+ try:
+ """ Create subvolume snapshot here """
+ if p['fstype'] == "btrfs" and p['mountpoint'] == "/" and not self.snapshot_created:
+ self.__create_subvolume_snapshots(p, p["mount"])
+ p['mount'].cleanup()
+ except:
+ pass
+ p['mount'] = None
+
+ """ Only for btrfs """
+ def __get_subvolume_id(self, rootpath, subvol):
+ if not self.btrfscmd:
+ self.btrfscmd=find_binary_path("btrfs")
+ argv = [ self.btrfscmd, "subvolume", "list", rootpath ]
+
+ rc, out = runner.runtool(argv)
+ msger.debug(out)
+
+ if rc != 0:
+ raise MountError("Failed to get subvolume id from %s', return code: %d." % (rootpath, rc))
+
+ subvolid = -1
+ for line in out.splitlines():
+ if line.endswith(" path %s" % subvol):
+ subvolid = line.split()[1]
+ if not subvolid.isdigit():
+ raise MountError("Invalid subvolume id: %s" % subvolid)
+ subvolid = int(subvolid)
+ break
+ return subvolid
+
+ def __create_subvolume_metadata(self, p, pdisk):
+ if len(self.subvolumes) == 0:
+ return
+
+ argv = [ self.btrfscmd, "subvolume", "list", pdisk.mountdir ]
+ rc, out = runner.runtool(argv)
+ msger.debug(out)
+
+ if rc != 0:
+ raise MountError("Failed to get subvolume id from %s', return code: %d." % (pdisk.mountdir, rc))
+
+ subvolid_items = out.splitlines()
+ subvolume_metadata = ""
+ for subvol in self.subvolumes:
+ for line in subvolid_items:
+ if line.endswith(" path %s" % subvol["subvol"]):
+ subvolid = line.split()[1]
+ if not subvolid.isdigit():
+ raise MountError("Invalid subvolume id: %s" % subvolid)
+
+ subvolid = int(subvolid)
+ opts = subvol["fsopts"].split(",")
+ for opt in opts:
+ if opt.strip().startswith("subvol="):
+ opts.remove(opt)
+ break
+ fsopts = ",".join(opts)
+ subvolume_metadata += "%d\t%s\t%s\t%s\n" % (subvolid, subvol["subvol"], subvol['mountpoint'], fsopts)
+
+ if subvolume_metadata:
+ fd = open("%s/.subvolume_metadata" % pdisk.mountdir, "w")
+ fd.write(subvolume_metadata)
+ fd.close()
+
+ def __get_subvolume_metadata(self, p, pdisk):
+ subvolume_metadata_file = "%s/.subvolume_metadata" % pdisk.mountdir
+ if not os.path.exists(subvolume_metadata_file):
+ return
+
+ fd = open(subvolume_metadata_file, "r")
+ content = fd.read()
+ fd.close()
+
+ for line in content.splitlines():
+ items = line.split("\t")
+ if items and len(items) == 4:
+ self.subvolumes.append({'size': 0, # In sectors
+ 'mountpoint': items[2], # Mount relative to chroot
+ 'fstype': "btrfs", # Filesystem type
+ 'fsopts': items[3] + ",subvol=%s" % items[1], # Filesystem mount options
+ 'disk': p['disk'], # physical disk name holding partition
+ 'device': None, # kpartx device node for partition
+ 'mount': None, # Mount object
+ 'subvol': items[1], # Subvolume name
+ 'boot': False, # Bootable flag
+ 'mounted': False # Mount flag
+ })
+
+ def __create_subvolumes(self, p, pdisk):
+ """ Create all the subvolumes """
+
+ for subvol in self.subvolumes:
+ argv = [ self.btrfscmd, "subvolume", "create", pdisk.mountdir + "/" + subvol["subvol"]]
+
+ rc = runner.show(argv)
+ if rc != 0:
+ raise MountError("Failed to create subvolume '%s', return code: %d." % (subvol["subvol"], rc))
+
+ """ Set default subvolume, subvolume for "/" is default """
+ subvol = None
+ for subvolume in self.subvolumes:
+ if subvolume["mountpoint"] == "/" and p["disk"] == subvolume["disk"]:
+ subvol = subvolume
+ break
+
+ if subvol:
+ """ Get default subvolume id """
+ subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
+ """ Set default subvolume """
+ if subvolid != -1:
+ rc = runner.show([ self.btrfscmd, "subvolume", "set-default", "%d" % subvolid, pdisk.mountdir])
+ if rc != 0:
+ raise MountError("Failed to set default subvolume id: %d', return code: %d." % (subvolid, rc))
+
+ self.__create_subvolume_metadata(p, pdisk)
+
+ def __mount_subvolumes(self, p, pdisk):
+ if self.skipformat:
+ """ Get subvolume info """
+ self.__get_subvolume_metadata(p, pdisk)
+ """ Set default mount options """
+ if len(self.subvolumes) != 0:
+ for subvol in self.subvolumes:
+ if subvol["mountpoint"] == p["mountpoint"] == "/":
+ opts = subvol["fsopts"].split(",")
+ for opt in opts:
+ if opt.strip().startswith("subvol="):
+ opts.remove(opt)
+ break
+ pdisk.fsopts = ",".join(opts)
+ break
+
+ if len(self.subvolumes) == 0:
+ """ Return directly if no subvolumes """
+ return
+
+ """ Remount to make default subvolume mounted """
+ rc = runner.show([self.umountcmd, pdisk.mountdir])
+ if rc != 0:
+ raise MountError("Failed to umount %s" % pdisk.mountdir)
+
+ rc = runner.show([self.mountcmd, "-o", pdisk.fsopts, pdisk.disk.device, pdisk.mountdir])
+ if rc != 0:
+ raise MountError("Failed to umount %s" % pdisk.mountdir)
+
+ for subvol in self.subvolumes:
+ if subvol["mountpoint"] == "/":
+ continue
+ subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
+ if subvolid == -1:
+ msger.debug("WARNING: invalid subvolume %s" % subvol["subvol"])
+ continue
+ """ Replace subvolume name with subvolume ID """
+ opts = subvol["fsopts"].split(",")
+ for opt in opts:
+ if opt.strip().startswith("subvol="):
+ opts.remove(opt)
+ break
+
+ opts.extend(["subvolrootid=0", "subvol=%s" % subvol["subvol"]])
+ fsopts = ",".join(opts)
+ subvol['fsopts'] = fsopts
+ mountpoint = self.mountdir + subvol['mountpoint']
+ makedirs(mountpoint)
+ rc = runner.show([self.mountcmd, "-o", fsopts, pdisk.disk.device, mountpoint])
+ if rc != 0:
+ raise MountError("Failed to mount subvolume %s to %s" % (subvol["subvol"], mountpoint))
+ subvol["mounted"] = True
+
+ def __unmount_subvolumes(self):
+ """ It may be called multiple times, so we need to chekc if it is still mounted. """
+ for subvol in self.subvolumes:
+ if subvol["mountpoint"] == "/":
+ continue
+ if not subvol["mounted"]:
+ continue
+ mountpoint = self.mountdir + subvol['mountpoint']
+ rc = runner.show([self.umountcmd, mountpoint])
+ if rc != 0:
+ raise MountError("Failed to unmount subvolume %s from %s" % (subvol["subvol"], mountpoint))
+ subvol["mounted"] = False
+
+ def __create_subvolume_snapshots(self, p, pdisk):
+ import time
+
+ if self.snapshot_created:
+ return
+
+ """ Remount with subvolid=0 """
+ rc = runner.show([self.umountcmd, pdisk.mountdir])
+ if rc != 0:
+ raise MountError("Failed to umount %s" % pdisk.mountdir)
+ if pdisk.fsopts:
+ mountopts = pdisk.fsopts + ",subvolid=0"
+ else:
+ mountopts = "subvolid=0"
+ rc = runner.show([self.mountcmd, "-o", mountopts, pdisk.disk.device, pdisk.mountdir])
+ if rc != 0:
+ raise MountError("Failed to umount %s" % pdisk.mountdir)
+
+ """ Create all the subvolume snapshots """
+ snapshotts = time.strftime("%Y%m%d-%H%M")
+ for subvol in self.subvolumes:
+ subvolpath = pdisk.mountdir + "/" + subvol["subvol"]
+ snapshotpath = subvolpath + "_%s-1" % snapshotts
+ rc = runner.show([ self.btrfscmd, "subvolume", "snapshot", subvolpath, snapshotpath ])
+ if rc != 0:
+ raise MountError("Failed to create subvolume snapshot '%s' for '%s', return code: %d." % (snapshotpath, subvolpath, rc))
+
+ self.snapshot_created = True
+
+ def mount(self):
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ d['disk'].create()
+
+ self.__format_disks()
+ self.__map_partitions()
+ self.__calculate_mountorder()
+
+ for mp in self.mountOrder:
+ p = None
+ for p1 in self.partitions:
+ if p1['mountpoint'] == mp:
+ p = p1
+ break
+
+ if not p['label']:
+ if p['mountpoint'] == "/":
+ p['label'] = 'platform'
+ else:
+ p['label'] = mp.split('/')[-1]
+
+ if mp == 'swap':
+ import uuid
+ p['uuid'] = str(uuid.uuid1())
+ runner.show([self.mkswap,
+ '-L', p['label'],
+ '-U', p['uuid'],
+ p['device']])
+ continue
+
+ rmmountdir = False
+ if p['mountpoint'] == "/":
+ rmmountdir = True
+ if p['fstype'] == "vfat" or p['fstype'] == "msdos":
+ myDiskMount = VfatDiskMount
+ elif p['fstype'] in ("ext2", "ext3", "ext4"):
+ myDiskMount = ExtDiskMount
+ elif p['fstype'] == "btrfs":
+ myDiskMount = BtrfsDiskMount
+ else:
+ raise MountError("Fail to support file system " + p['fstype'])
+
+ if p['fstype'] == "btrfs" and not p['fsopts']:
+ p['fsopts'] = "subvolid=0"
+
+ pdisk = myDiskMount(RawDisk(p['size'] * self.sector_size, p['device']),
+ self.mountdir + p['mountpoint'],
+ p['fstype'],
+ 4096,
+ p['label'],
+ rmmountdir,
+ self.skipformat,
+ fsopts = p['fsopts'])
+ pdisk.mount(pdisk.fsopts)
+ if p['fstype'] == "btrfs" and p['mountpoint'] == "/":
+ if not self.skipformat:
+ self.__create_subvolumes(p, pdisk)
+ self.__mount_subvolumes(p, pdisk)
+ p['mount'] = pdisk
+ p['uuid'] = pdisk.uuid
+
+ def resparse(self, size = None):
+ # Can't re-sparse a disk image - too hard
+ pass
diff --git a/mic/utils/proxy.py b/mic/utils/proxy.py
new file mode 100644
index 0000000..136d5c0
--- /dev/null
+++ b/mic/utils/proxy.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import urlparse
+
+_my_proxies = {}
+_my_noproxy = None
+_my_noproxy_list = []
+
+def set_proxy_environ():
+ global _my_noproxy, _my_proxies
+ if not _my_proxies:
+ return
+ for key in _my_proxies.keys():
+ os.environ[key + "_proxy"] = _my_proxies[key]
+ if not _my_noproxy:
+ return
+ os.environ["no_proxy"] = _my_noproxy
+
+def unset_proxy_environ():
+ for env in ('http_proxy',
+ 'https_proxy',
+ 'ftp_proxy',
+ 'all_proxy'):
+ if env in os.environ:
+ del os.environ[env]
+
+ ENV=env.upper()
+ if ENV in os.environ:
+ del os.environ[ENV]
+
+def _set_proxies(proxy = None, no_proxy = None):
+ """Return a dictionary of scheme -> proxy server URL mappings.
+ """
+
+ global _my_noproxy, _my_proxies
+ _my_proxies = {}
+ _my_noproxy = None
+ proxies = []
+ if proxy:
+ proxies.append(("http_proxy", proxy))
+ if no_proxy:
+ proxies.append(("no_proxy", no_proxy))
+
+ # Get proxy settings from environment if not provided
+ if not proxy and not no_proxy:
+ proxies = os.environ.items()
+
+ # Remove proxy env variables, urllib2 can't handle them correctly
+ unset_proxy_environ()
+
+ for name, value in proxies:
+ name = name.lower()
+ if value and name[-6:] == '_proxy':
+ if name[0:2] != "no":
+ _my_proxies[name[:-6]] = value
+ else:
+ _my_noproxy = value
+
+def _ip_to_int(ip):
+ ipint=0
+ shift=24
+ for dec in ip.split("."):
+ ipint |= int(dec) << shift
+ shift -= 8
+ return ipint
+
+def _int_to_ip(val):
+ ipaddr=""
+ shift=0
+ for i in range(4):
+ dec = val >> shift
+ dec &= 0xff
+ ipaddr = ".%d%s" % (dec, ipaddr)
+ shift += 8
+ return ipaddr[1:]
+
+def _isip(host):
+ if host.replace(".", "").isdigit():
+ return True
+ return False
+
+def _set_noproxy_list():
+ global _my_noproxy, _my_noproxy_list
+ _my_noproxy_list = []
+ if not _my_noproxy:
+ return
+ for item in _my_noproxy.split(","):
+ item = item.strip()
+ if not item:
+ continue
+
+ if item[0] != '.' and item.find("/") == -1:
+ # Need to match it
+ _my_noproxy_list.append({"match":0,"needle":item})
+
+ elif item[0] == '.':
+ # Need to match at tail
+ _my_noproxy_list.append({"match":1,"needle":item})
+
+ elif item.find("/") > 3:
+ # IP/MASK, need to match at head
+ needle = item[0:item.find("/")].strip()
+ ip = _ip_to_int(needle)
+ netmask = 0
+ mask = item[item.find("/")+1:].strip()
+
+ if mask.isdigit():
+ netmask = int(mask)
+ netmask = ~((1<<(32-netmask)) - 1)
+ ip &= netmask
+ else:
+ shift=24
+ netmask=0
+ for dec in mask.split("."):
+ netmask |= int(dec) << shift
+ shift -= 8
+ ip &= netmask
+
+ _my_noproxy_list.append({"match":2,"needle":ip,"netmask":netmask})
+
+def _isnoproxy(url):
+ (scheme, host, path, parm, query, frag) = urlparse.urlparse(url)
+
+ if '@' in host:
+ user_pass, host = host.split('@', 1)
+
+ if ':' in host:
+ host, port = host.split(':', 1)
+
+ hostisip = _isip(host)
+ for item in _my_noproxy_list:
+ if hostisip and item["match"] <= 1:
+ continue
+
+ if item["match"] == 2 and hostisip:
+ if (_ip_to_int(host) & item["netmask"]) == item["needle"]:
+ return True
+
+ if item["match"] == 0:
+ if host == item["needle"]:
+ return True
+
+ if item["match"] == 1:
+ if host.rfind(item["needle"]) > 0:
+ return True
+
+ return False
+
+def set_proxies(proxy = None, no_proxy = None):
+ _set_proxies(proxy, no_proxy)
+ _set_noproxy_list()
+ set_proxy_environ()
+
+def get_proxy_for(url):
+ if url.startswith('file:') or _isnoproxy(url):
+ return None
+
+ type = url[0:url.index(":")]
+ proxy = None
+ if _my_proxies.has_key(type):
+ proxy = _my_proxies[type]
+ elif _my_proxies.has_key("http"):
+ proxy = _my_proxies["http"]
+ else:
+ proxy = None
+
+ return proxy
diff --git a/mic/utils/rpmmisc.py b/mic/utils/rpmmisc.py
new file mode 100644
index 0000000..581fd2c
--- /dev/null
+++ b/mic/utils/rpmmisc.py
@@ -0,0 +1,667 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2008, 2009, 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys, re
+import fcntl
+import struct
+import termios
+import rpm
+from mic import msger
+from .errors import CreatorError
+from .proxy import get_proxy_for
+import runner
+from urlgrabber import grabber, __version__ as grabber_version
+if rpm.labelCompare(grabber_version.split('.'), '3.9.0'.split('.')) == -1:
+ msger.warning("Version of python-urlgrabber is %s, lower than '3.9.0', "
+ "you may encounter some network issues" % grabber_version)
+
+def myurlgrab(url, filename, proxies, progress_obj = None):
+ g = grabber.URLGrabber()
+ if progress_obj is None:
+ progress_obj = TextProgress()
+
+ if url.startswith("file:/"):
+ file = url.replace("file:", "")
+ if not os.path.exists(file):
+ raise CreatorError("URLGrabber error: can't find file %s" % file)
+ runner.show(['cp', "-f", file, filename])
+ else:
+ try:
+ filename = g.urlgrab(url = url, filename = filename,
+ ssl_verify_host = False, ssl_verify_peer = False,
+ proxies = proxies, http_headers = (('Pragma', 'no-cache'),),
+ quote = 0, progress_obj = progress_obj)
+ except grabber.URLGrabError, e:
+ raise CreatorError("URLGrabber error: %s" % url)
+
+ return filename
+
+def terminal_width(fd=1):
+ """ Get the real terminal width """
+ try:
+ buf = 'abcdefgh'
+ buf = fcntl.ioctl(fd, termios.TIOCGWINSZ, buf)
+ return struct.unpack('hhhh', buf)[1]
+ except: # IOError
+ return 80
+
+def truncate_url(url, width):
+ return os.path.basename(url)[0:width]
+
+class TextProgress(object):
+ # make the class as singleton
+ _instance = None
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = super(TextProgress, cls).__new__(cls, *args, **kwargs)
+
+ return cls._instance
+
+ def __init__(self, totalnum = None):
+ self.total = totalnum
+ self.counter = 1
+
+ def start(self, filename, url, *args, **kwargs):
+ self.url = url
+ self.termwidth = terminal_width()
+ msger.info("\r%-*s" % (self.termwidth, " "))
+ if self.total is None:
+ msger.info("\rRetrieving %s ..." % truncate_url(self.url, self.termwidth - 15))
+ else:
+ msger.info("\rRetrieving %s [%d/%d] ..." % (truncate_url(self.url, self.termwidth - 25), self.counter, self.total))
+
+ def update(self, *args):
+ pass
+
+ def end(self, *args):
+ if self.counter == self.total:
+ msger.raw("\n")
+
+ if self.total is not None:
+ self.counter += 1
+
+class RPMInstallCallback:
+ """ Command line callback class for callbacks from the RPM library.
+ """
+
+ def __init__(self, ts, output=1):
+ self.output = output
+ self.callbackfilehandles = {}
+ self.total_actions = 0
+ self.total_installed = 0
+ self.installed_pkg_names = []
+ self.total_removed = 0
+ self.mark = "+"
+ self.marks = 40
+ self.lastmsg = None
+ self.tsInfo = None # this needs to be set for anything else to work
+ self.ts = ts
+ self.filelog = False
+ self.logString = []
+ self.headmsg = "Installing"
+
+ def _dopkgtup(self, hdr):
+ tmpepoch = hdr['epoch']
+ if tmpepoch is None: epoch = '0'
+ else: epoch = str(tmpepoch)
+
+ return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
+
+ def _makeHandle(self, hdr):
+ handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
+ hdr['release'], hdr['arch'])
+
+ return handle
+
+ def _localprint(self, msg):
+ if self.output:
+ msger.info(msg)
+
+ def _makefmt(self, percent, progress = True):
+ l = len(str(self.total_actions))
+ size = "%s.%s" % (l, l)
+ fmt_done = "[%" + size + "s/%" + size + "s]"
+ done = fmt_done % (self.total_installed + self.total_removed,
+ self.total_actions)
+ marks = self.marks - (2 * l)
+ width = "%s.%s" % (marks, marks)
+ fmt_bar = "%-" + width + "s"
+ if progress:
+ bar = fmt_bar % (self.mark * int(marks * (percent / 100.0)), )
+ fmt = "\r %-10.10s: %-20.20s " + bar + " " + done
+ else:
+ bar = fmt_bar % (self.mark * marks, )
+ fmt = " %-10.10s: %-20.20s " + bar + " " + done
+ return fmt
+
+ def _logPkgString(self, hdr):
+ """return nice representation of the package for the log"""
+ (n,a,e,v,r) = self._dopkgtup(hdr)
+ if e == '0':
+ pkg = '%s.%s %s-%s' % (n, a, v, r)
+ else:
+ pkg = '%s.%s %s:%s-%s' % (n, a, e, v, r)
+
+ return pkg
+
+ def callback(self, what, bytes, total, h, user):
+ if what == rpm.RPMCALLBACK_TRANS_START:
+ if bytes == 6:
+ self.total_actions = total
+
+ elif what == rpm.RPMCALLBACK_TRANS_PROGRESS:
+ pass
+
+ elif what == rpm.RPMCALLBACK_TRANS_STOP:
+ pass
+
+ elif what == rpm.RPMCALLBACK_INST_OPEN_FILE:
+ self.lastmsg = None
+ hdr = None
+ if h is not None:
+ try:
+ hdr, rpmloc = h
+ except:
+ rpmloc = h
+ hdr = readRpmHeader(self.ts, h)
+
+ handle = self._makeHandle(hdr)
+ fd = os.open(rpmloc, os.O_RDONLY)
+ self.callbackfilehandles[handle]=fd
+ self.total_installed += 1
+ self.installed_pkg_names.append(hdr['name'])
+ return fd
+ else:
+ self._localprint("No header - huh?")
+
+ elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
+ hdr = None
+ if h is not None:
+ try:
+ hdr, rpmloc = h
+ except:
+ rpmloc = h
+ hdr = readRpmHeader(self.ts, h)
+
+ handle = self._makeHandle(hdr)
+ os.close(self.callbackfilehandles[handle])
+ fd = 0
+
+ # log stuff
+ #pkgtup = self._dopkgtup(hdr)
+ self.logString.append(self._logPkgString(hdr))
+
+ elif what == rpm.RPMCALLBACK_INST_PROGRESS:
+ if h is not None:
+ percent = (self.total_installed*100L)/self.total_actions
+ if total > 0:
+ try:
+ hdr, rpmloc = h
+ except:
+ rpmloc = h
+
+ m = re.match("(.*)-(\d+.*)-(\d+\.\d+)\.(.+)\.rpm", os.path.basename(rpmloc))
+ if m:
+ pkgname = m.group(1)
+ else:
+ pkgname = os.path.basename(rpmloc)
+ if self.output:
+ fmt = self._makefmt(percent)
+ msg = fmt % (self.headmsg, pkgname)
+ if msg != self.lastmsg:
+ self.lastmsg = msg
+
+ msger.info(msg)
+
+ if self.total_installed == self.total_actions:
+ msger.raw('')
+ msger.verbose('\n'.join(self.logString))
+
+ elif what == rpm.RPMCALLBACK_UNINST_START:
+ pass
+
+ elif what == rpm.RPMCALLBACK_UNINST_PROGRESS:
+ pass
+
+ elif what == rpm.RPMCALLBACK_UNINST_STOP:
+ self.total_removed += 1
+
+ elif what == rpm.RPMCALLBACK_REPACKAGE_START:
+ pass
+
+ elif what == rpm.RPMCALLBACK_REPACKAGE_STOP:
+ pass
+
+ elif what == rpm.RPMCALLBACK_REPACKAGE_PROGRESS:
+ pass
+
+def readRpmHeader(ts, filename):
+ """ Read an rpm header. """
+
+ fd = os.open(filename, os.O_RDONLY)
+ h = ts.hdrFromFdno(fd)
+ os.close(fd)
+ return h
+
+def splitFilename(filename):
+ """ Pass in a standard style rpm fullname
+
+ Return a name, version, release, epoch, arch, e.g.::
+ foo-1.0-1.i386.rpm returns foo, 1.0, 1, i386
+ 1:bar-9-123a.ia64.rpm returns bar, 9, 123a, 1, ia64
+ """
+
+ if filename[-4:] == '.rpm':
+ filename = filename[:-4]
+
+ archIndex = filename.rfind('.')
+ arch = filename[archIndex+1:]
+
+ relIndex = filename[:archIndex].rfind('-')
+ rel = filename[relIndex+1:archIndex]
+
+ verIndex = filename[:relIndex].rfind('-')
+ ver = filename[verIndex+1:relIndex]
+
+ epochIndex = filename.find(':')
+ if epochIndex == -1:
+ epoch = ''
+ else:
+ epoch = filename[:epochIndex]
+
+ name = filename[epochIndex + 1:verIndex]
+ return name, ver, rel, epoch, arch
+
+def getCanonX86Arch(arch):
+ #
+ if arch == "i586":
+ f = open("/proc/cpuinfo", "r")
+ lines = f.readlines()
+ f.close()
+ for line in lines:
+ if line.startswith("model name") and line.find("Geode(TM)") != -1:
+ return "geode"
+ return arch
+ # only athlon vs i686 isn't handled with uname currently
+ if arch != "i686":
+ return arch
+
+ # if we're i686 and AuthenticAMD, then we should be an athlon
+ f = open("/proc/cpuinfo", "r")
+ lines = f.readlines()
+ f.close()
+ for line in lines:
+ if line.startswith("vendor") and line.find("AuthenticAMD") != -1:
+ return "athlon"
+ # i686 doesn't guarantee cmov, but we depend on it
+ elif line.startswith("flags") and line.find("cmov") == -1:
+ return "i586"
+
+ return arch
+
+def getCanonX86_64Arch(arch):
+ if arch != "x86_64":
+ return arch
+
+ vendor = None
+ f = open("/proc/cpuinfo", "r")
+ lines = f.readlines()
+ f.close()
+ for line in lines:
+ if line.startswith("vendor_id"):
+ vendor = line.split(':')[1]
+ break
+ if vendor is None:
+ return arch
+
+ if vendor.find("Authentic AMD") != -1 or vendor.find("AuthenticAMD") != -1:
+ return "amd64"
+ if vendor.find("GenuineIntel") != -1:
+ return "ia32e"
+ return arch
+
+def getCanonArch():
+ arch = os.uname()[4]
+
+ if (len(arch) == 4 and arch[0] == "i" and arch[2:4] == "86"):
+ return getCanonX86Arch(arch)
+
+ if arch == "x86_64":
+ return getCanonX86_64Arch(arch)
+
+ return arch
+
+# Copy from libsatsolver:poolarch.c, with cleanup
+archPolicies = {
+ "x86_64": "x86_64:i686:i586:i486:i386",
+ "i686": "i686:i586:i486:i386",
+ "i586": "i586:i486:i386",
+ "ia64": "ia64:i686:i586:i486:i386",
+ "armv7tnhl": "armv7tnhl:armv7thl:armv7nhl:armv7hl",
+ "armv7thl": "armv7thl:armv7hl",
+ "armv7nhl": "armv7nhl:armv7hl",
+ "armv7hl": "armv7hl",
+ "armv7l": "armv7l:armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv6l": "armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5tejl": "armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5tel": "armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5l": "armv5l:armv4tl:armv4l:armv3l",
+}
+
+# dict mapping arch -> ( multicompat, best personality, biarch personality )
+multilibArches = {
+ "x86_64": ( "athlon", "x86_64", "athlon" ),
+}
+
+# from yumUtils.py
+arches = {
+ # ia32
+ "athlon": "i686",
+ "i686": "i586",
+ "geode": "i586",
+ "i586": "i486",
+ "i486": "i386",
+ "i386": "noarch",
+
+ # amd64
+ "x86_64": "athlon",
+ "amd64": "x86_64",
+ "ia32e": "x86_64",
+
+ # arm
+ "armv7tnhl": "armv7nhl",
+ "armv7nhl": "armv7hl",
+ "armv7hl": "noarch",
+ "armv7l": "armv6l",
+ "armv6l": "armv5tejl",
+ "armv5tejl": "armv5tel",
+ "armv5tel": "noarch",
+
+ #itanium
+ "ia64": "noarch",
+}
+
+def isMultiLibArch(arch=None):
+ """returns true if arch is a multilib arch, false if not"""
+ if arch is None:
+ arch = getCanonArch()
+
+ if not arches.has_key(arch): # or we could check if it is noarch
+ return False
+
+ if multilibArches.has_key(arch):
+ return True
+
+ if multilibArches.has_key(arches[arch]):
+ return True
+
+ return False
+
+def getBaseArch():
+ myarch = getCanonArch()
+ if not arches.has_key(myarch):
+ return myarch
+
+ if isMultiLibArch(arch=myarch):
+ if multilibArches.has_key(myarch):
+ return myarch
+ else:
+ return arches[myarch]
+
+ if arches.has_key(myarch):
+ basearch = myarch
+ value = arches[basearch]
+ while value != 'noarch':
+ basearch = value
+ value = arches[basearch]
+
+ return basearch
+
+def checkRpmIntegrity(bin_rpm, package):
+ return runner.quiet([bin_rpm, "-K", "--nosignature", package])
+
+def checkSig(ts, package):
+ """ Takes a transaction set and a package, check it's sigs,
+ return 0 if they are all fine
+ return 1 if the gpg key can't be found
+ return 2 if the header is in someway damaged
+ return 3 if the key is not trusted
+ return 4 if the pkg is not gpg or pgp signed
+ """
+
+ value = 0
+ currentflags = ts.setVSFlags(0)
+ fdno = os.open(package, os.O_RDONLY)
+ try:
+ hdr = ts.hdrFromFdno(fdno)
+
+ except rpm.error, e:
+ if str(e) == "public key not availaiable":
+ value = 1
+ if str(e) == "public key not available":
+ value = 1
+ if str(e) == "public key not trusted":
+ value = 3
+ if str(e) == "error reading package header":
+ value = 2
+ else:
+ error, siginfo = getSigInfo(hdr)
+ if error == 101:
+ os.close(fdno)
+ del hdr
+ value = 4
+ else:
+ del hdr
+
+ try:
+ os.close(fdno)
+ except OSError:
+ pass
+
+ ts.setVSFlags(currentflags) # put things back like they were before
+ return value
+
+def getSigInfo(hdr):
+ """ checks signature from an hdr hand back signature information and/or
+ an error code
+ """
+
+ import locale
+ locale.setlocale(locale.LC_ALL, 'C')
+
+ string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|'
+ siginfo = hdr.sprintf(string)
+ if siginfo != '(none)':
+ error = 0
+ sigtype, sigdate, sigid = siginfo.split(',')
+ else:
+ error = 101
+ sigtype = 'MD5'
+ sigdate = 'None'
+ sigid = 'None'
+
+ infotuple = (sigtype, sigdate, sigid)
+ return error, infotuple
+
+def checkRepositoryEULA(name, repo):
+ """ This function is to check the EULA file if provided.
+ return True: no EULA or accepted
+ return False: user declined the EULA
+ """
+
+ import tempfile
+ import shutil
+ import urlparse
+ import urllib2 as u2
+ import httplib
+ from errors import CreatorError
+
+ def _check_and_download_url(u2opener, url, savepath):
+ try:
+ if u2opener:
+ f = u2opener.open(url)
+ else:
+ f = u2.urlopen(url)
+ except u2.HTTPError, httperror:
+ if httperror.code in (404, 503):
+ return None
+ else:
+ raise CreatorError(httperror)
+ except OSError, oserr:
+ if oserr.errno == 2:
+ return None
+ else:
+ raise CreatorError(oserr)
+ except IOError, oserr:
+ if hasattr(oserr, "reason") and oserr.reason.errno == 2:
+ return None
+ else:
+ raise CreatorError(oserr)
+ except u2.URLError, err:
+ raise CreatorError(err)
+ except httplib.HTTPException, e:
+ raise CreatorError(e)
+
+ # save to file
+ licf = open(savepath, "w")
+ licf.write(f.read())
+ licf.close()
+ f.close()
+
+ return savepath
+
+ def _pager_file(savepath):
+
+ if os.path.splitext(savepath)[1].upper() in ('.HTM', '.HTML'):
+ pagers = ('w3m', 'links', 'lynx', 'less', 'more')
+ else:
+ pagers = ('less', 'more')
+
+ file_showed = False
+ for pager in pagers:
+ cmd = "%s %s" % (pager, savepath)
+ try:
+ os.system(cmd)
+ except OSError:
+ continue
+ else:
+ file_showed = True
+ break
+
+ if not file_showed:
+ f = open(savepath)
+ msger.raw(f.read())
+ f.close()
+ msger.pause()
+
+ # when proxy needed, make urllib2 follow it
+ proxy = repo.proxy
+ proxy_username = repo.proxy_username
+ proxy_password = repo.proxy_password
+
+ if not proxy:
+ proxy = get_proxy_for(repo.baseurl[0])
+
+ handlers = []
+ auth_handler = u2.HTTPBasicAuthHandler(u2.HTTPPasswordMgrWithDefaultRealm())
+ u2opener = None
+ if proxy:
+ if proxy_username:
+ proxy_netloc = urlparse.urlsplit(proxy).netloc
+ if proxy_password:
+ proxy_url = 'http://%s:%s@%s' % (proxy_username, proxy_password, proxy_netloc)
+ else:
+ proxy_url = 'http://%s@%s' % (proxy_username, proxy_netloc)
+ else:
+ proxy_url = proxy
+
+ proxy_support = u2.ProxyHandler({'http': proxy_url,
+ 'https': proxy_url,
+ 'ftp': proxy_url})
+ handlers.append(proxy_support)
+
+ # download all remote files to one temp dir
+ baseurl = None
+ repo_lic_dir = tempfile.mkdtemp(prefix = 'repolic')
+
+ for url in repo.baseurl:
+ tmphandlers = handlers[:]
+
+ (scheme, host, path, parm, query, frag) = urlparse.urlparse(url.rstrip('/') + '/')
+ if scheme not in ("http", "https", "ftp", "ftps", "file"):
+ raise CreatorError("Error: invalid url %s" % url)
+
+ if '@' in host:
+ try:
+ user_pass, host = host.split('@', 1)
+ if ':' in user_pass:
+ user, password = user_pass.split(':', 1)
+ except ValueError, e:
+ raise CreatorError('Bad URL: %s' % url)
+
+ msger.verbose("adding HTTP auth: %s, %s" %(user, password))
+ auth_handler.add_password(None, host, user, password)
+ tmphandlers.append(auth_handler)
+ url = scheme + "://" + host + path + parm + query + frag
+
+ if tmphandlers:
+ u2opener = u2.build_opener(*tmphandlers)
+
+ # try to download
+ repo_eula_url = urlparse.urljoin(url, "LICENSE.txt")
+ repo_eula_path = _check_and_download_url(
+ u2opener,
+ repo_eula_url,
+ os.path.join(repo_lic_dir, repo.id + '_LICENSE.txt'))
+ if repo_eula_path:
+ # found
+ baseurl = url
+ break
+
+ if not baseurl:
+ shutil.rmtree(repo_lic_dir) #cleanup
+ return True
+
+ # show the license file
+ msger.info('For the software packages in this yum repo:')
+ msger.info(' %s: %s' % (name, baseurl))
+ msger.info('There is an "End User License Agreement" file that need to be checked.')
+ msger.info('Please read the terms and conditions outlined in it and answer the followed qustions.')
+ msger.pause()
+
+ _pager_file(repo_eula_path)
+
+ # Asking for the "Accept/Decline"
+ if not msger.ask('Would you agree to the terms and conditions outlined in the above End User License Agreement?'):
+ msger.warning('Will not install pkgs from this repo.')
+ shutil.rmtree(repo_lic_dir) #cleanup
+ return False
+
+ # try to find support_info.html for extra infomation
+ repo_info_url = urlparse.urljoin(baseurl, "support_info.html")
+ repo_info_path = _check_and_download_url(
+ u2opener,
+ repo_info_url,
+ os.path.join(repo_lic_dir, repo.id + '_support_info.html'))
+ if repo_info_path:
+ msger.info('There is one more file in the repo for additional support information, please read it')
+ msger.pause()
+ _pager_file(repo_info_path)
+
+ #cleanup
+ shutil.rmtree(repo_lic_dir)
+ return True
diff --git a/mic/utils/runner.py b/mic/utils/runner.py
new file mode 100644
index 0000000..9e0522a
--- /dev/null
+++ b/mic/utils/runner.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+from subprocess import *
+
+from mic import msger
+
+def runtool(cmdln_or_args, catch=1):
+ """ wrapper for most of the subprocess calls
+ input:
+ cmdln_or_args: can be both args and cmdln str (shell=True)
+ catch: 0, quitely run
+ 1, only STDOUT
+ 2, only STDERR
+ 3, both STDOUT and STDERR
+ return:
+ (rc, output)
+ if catch==0: the output will always None
+ """
+
+ if catch not in (0, 1, 2, 3):
+ # invalid catch selection, will cause exception, that's good
+ return None
+
+ if isinstance(cmdln_or_args, list):
+ cmd = cmdln_or_args[0]
+ shell = False
+ else:
+ import shlex
+ cmd = shlex.split(cmdln_or_args)[0]
+ shell = True
+
+ if catch != 3:
+ dev_null = os.open("/dev/null", os.O_WRONLY)
+
+ if catch == 0:
+ sout = dev_null
+ serr = dev_null
+ elif catch == 1:
+ sout = PIPE
+ serr = dev_null
+ elif catch == 2:
+ sout = dev_null
+ serr = PIPE
+ elif catch == 3:
+ sout = PIPE
+ serr = STDOUT
+
+ try:
+ p = Popen(cmdln_or_args, stdout=sout, stderr=serr, shell=shell)
+ (sout, serr) = p.communicate()
+ # combine stdout and stderr, filter None out
+ out = ''.join(filter(None, [sout, serr]))
+ except OSError, e:
+ if e.errno == 2:
+ # [Errno 2] No such file or directory
+ msger.error('Cannot run command: %s, lost dependency?' % cmd)
+ else:
+ raise # relay
+ finally:
+ if catch != 3:
+ os.close(dev_null)
+
+ return (p.returncode, out)
+
+def show(cmdln_or_args):
+ # show all the message using msger.verbose
+
+ rc, out = runtool(cmdln_or_args, catch=3)
+
+ if isinstance(cmdln_or_args, list):
+ cmd = ' '.join(cmdln_or_args)
+ else:
+ cmd = cmdln_or_args
+
+ msg = 'running command: "%s"' % cmd
+ if out: out = out.strip()
+ if out:
+ msg += ', with output::'
+ msg += '\n +----------------'
+ for line in out.splitlines():
+ msg += '\n | %s' % line
+ msg += '\n +----------------'
+
+ msger.verbose(msg)
+ return rc
+
+def outs(cmdln_or_args, catch=1):
+ # get the outputs of tools
+ return runtool(cmdln_or_args, catch)[1].strip()
+
+def quiet(cmdln_or_args):
+ return runtool(cmdln_or_args, catch=0)[0]
diff --git a/packaging/mic.changes b/packaging/mic.changes
new file mode 100644
index 0000000..3e8b3c6
--- /dev/null
+++ b/packaging/mic.changes
@@ -0,0 +1,182 @@
+* Thu Aug 02 2012 Gui Chen <gui.chen@intel.com> - 0.14
+- Release 0.14, changes including:
+ - use cached metadata when checksum is not changed
+ - skip non-fatal error in ks file and prompt user to handle
+ - prompt user to handle when failed to apply img configure
+ - replace hard name with device uuid in etc/fstab
+ - enhance extlinux cfg file for symbolic kernel like IVI
+ - support label assign for raw image
+ - bug fix:
+ - fix live image create failure when label assigned
+ - avoid traceback when converting unsupported type
+ - fix mic --version ugly output
+
+* Wed Jul 12 2012 Gui Chen <gui.chen@intel.com> - 0.13
+- Release 0.13, changes including:
+ - create logfile as default when --release specifid
+ - use 'gzip' and 'bzip2' to pack image instead of python
+ - automatically detect path of 'env' for chroot
+ - record version and os info in build log and logfile
+ - bug fix:
+ - fix popup message in ubuntus
+ - fix unicode issue for logfile
+ - better fix for 'chroot raw' issue
+
+* Wed Jun 20 2012 Gui Chen <gui.chen@intel.com> - 0.12
+- Release 0.12, changes including:
+ - use default value when @BUILD_ID@ and @ARCH@ not specified
+ - enhance proxy support in attachment retrieve
+ - add new --shrink opt for loop image to control img shrinking
+ - avoid invalid literal for loop device generation
+ - relocate and refactor selinux_check func
+ - remove prefix for make install
+ - bug fix:
+ - fix compres image in raw image
+ - fix src pkgs download failed issue
+ - fix convert failed issue
+
+* Fri Jun 08 2012 Gui Chen <gui.chen@intel.com> - 0.11
+- Release 0.11, changes including:
+ - support new subcmd 'auto' to handle magic line in ks
+ - enhance the handle of authentication url and https proxy
+ - support packing images together and support compressed file format
+ - reset LD_PRELOAD for chroot env
+ - centralized interface to check existing images
+ - avoid live image creating when using multi-partitions
+ - resolve the depends of python-urlgrabber
+ - bug fix:
+ - fix logfile context lost issue
+ - fix attachment package url handling
+ - fix mic ch raw failed issue
+
+* Tue May 15 2012 Gui Chen <gui.chen@intel.com> - 0.10
+- Release 0.10.4, changes including:
+ - container support using '%attachment' section in ks
+ - add --compress-to option to support zip format in loop image
+ - auto-detect config and plugindir to meet virtualenv and customized install
+ - remove all hardcoded info in setup.py and use sys.prefix for installing
+ - tolerate some OS errors in the image configurations stage
+ - extra patch:
+ - fix zypp missing password when using username passwd
+ - some fixes to enhance authentication url
+ - refine repostr structure to fix comma issue in baseurl
+
+* Fri Apr 13 2012 Gui Chen <gui.chen@intel.com> - 0.9
+- Release 0.9, changes including:
+ - support pre-install package with zypp backend
+ - sync /etc/mic/mic.conf to bootstrap
+ - enhance sorting for version comparsion in zypp
+ - rewrite chroot tar image using xml format mount point file
+ - fix the incorrect number showing in fs src pkgs download
+ - remove tests directory for dist in Makefile
+ - fix liveusb parted mkpart failure, revert mbr size expand in raw
+ - cleanup /tmp/repolic* dir in the EULA checking
+
+* Mon Mar 26 2012 Gui Chen <gui.chen@intel.com> - 0.8
+- Release 0.8, changes including:
+ - partition alignment support
+ - remove bootloader option 'quiet vga' for raw
+ - update dist files in git source
+ - update unittest, add cases for chroot, msger, runner
+ - add 40 system test case for help
+ - rewrite loop device allocation mechanism
+
+* Fri Mar 02 2012 Jian-feng Ding <jian-feng.ding@intel.com> - 0.7
+- Release 0.7, changes including:
+ - zypp backend: fixed a fatal issue of unreleasable loop devs
+ - zypp backend: more friendly output message
+ - backend: share cached rpm files between yum and zypp
+ - enhancement for multiple partition loop format
+ - make msger to accept Unicode string
+ - fixed a regression of compress option for FS format
+ - fixed issues in openSUSE12.1
+ - new written man page
+
+* Thu Feb 16 2012 Qiang Zhang <qiang.z.zhang@intel.com> - 0.6
+- Release 0.6, changes including:
+ - give hint when converted image existed
+ - conf.py: proxy scheme check
+ - space check before copy image
+ - zypp: abort with error msg for repo resolver issues
+ - runner.py refinement
+ - ks file syntax check for '%post' without '%end'
+ - support more compression formats than only bzip2
+ - fix msg NoneType issue, causing exit after install
+ - bootstrap:
+ - catch creator error when retrieving bootstrap metadata
+ - correct matching .metadata file in bootstrap
+
+* Mon Feb 06 2012 Gui Chen <gui.chen@intel.com> - 0.5
+- Release 0.5, changes including:
+ - Rewrite the algorithm of checking free space for download and install
+ - Add --shell option for convert to recreate image modified by internal shell
+ - Add -s option for chroot to unpack image
+ - Introduce --copy-kernel option for creator
+ - Remove the hardcoded default args for bootloader
+ - Disable logstderr and flush message buffer in disable_logstderr
+ - Deal with yum.conf inside yum backend by itself
+ - Bug fix:
+ - Fix rpmdb error in yum and zypp to avoid bad file descriptor message
+ - Fix MANIFEST syntax to be compliant with md5sum
+ - Correct dependencies for mic in bootstrap
+
+* Fri Jan 06 2012 Gui Chen <gui.chen@intel.com> - 0.4
+- Release 0.4, changes including:
+ - Support bootstrap mode, run with '--runtime=bootstrap'
+ - Full support for taring-to output, use 'mic ch x.tar'
+ - Break dependency between backend and baseimage
+ - Check valid repos in ks file
+ - Space check update and catch no space exception
+ - Fix no prompt when cv and ch no existed image
+ - Fix NoneType 'createopts' when convert
+ - Fix no existed local_pkgs_path
+
+* Mon Dec 26 2011 Zhang Qiang<qiang.z.zhang@intel.com> - 0.3
+- Upgrade to version 0.3, changes including:
+ - Unit test support, run 'make test'
+ - Enable proxy support in config file
+ - Refine configmgr and pluginmgr
+ - Support multi instance with different cache dir
+ - Add 47 system test case
+ - Improve md5sum generation
+ - Add repo option --ssl_verify
+ - Add option --name_prefix
+ - Reformatted code according to PEP08
+ - Backport from mic2:
+ - Add priority and cost option for repos
+ - Reinstroduced compress-disk-image option
+
+* Tue Nov 29 2011 Gui Chen <gui.chen@intel.com> - 0.2
+- Release 0.2, with the following changes:
+ - Support btrfs and ext4 fstype for creator, convertor, and chroot
+ - Append distfiles and Makefile
+ - Check arch type from repo data
+ - Set rpm dbpath to fix 'rpm -qa' issue
+ - Fix chroot issue caused by image size
+ - Improve setup.py and make it compatible with python 2.5
+ - Disable ca check for https
+ - Change default output dir name to ./mic-output
+ - untrack mic/__version__.py
+ - Fix some minor issues
+
+* Thu Oct 27 2011 Gui Chen <gui.chen@intel.com> - 0.1
+- Official release 0.1, with the following features:
+ - Support three subcommand: create, convert, chroot
+ - Support five image types: fs, loop, raw, livecd, liveusb
+ - Support two package manager backend: yum and zypp
+ - Support the following global command line options:
+ - --verbose
+ - --debug
+ - Creator subcommand support the following command line options:
+ - --logfile=LOGFILE
+ - -c CONFIG, --config=CONFIG
+ - -k CACHEDIR, --cachedir=CACHEDIR
+ - -o OUTDIR, --outdir=OUTDIR
+ - -A ARCH, --arch=ARCH
+ - --release=RID
+ - --record-pkgs=RECORD_PKGS
+ - --pkgmgr=PKGMGR
+ - --local-pkgs-path=LOCAL_PKGS_PATH
+
+ Please read README.rst for more details.
+
diff --git a/packaging/mic.spec b/packaging/mic.spec
new file mode 100644
index 0000000..10f68fc
--- /dev/null
+++ b/packaging/mic.spec
@@ -0,0 +1,76 @@
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+Name: mic
+Summary: Image Creator for Linux Distributions
+Version: 0.14
+Release: 1
+Group: System/Base
+License: GPLv2
+BuildArch: noarch
+URL: http://www.tizen.org
+Source0: %{name}-%{version}.tar.gz
+Requires: rpm-python
+Requires: util-linux
+Requires: coreutils
+Requires: python >= 2.5
+Requires: e2fsprogs
+Requires: dosfstools
+Requires: syslinux
+Requires: kpartx
+Requires: parted
+Requires: device-mapper
+Requires: cpio
+Requires: gzip
+Requires: bzip2
+Requires: qemu-arm-static
+Requires: python-urlgrabber
+#Requires: btrfs-progs
+
+
+Requires: syslinux-extlinux
+
+Requires: python-zypp
+BuildRequires: python-devel
+
+Obsoletes: mic2
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+
+%description
+The tool mic is used to create and manipulate images for Linux distributions.
+It is composed of three subcommand\: create, convert, chroot. Subcommand create
+is used to create images with different types; subcommand convert is used to
+convert an image to a specified type; subcommand chroot is used to chroot into
+an image.
+
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+%if 0%{?suse_version}
+%{__python} setup.py install --root=$RPM_BUILD_ROOT --prefix=%{_prefix}
+%else
+%{__python} setup.py install --root=$RPM_BUILD_ROOT -O1
+%endif
+
+# install man page
+mkdir -p %{buildroot}/%{_prefix}/share/man/man1
+install -m644 doc/mic.1 %{buildroot}/%{_prefix}/share/man/man1
+
+
+%files
+%defattr(-,root,root,-)
+%doc README.rst
+%{_mandir}/man1/*
+%dir %{_sysconfdir}/%{name}
+%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
+%{python_sitelib}/*
+%dir %{_prefix}/lib/%{name}
+%{_prefix}/lib/%{name}/*
+%{_bindir}/*
diff --git a/plugins/backend/yumpkgmgr.py b/plugins/backend/yumpkgmgr.py
new file mode 100644
index 0000000..d677428
--- /dev/null
+++ b/plugins/backend/yumpkgmgr.py
@@ -0,0 +1,469 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2007 Red Hat Inc.
+# Copyright (c) 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys
+import re
+import tempfile
+import glob
+from string import Template
+
+import rpmUtils
+import yum
+
+from mic import msger
+from mic.kickstart import ksparser
+from mic.utils import misc, rpmmisc
+from mic.utils.proxy import get_proxy_for
+from mic.utils.errors import CreatorError
+from mic.imager.baseimager import BaseImageCreator
+
+YUMCONF_TEMP = """[main]
+installroot=$installroot
+cachedir=/var/cache/yum
+persistdir=/var/lib/yum
+plugins=0
+reposdir=
+failovermethod=priority
+http_caching=packages
+sslverify=1
+"""
+
+class MyYumRepository(yum.yumRepo.YumRepository):
+ def __del__(self):
+ pass
+
+ def dirSetup(self):
+ super(MyYumRepository, self).dirSetup()
+ # relocate package dir
+ pkgdir = os.path.join(self.basecachedir, 'packages', self.id)
+ self.setAttribute('_dir_setup_pkgdir', pkgdir)
+ self._dirSetupMkdir_p(self.pkgdir)
+
+ def _getFile(self, url=None,
+ relative=None,
+ local=None,
+ start=None,
+ end=None,
+ copy_local=None,
+ checkfunc=None,
+ text=None,
+ reget='simple',
+ cache=True,
+ size=None):
+
+ m2c_connection = None
+ if not self.sslverify:
+ import M2Crypto
+ m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck
+ M2Crypto.SSL.Connection.clientPostConnectionCheck = None
+
+ size = int(size) if size else None
+ rvalue = super(MyYumRepository, self)._getFile(url,
+ relative,
+ local,
+ start,
+ end,
+ copy_local,
+ checkfunc,
+ text,
+ reget,
+ cache,
+ size)
+
+ if m2c_connection and \
+ not M2Crypto.SSL.Connection.clientPostConnectionCheck:
+ M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection
+
+ return rvalue
+
+from mic.pluginbase import BackendPlugin
+class Yum(BackendPlugin, yum.YumBase):
+ name = 'yum'
+
+ def __init__(self, target_arch, instroot, cachedir):
+ yum.YumBase.__init__(self)
+
+ self.cachedir = cachedir
+ self.instroot = instroot
+ self.target_arch = target_arch
+
+ if self.target_arch:
+ if not rpmUtils.arch.arches.has_key(self.target_arch):
+ rpmUtils.arch.arches["armv7hl"] = "noarch"
+ rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl"
+ rpmUtils.arch.arches["armv7tnhl"] = "armv7thl"
+ rpmUtils.arch.arches["armv7thl"] = "armv7hl"
+ rpmUtils.arch.arches["armv7nhl"] = "armv7hl"
+ self.arch.setup_arch(self.target_arch)
+
+ self.__pkgs_license = {}
+ self.__pkgs_content = {}
+
+ def doFileLogSetup(self, uid, logfile):
+ # don't do the file log for the livecd as it can lead to open fds
+ # being left and an inability to clean up after ourself
+ pass
+
+ def close(self):
+ try:
+ os.unlink(self.confpath)
+ os.unlink(self.conf.installroot + "/yum.conf")
+ except:
+ pass
+
+ if self.ts:
+ self.ts.close()
+ self._delRepos()
+ self._delSacks()
+ yum.YumBase.close(self)
+ self.closeRpmDB()
+
+ if not os.path.exists("/etc/fedora-release") and \
+ not os.path.exists("/etc/meego-release"):
+ for i in range(3, os.sysconf("SC_OPEN_MAX")):
+ try:
+ os.close(i)
+ except:
+ pass
+
+ def __del__(self):
+ pass
+
+ def _writeConf(self, confpath, installroot):
+ conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot)
+
+ f = file(confpath, "w+")
+ f.write(conf)
+ f.close()
+
+ os.chmod(confpath, 0644)
+
+ def _cleanupRpmdbLocks(self, installroot):
+ # cleans up temporary files left by bdb so that differing
+ # versions of rpm don't cause problems
+ for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
+ os.unlink(f)
+
+ def setup(self):
+ # create yum.conf
+ (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir,
+ prefix='yum.conf-')
+ os.close(fn)
+ self._writeConf(self.confpath, self.instroot)
+ self._cleanupRpmdbLocks(self.instroot)
+ # do setup
+ self.doConfigSetup(fn = self.confpath, root = self.instroot)
+ self.conf.cache = 0
+ self.doTsSetup()
+ self.doRpmDBSetup()
+ self.doRepoSetup()
+ self.doSackSetup()
+
+ def preInstall(self, pkg):
+ # FIXME: handle pre-install package
+ return None
+
+ def selectPackage(self, pkg):
+ """Select a given package.
+ Can be specified with name.arch or name*
+ """
+
+ try:
+ self.install(pattern = pkg)
+ return None
+ except yum.Errors.InstallError:
+ return "No package(s) available to install"
+ except yum.Errors.RepoError, e:
+ raise CreatorError("Unable to download from repo : %s" % (e,))
+ except yum.Errors.YumBaseError, e:
+ raise CreatorError("Unable to install: %s" % (e,))
+
+ def deselectPackage(self, pkg):
+ """Deselect package. Can be specified as name.arch or name*
+ """
+
+ sp = pkg.rsplit(".", 2)
+ txmbrs = []
+ if len(sp) == 2:
+ txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
+
+ if len(txmbrs) == 0:
+ exact, match, unmatch = yum.packages.parsePackages(
+ self.pkgSack.returnPackages(),
+ [pkg],
+ casematch=1)
+ for p in exact + match:
+ txmbrs.append(p)
+
+ if len(txmbrs) > 0:
+ for x in txmbrs:
+ self.tsInfo.remove(x.pkgtup)
+ # we also need to remove from the conditionals
+ # dict so that things don't get pulled back in as a result
+ # of them. yes, this is ugly. conditionals should die.
+ for req, pkgs in self.tsInfo.conditionals.iteritems():
+ if x in pkgs:
+ pkgs.remove(x)
+ self.tsInfo.conditionals[req] = pkgs
+ else:
+ msger.warning("No such package %s to remove" %(pkg,))
+
+ def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
+ try:
+ yum.YumBase.selectGroup(self, grp)
+ if include == ksparser.GROUP_REQUIRED:
+ for p in grp.default_packages.keys():
+ self.deselectPackage(p)
+
+ elif include == ksparser.GROUP_ALL:
+ for p in grp.optional_packages.keys():
+ self.selectPackage(p)
+
+ return None
+ except (yum.Errors.InstallError, yum.Errors.GroupsError), e:
+ return e
+ except yum.Errors.RepoError, e:
+ raise CreatorError("Unable to download from repo : %s" % (e,))
+ except yum.Errors.YumBaseError, e:
+ raise CreatorError("Unable to install: %s" % (e,))
+
+ def addRepository(self, name, url = None, mirrorlist = None, proxy = None,
+ proxy_username = None, proxy_password = None,
+ inc = None, exc = None, ssl_verify=True, cost = None,
+ priority=None):
+ # TODO: Handle priority attribute for repos
+ def _varSubstitute(option):
+ # takes a variable and substitutes like yum configs do
+ option = option.replace("$basearch", rpmUtils.arch.getBaseArch())
+ option = option.replace("$arch", rpmUtils.arch.getCanonArch())
+ return option
+
+ repo = MyYumRepository(name)
+
+ # Set proxy
+ repo.proxy = proxy
+ repo.proxy_username = proxy_username
+ repo.proxy_password = proxy_password
+
+ if url:
+ repo.baseurl.append(_varSubstitute(url))
+
+ # check LICENSE files
+ if not rpmmisc.checkRepositoryEULA(name, repo):
+ msger.warning('skip repo:%s for failed EULA confirmation' % name)
+ return None
+
+ if mirrorlist:
+ repo.mirrorlist = _varSubstitute(mirrorlist)
+
+ conf = yum.config.RepoConf()
+ for k, v in conf.iteritems():
+ if v or not hasattr(repo, k):
+ repo.setAttribute(k, v)
+
+ repo.sslverify = ssl_verify
+
+ repo.basecachedir = self.cachedir
+ repo.base_persistdir = self.conf.persistdir
+ repo.failovermethod = "priority"
+ repo.metadata_expire = 0
+ # Enable gpg check for verifying corrupt packages
+ repo.gpgcheck = 1
+ repo.enable()
+ repo.setup(0)
+ self.repos.add(repo)
+ if cost:
+ repo.cost = cost
+
+ msger.verbose('repo: %s was added' % name)
+ return repo
+
+ def installLocal(self, pkg, po=None, updateonly=False):
+ ts = rpmUtils.transaction.initReadOnlyTransaction()
+ try:
+ hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
+ except rpmUtils.RpmUtilsError, e:
+ raise yum.Errors.MiscError, \
+ 'Could not open local rpm file: %s: %s' % (pkg, e)
+
+ self.deselectPackage(hdr['name'])
+ yum.YumBase.installLocal(self, pkg, po, updateonly)
+
+ def installHasFile(self, file):
+ provides_pkg = self.whatProvides(file, None, None)
+ dlpkgs = map(
+ lambda x: x.po,
+ filter(
+ lambda txmbr: txmbr.ts_state in ("i", "u"),
+ self.tsInfo.getMembers()))
+
+ for p in dlpkgs:
+ for q in provides_pkg:
+ if (p == q):
+ return True
+
+ return False
+
+ def runInstall(self, checksize = 0):
+ os.environ["HOME"] = "/"
+ os.environ["LD_PRELOAD"] = ""
+ try:
+ (res, resmsg) = self.buildTransaction()
+ except yum.Errors.RepoError, e:
+ raise CreatorError("Unable to download from repo : %s" %(e,))
+
+ if res != 2:
+ raise CreatorError("Failed to build transaction : %s" \
+ % str.join("\n", resmsg))
+
+ dlpkgs = map(
+ lambda x: x.po,
+ filter(
+ lambda txmbr: txmbr.ts_state in ("i", "u"),
+ self.tsInfo.getMembers()))
+
+ # record all pkg and the content
+ for pkg in dlpkgs:
+ pkg_long_name = misc.RPM_FMT % {
+ 'name': pkg.name,
+ 'arch': pkg.arch,
+ 'ver_rel': pkg.printVer(),
+ }
+ self.__pkgs_content[pkg_long_name] = pkg.files
+ license = pkg.license
+ if license in self.__pkgs_license.keys():
+ self.__pkgs_license[license].append(pkg_long_name)
+ else:
+ self.__pkgs_license[license] = [pkg_long_name]
+
+ total_count = len(dlpkgs)
+ cached_count = 0
+ download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs))
+
+ msger.info("\nChecking packages cache and packages integrity ...")
+ for po in dlpkgs:
+ local = po.localPkg()
+ if not os.path.exists(local):
+ continue
+ if not self.verifyPkg(local, po, False):
+ msger.warning("Package %s is damaged: %s" \
+ % (os.path.basename(local), local))
+ else:
+ download_total_size -= int(po.packagesize)
+ cached_count +=1
+
+ cache_avail_size = misc.get_filesystem_avail(self.cachedir)
+ if cache_avail_size < download_total_size:
+ raise CreatorError("No enough space used for downloading.")
+
+ # record the total size of installed pkgs
+ pkgs_total_size = 0L
+ for x in dlpkgs:
+ if hasattr(x, 'installedsize'):
+ pkgs_total_size += int(x.installedsize)
+ else:
+ pkgs_total_size += int(x.size)
+
+ # check needed size before actually download and install
+ if checksize and pkgs_total_size > checksize:
+ raise CreatorError("No enough space used for installing, "
+ "please resize partition size in ks file")
+
+ msger.info("%d packages to be installed, "
+ "%d packages gotten from cache, "
+ "%d packages to be downloaded" \
+ % (total_count, cached_count, total_count - cached_count))
+
+ try:
+ repos = self.repos.listEnabled()
+ for repo in repos:
+ repo.setCallback(
+ rpmmisc.TextProgress(total_count - cached_count))
+
+ self.downloadPkgs(dlpkgs)
+ # FIXME: sigcheck?
+
+ self.initActionTs()
+ self.populateTs(keepold=0)
+
+ deps = self.ts.check()
+ if len(deps) != 0:
+ # This isn't fatal, Ubuntu has this issue but it is ok.
+ msger.debug(deps)
+ msger.warning("Dependency check failed!")
+
+ rc = self.ts.order()
+ if rc != 0:
+ raise CreatorError("ordering packages for installation failed")
+
+ # FIXME: callback should be refactored a little in yum
+ cb = rpmmisc.RPMInstallCallback(self.ts)
+ cb.tsInfo = self.tsInfo
+ cb.filelog = False
+
+ msger.warning('\nCaution, do NOT interrupt the installation, '
+ 'else mic cannot finish the cleanup.')
+
+ installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
+ msger.enable_logstderr(installlogfile)
+ self.runTransaction(cb)
+ self._cleanupRpmdbLocks(self.conf.installroot)
+
+ except rpmUtils.RpmUtilsError, e:
+ raise CreatorError("mic does NOT support delta rpm: %s" % e)
+ except yum.Errors.RepoError, e:
+ raise CreatorError("Unable to download from repo : %s" % e)
+ except yum.Errors.YumBaseError, e:
+ raise CreatorError("Unable to install: %s" % e)
+ finally:
+ msger.disable_logstderr()
+
+ def getAllContent(self):
+ return self.__pkgs_content
+
+ def getPkgsLicense(self):
+ return self.__pkgs_license
+
+ def getFilelist(self, pkgname):
+ if not pkgname:
+ return None
+
+ pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers())
+ if not pkg:
+ return None
+ return pkg[0].po.filelist
+
+ def package_url(self, pkgname):
+ pkgs = self.pkgSack.searchNevra(name=pkgname)
+ if pkgs:
+ proxy = None
+ proxies = None
+ url = pkgs[0].remote_url
+ repoid = pkgs[0].repoid
+ repos = filter(lambda r: r.id == repoid, self.repos.listEnabled())
+
+ if repos:
+ proxy = repos[0].proxy
+ if not proxy:
+ proxy = get_proxy_for(url)
+ if proxy:
+ proxies = {str(url.split(':')[0]): str(proxy)}
+
+ return (url, proxies)
+
+ return (None, None)
diff --git a/plugins/backend/zypppkgmgr.py b/plugins/backend/zypppkgmgr.py
new file mode 100755
index 0000000..ac1fa5d
--- /dev/null
+++ b/plugins/backend/zypppkgmgr.py
@@ -0,0 +1,864 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2010, 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import urlparse
+import rpm
+
+import zypp
+if not hasattr(zypp, 'PoolQuery') or \
+ not hasattr(zypp.RepoManager, 'loadSolvFile'):
+ raise ImportError("python-zypp in host system cannot support PoolQuery or "
+ "loadSolvFile interface, please update it to enhanced "
+ "version which can be found in repo.meego.com/tools")
+
+from mic import msger
+from mic.kickstart import ksparser
+from mic.utils import misc, rpmmisc, runner, fs_related
+from mic.utils.proxy import get_proxy_for
+from mic.utils.errors import CreatorError, RepoError, RpmError
+from mic.imager.baseimager import BaseImageCreator
+
+class RepositoryStub:
+ def __init__(self):
+ self.name = None
+ self.baseurl = []
+ self.mirrorlist = None
+ self.proxy = None
+ self.proxy_username = None
+ self.proxy_password = None
+
+ self.enabled = True
+ self.autorefresh = True
+ self.keeppackages = True
+ self.priority = None
+
+from mic.pluginbase import BackendPlugin
+class Zypp(BackendPlugin):
+ name = 'zypp'
+
+ def __init__(self, target_arch, instroot, cachedir):
+ self.cachedir = cachedir
+ self.instroot = instroot
+ self.target_arch = target_arch
+
+ self.__pkgs_license = {}
+ self.__pkgs_content = {}
+ self.repos = []
+ self.to_deselect = []
+ self.localpkgs = {}
+ self.repo_manager = None
+ self.repo_manager_options = None
+ self.Z = None
+ self.ts = None
+ self.ts_pre = None
+ self.incpkgs = {}
+ self.excpkgs = {}
+ self.pre_pkgs = []
+ self.probFilterFlags = [ rpm.RPMPROB_FILTER_OLDPACKAGE,
+ rpm.RPMPROB_FILTER_REPLACEPKG ]
+
+ self.has_prov_query = True
+
+ def doFileLogSetup(self, uid, logfile):
+ # don't do the file log for the livecd as it can lead to open fds
+ # being left and an inability to clean up after ourself
+ pass
+
+ def closeRpmDB(self):
+ pass
+
+ def close(self):
+ if self.ts:
+ self.ts.closeDB()
+ self.ts = None
+
+ if self.ts_pre:
+ self.ts_pre.closeDB()
+ self.ts = None
+
+ self.closeRpmDB()
+
+ if not os.path.exists("/etc/fedora-release") and \
+ not os.path.exists("/etc/meego-release"):
+ for i in range(3, os.sysconf("SC_OPEN_MAX")):
+ try:
+ os.close(i)
+ except:
+ pass
+
+ def __del__(self):
+ self.close()
+
+ def _cleanupRpmdbLocks(self, installroot):
+ # cleans up temporary files left by bdb so that differing
+ # versions of rpm don't cause problems
+ import glob
+ for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
+ os.unlink(f)
+
+ def setup(self):
+ self._cleanupRpmdbLocks(self.instroot)
+
+ def whatObsolete(self, pkg):
+ query = zypp.PoolQuery()
+ query.addKind(zypp.ResKind.package)
+ query.addAttribute(zypp.SolvAttr.obsoletes, pkg)
+ query.setMatchExact()
+ for pi in query.queryResults(self.Z.pool()):
+ return pi
+ return None
+
+ def _splitPkgString(self, pkg):
+ sp = pkg.rsplit(".",1)
+ name = sp[0]
+ arch = None
+ if len(sp) == 2:
+ arch = sp[1]
+ sysarch = zypp.Arch(self.target_arch)
+ if not zypp.Arch(arch).compatible_with (sysarch):
+ arch = None
+ name = ".".join(sp)
+ return name, arch
+
+ def selectPackage(self, pkg):
+ """Select a given package or package pattern, can be specified
+ with name.arch or name* or *name
+ """
+
+ if not self.Z:
+ self.__initialize_zypp()
+
+ def markPoolItem(obs, pi):
+ if obs == None:
+ pi.status().setToBeInstalled (zypp.ResStatus.USER)
+ else:
+ obs.status().setToBeInstalled (zypp.ResStatus.USER)
+
+ def cmpEVR(ed1, ed2):
+ (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
+ (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
+ return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
+
+ found = False
+ startx = pkg.startswith("*")
+ endx = pkg.endswith("*")
+ ispattern = startx or endx
+ name, arch = self._splitPkgString(pkg)
+
+ q = zypp.PoolQuery()
+ q.addKind(zypp.ResKind.package)
+
+ if ispattern:
+ if startx and not endx:
+ pattern = '%s$' % (pkg[1:])
+ if endx and not startx:
+ pattern = '^%s' % (pkg[0:-1])
+ if endx and startx:
+ pattern = '%s' % (pkg[1:-1])
+ q.setMatchRegex()
+ q.addAttribute(zypp.SolvAttr.name,pattern)
+
+ elif arch:
+ q.setMatchExact()
+ q.addAttribute(zypp.SolvAttr.name,name)
+
+ else:
+ q.setMatchExact()
+ q.addAttribute(zypp.SolvAttr.name,pkg)
+
+ for item in sorted(
+ q.queryResults(self.Z.pool()),
+ cmp=lambda x,y: cmpEVR(x.edition(), y.edition()),
+ reverse=True):
+
+ if item.name() in self.excpkgs.keys() and \
+ self.excpkgs[item.name()] == item.repoInfo().name():
+ continue
+ if item.name() in self.incpkgs.keys() and \
+ self.incpkgs[item.name()] != item.repoInfo().name():
+ continue
+
+ found = True
+ obspkg = self.whatObsolete(item.name())
+ if arch:
+ if arch == str(item.arch()):
+ item.status().setToBeInstalled (zypp.ResStatus.USER)
+ else:
+ markPoolItem(obspkg, item)
+ if not ispattern:
+ break
+
+ # Can't match using package name, then search from packge
+ # provides infomation
+ if found == False and not ispattern:
+ q.addAttribute(zypp.SolvAttr.provides, pkg)
+ q.addAttribute(zypp.SolvAttr.name,'')
+
+ for item in sorted(
+ q.queryResults(self.Z.pool()),
+ cmp=lambda x,y: cmpEVR(x.edition(), y.edition()),
+ reverse=True):
+ if item.name() in self.excpkgs.keys() and \
+ self.excpkgs[item.name()] == item.repoInfo().name():
+ continue
+ if item.name() in self.incpkgs.keys() and \
+ self.incpkgs[item.name()] != item.repoInfo().name():
+ continue
+
+ found = True
+ obspkg = self.whatObsolete(item.name())
+ markPoolItem(obspkg, item)
+ break
+
+ if found:
+ return None
+ else:
+ raise CreatorError("Unable to find package: %s" % (pkg,))
+
+ def inDeselectPackages(self, item):
+ """check if specified pacakges are in the list of inDeselectPackages
+ """
+
+ name = item.name()
+ for pkg in self.to_deselect:
+ startx = pkg.startswith("*")
+ endx = pkg.endswith("*")
+ ispattern = startx or endx
+ pkgname, pkgarch = self._splitPkgString(pkg)
+ if not ispattern:
+ if pkgarch:
+ if name == pkgname and str(item.arch()) == pkgarch:
+ return True;
+ else:
+ if name == pkgname:
+ return True;
+ else:
+ if startx and name.endswith(pkg[1:]):
+ return True;
+ if endx and name.startswith(pkg[:-1]):
+ return True;
+
+ return False;
+
+ def deselectPackage(self, pkg):
+ """collect packages should not be installed"""
+ self.to_deselect.append(pkg)
+
+ def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
+ if not self.Z:
+ self.__initialize_zypp()
+ found = False
+ q=zypp.PoolQuery()
+ q.addKind(zypp.ResKind.pattern)
+ for item in q.queryResults(self.Z.pool()):
+ summary = "%s" % item.summary()
+ name = "%s" % item.name()
+ if name == grp or summary == grp:
+ found = True
+ item.status().setToBeInstalled (zypp.ResStatus.USER)
+ break
+
+ if found:
+ if include == ksparser.GROUP_REQUIRED:
+ map(
+ lambda p: self.deselectPackage(p),
+ grp.default_packages.keys())
+
+ return None
+ else:
+ raise CreatorError("Unable to find pattern: %s" % (grp,))
+
+ def addRepository(self, name,
+ url = None,
+ mirrorlist = None,
+ proxy = None,
+ proxy_username = None,
+ proxy_password = None,
+ inc = None,
+ exc = None,
+ ssl_verify = True,
+ cost=None,
+ priority=None):
+ # TODO: Handle cost attribute for repos
+
+ if not self.repo_manager:
+ self.__initialize_repo_manager()
+
+ if not proxy and url:
+ proxy = get_proxy_for(url)
+
+ repo = RepositoryStub()
+ repo.name = name
+ repo.id = name
+ repo.proxy = proxy
+ repo.proxy_username = proxy_username
+ repo.proxy_password = proxy_password
+ repo.ssl_verify = ssl_verify
+ repo.baseurl.append(url)
+ if inc:
+ for pkg in inc:
+ self.incpkgs[pkg] = name
+ if exc:
+ for pkg in exc:
+ self.excpkgs[pkg] = name
+
+ # check LICENSE files
+ if not rpmmisc.checkRepositoryEULA(name, repo):
+ msger.warning('skip repo:%s for failed EULA confirmation' % name)
+ return None
+
+ if mirrorlist:
+ repo.mirrorlist = mirrorlist
+
+ # Enable gpg check for verifying corrupt packages
+ repo.gpgcheck = 1
+ if priority:
+ repo.priority = priority
+
+ try:
+ repo_info = zypp.RepoInfo()
+ repo_info.setAlias(repo.name)
+ repo_info.setName(repo.name)
+ repo_info.setEnabled(repo.enabled)
+ repo_info.setAutorefresh(repo.autorefresh)
+ repo_info.setKeepPackages(repo.keeppackages)
+ baseurl = zypp.Url(repo.baseurl[0])
+ if not ssl_verify:
+ baseurl.setQueryParam("ssl_verify", "no")
+ if proxy:
+ scheme, host, path, parm, query, frag = urlparse.urlparse(proxy)
+
+ proxyinfo = host.split(":")
+ host = proxyinfo[0]
+
+ port = "80"
+ if len(proxyinfo) > 1:
+ port = proxyinfo[1]
+
+ if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2:
+ host = proxy.rsplit(':', 1)[0]
+ port = proxy.rsplit(':', 1)[1]
+
+ baseurl.setQueryParam ("proxy", host)
+ baseurl.setQueryParam ("proxyport", port)
+
+ repo.baseurl[0] = baseurl.asCompleteString()
+ self.repos.append(repo)
+
+ repo_info.addBaseUrl(baseurl)
+
+ if repo.priority:
+ repo_info.setPriority(repo.priority)
+
+ self.repo_manager.addRepository(repo_info)
+
+ self.__build_repo_cache(name)
+
+ except RuntimeError, e:
+ raise CreatorError(str(e))
+
+ msger.verbose('repo: %s was added' % name)
+ return repo
+
+ def installHasFile(self, file):
+ return False
+
+ def preInstall(self, pkg):
+ self.pre_pkgs.append(pkg)
+
+ def runInstall(self, checksize = 0):
+ os.environ["HOME"] = "/"
+ os.environ["LD_PRELOAD"] = ""
+ self.buildTransaction()
+
+ todo = zypp.GetResolvablesToInsDel(self.Z.pool())
+ installed_pkgs = todo._toInstall
+ dlpkgs = []
+ for item in installed_pkgs:
+ if not zypp.isKindPattern(item) and \
+ not self.inDeselectPackages(item):
+ dlpkgs.append(item)
+
+ # record all pkg and the content
+ localpkgs = self.localpkgs.keys()
+ for pkg in dlpkgs:
+ license = ''
+ if pkg.name() in localpkgs:
+ hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()])
+ pkg_long_name = misc.RPM_FMT % {
+ 'name': hdr['name'],
+ 'arch': hdr['arch'],
+ 'ver_rel': '%s-%s' % (hdr['version'],
+ hdr['release']),
+ }
+ license = hdr['license']
+
+ else:
+ pkg_long_name = misc.RPM_FMT % {
+ 'name': pkg.name(),
+ 'arch': pkg.arch(),
+ 'ver_rel': pkg.edition(),
+ }
+
+ package = zypp.asKindPackage(pkg)
+ license = package.license()
+
+ self.__pkgs_content[pkg_long_name] = {} #TBD: to get file list
+
+ if license in self.__pkgs_license.keys():
+ self.__pkgs_license[license].append(pkg_long_name)
+ else:
+ self.__pkgs_license[license] = [pkg_long_name]
+
+ total_count = len(dlpkgs)
+ cached_count = 0
+ download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs))
+ localpkgs = self.localpkgs.keys()
+
+ msger.info("Checking packages cache and packages integrity ...")
+ for po in dlpkgs:
+ # Check if it is cached locally
+ if po.name() in localpkgs:
+ cached_count += 1
+ else:
+ local = self.getLocalPkgPath(po)
+ if os.path.exists(local):
+ if self.checkPkg(local) != 0:
+ os.unlink(local)
+ else:
+ download_total_size -= int(po.downloadSize())
+ cached_count += 1
+ cache_avail_size = misc.get_filesystem_avail(self.cachedir)
+ if cache_avail_size < download_total_size:
+ raise CreatorError("No enough space used for downloading.")
+
+ # record the total size of installed pkgs
+ install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs))
+ # check needed size before actually download and install
+
+ # FIXME: for multiple partitions for loop type, check fails
+ # skip the check temporarily
+ #if checksize and install_total_size > checksize:
+ # raise CreatorError("No enough space used for installing, "
+ # "please resize partition size in ks file")
+
+ download_count = total_count - cached_count
+ msger.info("%d packages to be installed, "
+ "%d packages gotten from cache, "
+ "%d packages to be downloaded" \
+ % (total_count, cached_count, download_count))
+
+ try:
+ if download_count > 0:
+ msger.info("Downloading packages ...")
+ self.downloadPkgs(dlpkgs, download_count)
+
+ self.installPkgs(dlpkgs)
+
+ except (RepoError, RpmError):
+ raise
+ except Exception, e:
+ raise CreatorError("Package installation failed: %s" % (e,))
+
+ def getAllContent(self):
+ return self.__pkgs_content
+
+ def getPkgsLicense(self):
+ return self.__pkgs_license
+
+ def getFilelist(self, pkgname):
+ if not pkgname:
+ return None
+
+ if not self.ts:
+ self.__initialize_transaction()
+
+ mi = self.ts.dbMatch('name', pkgname)
+ for header in mi:
+ return header['FILENAMES']
+
+ def __initialize_repo_manager(self):
+ if self.repo_manager:
+ return
+
+ # Clean up repo metadata
+ shutil.rmtree(self.cachedir + "/etc", ignore_errors = True)
+ shutil.rmtree(self.cachedir + "/solv", ignore_errors = True)
+ shutil.rmtree(self.cachedir + "/raw", ignore_errors = True)
+
+ zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE
+ | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED
+ | zypp.KeyRing.ACCEPT_UNKNOWNKEY
+ | zypp.KeyRing.TRUST_KEY_TEMPORARILY
+ )
+
+ self.repo_manager_options = \
+ zypp.RepoManagerOptions(zypp.Pathname(self.instroot))
+
+ self.repo_manager_options.knownReposPath = \
+ zypp.Pathname(self.cachedir + "/etc/zypp/repos.d")
+
+ self.repo_manager_options.repoCachePath = \
+ zypp.Pathname(self.cachedir)
+
+ self.repo_manager_options.repoRawCachePath = \
+ zypp.Pathname(self.cachedir + "/raw")
+
+ self.repo_manager_options.repoSolvCachePath = \
+ zypp.Pathname(self.cachedir + "/solv")
+
+ self.repo_manager_options.repoPackagesCachePath = \
+ zypp.Pathname(self.cachedir + "/packages")
+
+ self.repo_manager = zypp.RepoManager(self.repo_manager_options)
+
+ def __build_repo_cache(self, name):
+ repo = self.repo_manager.getRepositoryInfo(name)
+ if self.repo_manager.isCached(repo) or not repo.enabled():
+ return
+
+ msger.info('Refreshing repository: %s ...' % name)
+ self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded)
+
+ def __initialize_zypp(self):
+ if self.Z:
+ return
+
+ zconfig = zypp.ZConfig_instance()
+
+ # Set system architecture
+ if self.target_arch:
+ zconfig.setSystemArchitecture(zypp.Arch(self.target_arch))
+
+ msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture())
+
+ # repoPackagesCachePath is corrected by this
+ self.repo_manager = zypp.RepoManager(self.repo_manager_options)
+ repos = self.repo_manager.knownRepositories()
+ for repo in repos:
+ if not repo.enabled():
+ continue
+ self.repo_manager.loadFromCache(repo)
+
+ self.Z = zypp.ZYppFactory_instance().getZYpp()
+ self.Z.initializeTarget(zypp.Pathname(self.instroot))
+ self.Z.target().load()
+
+ def buildTransaction(self):
+ if not self.Z.resolver().resolvePool():
+ probs = self.Z.resolver().problems()
+
+ for problem in probs:
+ msger.warning("repo problem: %s, %s" \
+ % (problem.description().decode("utf-8"),
+ problem.details().decode("utf-8")))
+
+ raise RepoError("found %d resolver problem, abort!" \
+ % len(probs))
+
+ def getLocalPkgPath(self, po):
+ repoinfo = po.repoInfo()
+ cacheroot = repoinfo.packagesPath()
+ location= zypp.asKindPackage(po).location()
+ rpmpath = str(location.filename())
+ pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath))
+ return pkgpath
+
+ def installLocal(self, pkg, po=None, updateonly=False):
+ if not self.ts:
+ self.__initialize_transaction()
+
+ solvfile = "%s/.solv" % (self.cachedir)
+
+ rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"),
+ pkg])
+ if rc == 0:
+ f = open(solvfile, "w+")
+ f.write(out)
+ f.close()
+
+ warnmsg = self.repo_manager.loadSolvFile(solvfile,
+ os.path.basename(pkg))
+ if warnmsg:
+ msger.warning(warnmsg)
+
+ os.unlink(solvfile)
+ else:
+ msger.warning('Can not get %s solv data.' % pkg)
+
+ hdr = rpmmisc.readRpmHeader(self.ts, pkg)
+ arch = zypp.Arch(hdr['arch'])
+ sysarch = zypp.Arch(self.target_arch)
+
+ if arch.compatible_with (sysarch):
+ pkgname = hdr['name']
+ self.localpkgs[pkgname] = pkg
+ self.selectPackage(pkgname)
+ msger.info("Marking %s to be installed" % (pkg))
+
+ else:
+ msger.warning("Cannot add package %s to transaction. "
+ "Not a compatible architecture: %s" \
+ % (pkg, hdr['arch']))
+
+ def downloadPkgs(self, package_objects, count):
+ localpkgs = self.localpkgs.keys()
+ progress_obj = rpmmisc.TextProgress(count)
+
+ for po in package_objects:
+ if po.name() in localpkgs:
+ continue
+
+ filename = self.getLocalPkgPath(po)
+ if os.path.exists(filename):
+ if self.checkPkg(filename) == 0:
+ continue
+
+ dirn = os.path.dirname(filename)
+ if not os.path.exists(dirn):
+ os.makedirs(dirn)
+
+ url = self.get_url(po)
+ proxies = self.get_proxies(po)
+
+ try:
+ filename = rpmmisc.myurlgrab(url,
+ filename,
+ proxies,
+ progress_obj)
+ except CreatorError:
+ self.close()
+ raise
+
+ def preinstallPkgs(self):
+ if not self.ts_pre:
+ self.__initialize_transaction()
+
+ self.ts_pre.order()
+ cb = rpmmisc.RPMInstallCallback(self.ts_pre)
+ cb.headmsg = "Preinstall"
+ installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
+
+ # start to catch stderr output from librpm
+ msger.enable_logstderr(installlogfile)
+
+ errors = self.ts_pre.run(cb.callback, '')
+ # stop catch
+ msger.disable_logstderr()
+ self.ts_pre.closeDB()
+ self.ts_pre = None
+
+ if errors is not None:
+ if len(errors) == 0:
+ msger.warning('scriptlet or other non-fatal errors occurred '
+ 'during transaction.')
+
+ else:
+ for e in errors:
+ msger.warning(e[0])
+ raise RepoError('Could not run transaction.')
+
+ def installPkgs(self, package_objects):
+ if not self.ts:
+ self.__initialize_transaction()
+
+ # Set filters
+ probfilter = 0
+ for flag in self.probFilterFlags:
+ probfilter |= flag
+ self.ts.setProbFilter(probfilter)
+ self.ts_pre.setProbFilter(probfilter)
+
+ localpkgs = self.localpkgs.keys()
+
+ for po in package_objects:
+ pkgname = po.name()
+ if pkgname in localpkgs:
+ rpmpath = self.localpkgs[pkgname]
+ else:
+ rpmpath = self.getLocalPkgPath(po)
+
+ if not os.path.exists(rpmpath):
+ # Maybe it is a local repo
+ baseurl = str(po.repoInfo().baseUrls()[0])
+ baseurl = baseurl.strip()
+
+ location = zypp.asKindPackage(po).location()
+ location = str(location.filename())
+
+ if baseurl.startswith("file:/"):
+ rpmpath = baseurl[5:] + "/%s" % (location)
+
+ if not os.path.exists(rpmpath):
+ raise RpmError("Error: %s doesn't exist" % rpmpath)
+
+ h = rpmmisc.readRpmHeader(self.ts, rpmpath)
+
+ if pkgname in self.pre_pkgs:
+ msger.verbose("pre-install package added: %s" % pkgname)
+ self.ts_pre.addInstall(h, rpmpath, 'u')
+
+ self.ts.addInstall(h, rpmpath, 'u')
+
+ unresolved_dependencies = self.ts.check()
+ if not unresolved_dependencies:
+ if self.pre_pkgs:
+ self.preinstallPkgs()
+
+ self.ts.order()
+ cb = rpmmisc.RPMInstallCallback(self.ts)
+ installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
+
+ # start to catch stderr output from librpm
+ msger.enable_logstderr(installlogfile)
+
+ errors = self.ts.run(cb.callback, '')
+ # stop catch
+ msger.disable_logstderr()
+ self.ts.closeDB()
+ self.ts = None
+
+ if errors is not None:
+ if len(errors) == 0:
+ msger.warning('scriptlet or other non-fatal errors occurred '
+ 'during transaction.')
+
+ else:
+ for e in errors:
+ msger.warning(e[0])
+ raise RepoError('Could not run transaction.')
+
+ else:
+ for pkg, need, needflags, sense, key in unresolved_dependencies:
+ package = '-'.join(pkg)
+
+ if needflags == rpm.RPMSENSE_LESS:
+ deppkg = ' < '.join(need)
+ elif needflags == rpm.RPMSENSE_EQUAL:
+ deppkg = ' = '.join(need)
+ elif needflags == rpm.RPMSENSE_GREATER:
+ deppkg = ' > '.join(need)
+ else:
+ deppkg = '-'.join(need)
+
+ if sense == rpm.RPMDEP_SENSE_REQUIRES:
+ msger.warning("[%s] Requires [%s], which is not provided" \
+ % (package, deppkg))
+
+ elif sense == rpm.RPMDEP_SENSE_CONFLICTS:
+ msger.warning("[%s] Conflicts with [%s]" %(package,deppkg))
+
+ raise RepoError("Unresolved dependencies, transaction failed.")
+
+ def __initialize_transaction(self):
+ if not self.ts:
+ self.ts = rpm.TransactionSet(self.instroot)
+ # Set to not verify DSA signatures.
+ self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
+
+ if not self.ts_pre:
+ self.ts_pre = rpm.TransactionSet(self.instroot)
+ # Just unpack the files, don't run scripts
+ self.ts_pre.setFlags(rpm.RPMTRANS_FLAG_ALLFILES | rpm.RPMTRANS_FLAG_NOSCRIPTS)
+ # Set to not verify DSA signatures.
+ self.ts_pre.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
+
+ def checkPkg(self, pkg):
+ ret = 1
+ if not os.path.exists(pkg):
+ return ret
+ ret = rpmmisc.checkRpmIntegrity('rpm', pkg)
+ if ret != 0:
+ msger.warning("package %s is damaged: %s" \
+ % (os.path.basename(pkg), pkg))
+
+ return ret
+
+ def _add_prob_flags(self, *flags):
+ for flag in flags:
+ if flag not in self.probFilterFlags:
+ self.probFilterFlags.append(flag)
+
+ def get_proxies(self, pobj):
+ if not pobj:
+ return None
+
+ proxy = None
+ proxies = None
+ repoinfo = pobj.repoInfo()
+ reponame = "%s" % repoinfo.name()
+ repos = filter(lambda r: r.name == reponame, self.repos)
+ repourl = str(repoinfo.baseUrls()[0])
+
+ if repos:
+ proxy = repos[0].proxy
+ if not proxy:
+ proxy = get_proxy_for(repourl)
+ if proxy:
+ proxies = {str(repourl.split(':')[0]): str(proxy)}
+
+ return proxies
+
+ def get_url(self, pobj):
+ if not pobj:
+ return None
+
+ name = str(pobj.repoInfo().name())
+ try:
+ repo = filter(lambda r: r.name == name, self.repos)[0]
+ except IndexError:
+ return None
+
+ baseurl = repo.baseurl[0]
+
+ index = baseurl.find("?")
+ if index > -1:
+ baseurl = baseurl[:index]
+
+ location = zypp.asKindPackage(pobj).location()
+ location = str(location.filename())
+ if location.startswith("./"):
+ location = location[2:]
+
+ return os.path.join(baseurl, location)
+
+ def package_url(self, pkgname):
+
+ def cmpEVR(ed1, ed2):
+ (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
+ (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
+ return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
+
+ if not self.Z:
+ self.__initialize_zypp()
+
+ q = zypp.PoolQuery()
+ q.addKind(zypp.ResKind.package)
+ q.setMatchExact()
+ q.addAttribute(zypp.SolvAttr.name, pkgname)
+ items = sorted(q.queryResults(self.Z.pool()),
+ cmp=lambda x,y: cmpEVR(x.edition(), y.edition()),
+ reverse=True)
+
+ if items:
+ url = self.get_url(items[0])
+ proxies = self.get_proxies(items[0])
+ return (url, proxies)
+
+ return (None, None)
diff --git a/plugins/hook/.py b/plugins/hook/.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/hook/.py
diff --git a/plugins/hook/empty_hook.py b/plugins/hook/empty_hook.py
new file mode 100644
index 0000000..397585d
--- /dev/null
+++ b/plugins/hook/empty_hook.py
@@ -0,0 +1,3 @@
+#!/usr/bin/python
+
+# TODO: plugin base for hooks
diff --git a/plugins/imager/fs_plugin.py b/plugins/imager/fs_plugin.py
new file mode 100644
index 0000000..5988b73
--- /dev/null
+++ b/plugins/imager/fs_plugin.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import sys
+
+from mic import chroot, msger, rt_util
+from mic.utils import cmdln, misc, errors, fs_related
+from mic.imager import fs
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+
+from mic.pluginbase import ImagerPlugin
+class FsPlugin(ImagerPlugin):
+ name = 'fs'
+
+ @classmethod
+ @cmdln.option("--include-src",
+ dest="include_src",
+ action="store_true",
+ default=False,
+ help="Generate a image with source rpms included")
+ def do_create(self, subcmd, opts, *args):
+ """${cmd_name}: create fs image
+
+ Usage:
+ ${name} ${cmd_name} <ksfile> [OPTS]
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ raise errors.Usage("need one argument as the path of ks file")
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ creatoropts = configmgr.create
+ ksconf = args[0]
+
+ if not os.path.exists(ksconf):
+ raise errors.CreatorError("Can't find the file: %s" % ksconf)
+
+ recording_pkgs = []
+ if len(creatoropts['record_pkgs']) > 0:
+ recording_pkgs = creatoropts['record_pkgs']
+
+ if creatoropts['release'] is not None:
+ if 'name' not in recording_pkgs:
+ recording_pkgs.append('name')
+
+ ksconf = misc.normalize_ksfile(ksconf,
+ creatoropts['release'],
+ creatoropts['arch'])
+
+ configmgr._ksconf = ksconf
+
+ # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
+ if creatoropts['release'] is not None:
+ creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
+
+ # try to find the pkgmgr
+ pkgmgr = None
+ for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
+ if key == creatoropts['pkgmgr']:
+ pkgmgr = pcls
+ break
+
+ if not pkgmgr:
+ pkgmgrs = pluginmgr.get_plugins('backend').keys()
+ raise errors.CreatorError("Can't find package manager: %s (availables: %s)" % (creatoropts['pkgmgr'], ', '.join(pkgmgrs)))
+
+ if creatoropts['runtime']:
+ rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
+
+ creator = fs.FsImageCreator(creatoropts, pkgmgr)
+ creator._include_src = opts.include_src
+
+ if len(recording_pkgs) > 0:
+ creator._recording_pkgs = recording_pkgs
+
+ self.check_image_exists(creator.destdir,
+ creator.pack_to,
+ [creator.name],
+ creatoropts['release'])
+
+ try:
+ creator.check_depend_tools()
+ creator.mount(None, creatoropts["cachedir"])
+ creator.install()
+ #Download the source packages ###private options
+ if opts.include_src:
+ installed_pkgs = creator.get_installed_packages()
+ msger.info('--------------------------------------------------')
+ msger.info('Generating the image with source rpms included ...')
+ if not misc.SrcpkgsDownload(installed_pkgs, creatoropts["repomd"], creator._instroot, creatoropts["cachedir"]):
+ msger.warning("Source packages can't be downloaded")
+
+ creator.configure(creatoropts["repomd"])
+ creator.copy_kernel()
+ creator.unmount()
+ creator.package(creatoropts["outdir"])
+ if creatoropts['release'] is not None:
+ creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
+ creator.print_outimage_info()
+ except errors.CreatorError:
+ raise
+ finally:
+ creator.cleanup()
+
+ msger.info("Finished.")
+ return 0
+
+ @classmethod
+ def do_chroot(self, target):#chroot.py parse opts&args
+ try:
+ envcmd = fs_related.find_binary_inchroot("env", target)
+ if envcmd:
+ cmdline = "%s HOME=/root /bin/bash" % envcmd
+ else:
+ cmdline = "/bin/bash"
+ chroot.chroot(target, None, cmdline)
+ finally:
+ chroot.cleanup_after_chroot("dir", None, None, None)
+ return 1
+
diff --git a/plugins/imager/livecd_plugin.py b/plugins/imager/livecd_plugin.py
new file mode 100644
index 0000000..d62b20c
--- /dev/null
+++ b/plugins/imager/livecd_plugin.py
@@ -0,0 +1,251 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import tempfile
+
+from mic import chroot, msger, rt_util
+from mic.utils import misc, fs_related, errors
+from mic.conf import configmgr
+import mic.imager.livecd as livecd
+from mic.plugin import pluginmgr
+
+from mic.pluginbase import ImagerPlugin
+class LiveCDPlugin(ImagerPlugin):
+ name = 'livecd'
+
+ @classmethod
+ def do_create(self, subcmd, opts, *args):
+ """${cmd_name}: create livecd image
+
+ Usage:
+ ${name} ${cmd_name} <ksfile> [OPTS]
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ raise errors.Usage("need one argument as the path of ks file")
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ creatoropts = configmgr.create
+ ksconf = args[0]
+
+ if not os.path.exists(ksconf):
+ raise errors.CreatorError("Can't find the file: %s" % ksconf)
+
+ if creatoropts['arch'] and creatoropts['arch'].startswith('arm'):
+ msger.warning('livecd cannot support arm images, Quit')
+ return
+
+ recording_pkgs = []
+ if len(creatoropts['record_pkgs']) > 0:
+ recording_pkgs = creatoropts['record_pkgs']
+
+ if creatoropts['release'] is not None:
+ if 'name' not in recording_pkgs:
+ recording_pkgs.append('name')
+
+ ksconf = misc.normalize_ksfile(ksconf,
+ creatoropts['release'],
+ creatoropts['arch'])
+
+ configmgr._ksconf = ksconf
+
+ # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
+ if creatoropts['release'] is not None:
+ creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
+
+ # try to find the pkgmgr
+ pkgmgr = None
+ for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
+ if key == creatoropts['pkgmgr']:
+ pkgmgr = pcls
+ break
+
+ if not pkgmgr:
+ pkgmgrs = pluginmgr.get_plugins('backend').keys()
+ raise errors.CreatorError("Can't find package manager: %s (availables: %s)" % (creatoropts['pkgmgr'], ', '.join(pkgmgrs)))
+
+ if creatoropts['runtime']:
+ rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
+
+ creator = livecd.LiveCDImageCreator(creatoropts, pkgmgr)
+
+ if len(recording_pkgs) > 0:
+ creator._recording_pkgs = recording_pkgs
+
+ self.check_image_exists(creator.destdir,
+ creator.pack_to,
+ [creator.name + ".iso"],
+ creatoropts['release'])
+
+ try:
+ creator.check_depend_tools()
+ creator.mount(None, creatoropts["cachedir"])
+ creator.install()
+ creator.configure(creatoropts["repomd"])
+ creator.copy_kernel()
+ creator.unmount()
+ creator.package(creatoropts["outdir"])
+ if creatoropts['release'] is not None:
+ creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
+ creator.print_outimage_info()
+
+ except errors.CreatorError:
+ raise
+ finally:
+ creator.cleanup()
+
+ msger.info("Finished.")
+ return 0
+
+ @classmethod
+ def do_chroot(cls, target):
+ os_image = cls.do_unpack(target)
+ os_image_dir = os.path.dirname(os_image)
+
+ # unpack image to target dir
+ imgsize = misc.get_file_size(os_image) * 1024L * 1024L
+ imgtype = misc.get_image_type(os_image)
+ if imgtype == "btrfsimg":
+ fstype = "btrfs"
+ myDiskMount = fs_related.BtrfsDiskMount
+ elif imgtype in ("ext3fsimg", "ext4fsimg"):
+ fstype = imgtype[:4]
+ myDiskMount = fs_related.ExtDiskMount
+ else:
+ raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
+
+ extmnt = misc.mkdtemp()
+ extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize),
+ extmnt,
+ fstype,
+ 4096,
+ "%s label" % fstype)
+ try:
+ extloop.mount()
+
+ except errors.MountError:
+ extloop.cleanup()
+ shutil.rmtree(extmnt, ignore_errors = True)
+ shutil.rmtree(os_image_dir, ignore_errors = True)
+ raise
+
+ try:
+ envcmd = fs_related.find_binary_inchroot("env", extmnt)
+ if envcmd:
+ cmdline = "%s HOME=/root /bin/bash" % envcmd
+ else:
+ cmdline = "/bin/bash"
+ chroot.chroot(extmnt, None, cmdline)
+ except:
+ raise errors.CreatorError("Failed to chroot to %s." %target)
+ finally:
+ chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt)
+
+ @classmethod
+ def do_pack(cls, base_on):
+ import subprocess
+
+ def __mkinitrd(instance):
+ kernelver = instance._get_kernel_versions().values()[0][0]
+ args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ]
+ try:
+ subprocess.call(args, preexec_fn = instance._chroot)
+ except OSError, (err, msg):
+ raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg)
+
+ def __run_post_cleanups(instance):
+ kernelver = instance._get_kernel_versions().values()[0][0]
+ args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver]
+
+ try:
+ subprocess.call(args, preexec_fn = instance._chroot)
+ except OSError, (err, msg):
+ raise errors.CreatorError("Failed to run post cleanups: %s" % msg)
+
+ convertoropts = configmgr.convert
+ convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0]
+ convertor = livecd.LiveCDImageCreator(convertoropts)
+ imgtype = misc.get_image_type(base_on)
+ if imgtype == "btrfsimg":
+ fstype = "btrfs"
+ elif imgtype in ("ext3fsimg", "ext4fsimg"):
+ fstype = imgtype[:4]
+ else:
+ raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
+ convertor._set_fstype(fstype)
+ try:
+ convertor.mount(base_on)
+ __mkinitrd(convertor)
+ convertor._create_bootconfig()
+ __run_post_cleanups(convertor)
+ convertor.launch_shell(convertoropts['shell'])
+ convertor.unmount()
+ convertor.package()
+ convertor.print_outimage_info()
+ finally:
+ shutil.rmtree(os.path.dirname(base_on), ignore_errors = True)
+
+ @classmethod
+ def do_unpack(cls, srcimg):
+ img = srcimg
+ imgmnt = misc.mkdtemp()
+ imgloop = fs_related.DiskMount(fs_related.LoopbackDisk(img, 0), imgmnt)
+ try:
+ imgloop.mount()
+ except errors.MountError:
+ imgloop.cleanup()
+ raise
+
+ # legacy LiveOS filesystem layout support, remove for F9 or F10
+ if os.path.exists(imgmnt + "/squashfs.img"):
+ squashimg = imgmnt + "/squashfs.img"
+ else:
+ squashimg = imgmnt + "/LiveOS/squashfs.img"
+
+ tmpoutdir = misc.mkdtemp()
+ # unsquashfs requires outdir mustn't exist
+ shutil.rmtree(tmpoutdir, ignore_errors = True)
+ misc.uncompress_squashfs(squashimg, tmpoutdir)
+
+ try:
+ # legacy LiveOS filesystem layout support, remove for F9 or F10
+ if os.path.exists(tmpoutdir + "/os.img"):
+ os_image = tmpoutdir + "/os.img"
+ else:
+ os_image = tmpoutdir + "/LiveOS/ext3fs.img"
+
+ if not os.path.exists(os_image):
+ raise errors.CreatorError("'%s' is not a valid live CD ISO : neither "
+ "LiveOS/ext3fs.img nor os.img exist" %img)
+
+ imgname = os.path.basename(srcimg)
+ imgname = os.path.splitext(imgname)[0] + ".img"
+ rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname)
+ shutil.copyfile(os_image, rtimage)
+
+ finally:
+ imgloop.cleanup()
+ shutil.rmtree(tmpoutdir, ignore_errors = True)
+ shutil.rmtree(imgmnt, ignore_errors = True)
+
+ return rtimage
diff --git a/plugins/imager/liveusb_plugin.py b/plugins/imager/liveusb_plugin.py
new file mode 100644
index 0000000..97fc0bd
--- /dev/null
+++ b/plugins/imager/liveusb_plugin.py
@@ -0,0 +1,255 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import tempfile
+
+from mic import chroot, msger, rt_util
+from mic.utils import misc, fs_related, errors
+from mic.utils.partitionedfs import PartitionedMount
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+
+import mic.imager.liveusb as liveusb
+
+from mic.pluginbase import ImagerPlugin
+class LiveUSBPlugin(ImagerPlugin):
+ name = 'liveusb'
+
+ @classmethod
+ def do_create(self, subcmd, opts, *args):
+ """${cmd_name}: create liveusb image
+
+ Usage:
+ ${name} ${cmd_name} <ksfile> [OPTS]
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ raise errors.Usage("need one argument as the path of ks file")
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ creatoropts = configmgr.create
+ ksconf = args[0]
+
+ if not os.path.exists(ksconf):
+ raise errors.CreatorError("Can't find the file: %s" % ksconf)
+
+ if creatoropts['arch'] and creatoropts['arch'].startswith('arm'):
+ msger.warning('liveusb cannot support arm images, Quit')
+ return
+
+ recording_pkgs = []
+ if len(creatoropts['record_pkgs']) > 0:
+ recording_pkgs = creatoropts['record_pkgs']
+
+ if creatoropts['release'] is not None:
+ if 'name' not in recording_pkgs:
+ recording_pkgs.append('name')
+
+ ksconf = misc.normalize_ksfile(ksconf,
+ creatoropts['release'],
+ creatoropts['arch'])
+
+ configmgr._ksconf = ksconf
+
+ # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
+ if creatoropts['release'] is not None:
+ creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
+
+ # try to find the pkgmgr
+ pkgmgr = None
+ for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
+ if key == creatoropts['pkgmgr']:
+ pkgmgr = pcls
+ break
+
+ if not pkgmgr:
+ pkgmgrs = pluginmgr.get_plugins('backend').keys()
+ raise errors.CreatorError("Can't find package manager: %s (availables: %s)" % (creatoropts['pkgmgr'], ', '.join(pkgmgrs)))
+
+ if creatoropts['runtime']:
+ rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
+
+ creator = liveusb.LiveUSBImageCreator(creatoropts, pkgmgr)
+
+ if len(recording_pkgs) > 0:
+ creator._recording_pkgs = recording_pkgs
+
+ self.check_image_exists(creator.destdir,
+ creator.pack_to,
+ [creator.name + ".usbimg"],
+ creatoropts['release'])
+ try:
+ creator.check_depend_tools()
+ creator.mount(None, creatoropts["cachedir"])
+ creator.install()
+ creator.configure(creatoropts["repomd"])
+ creator.copy_kernel()
+ creator.unmount()
+ creator.package(creatoropts["outdir"])
+ if creatoropts['release'] is not None:
+ creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
+ creator.print_outimage_info()
+
+ except errors.CreatorError:
+ raise
+ finally:
+ creator.cleanup()
+
+ msger.info("Finished.")
+ return 0
+
+ @classmethod
+ def do_chroot(cls, target):
+ os_image = cls.do_unpack(target)
+ os_image_dir = os.path.dirname(os_image)
+
+ # unpack image to target dir
+ imgsize = misc.get_file_size(os_image) * 1024L * 1024L
+ imgtype = misc.get_image_type(os_image)
+ if imgtype == "btrfsimg":
+ fstype = "btrfs"
+ myDiskMount = fs_related.BtrfsDiskMount
+ elif imgtype in ("ext3fsimg", "ext4fsimg"):
+ fstype = imgtype[:4]
+ myDiskMount = fs_related.ExtDiskMount
+ else:
+ raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
+
+ extmnt = misc.mkdtemp()
+ extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize),
+ extmnt,
+ fstype,
+ 4096,
+ "%s label" % fstype)
+
+ try:
+ extloop.mount()
+
+ except errors.MountError:
+ extloop.cleanup()
+ shutil.rmtree(extmnt, ignore_errors = True)
+ raise
+
+ try:
+ envcmd = fs_related.find_binary_inchroot("env", extmnt)
+ if envcmd:
+ cmdline = "%s HOME=/root /bin/bash" % envcmd
+ else:
+ cmdline = "/bin/bash"
+ chroot.chroot(extmnt, None, cmdline)
+ except:
+ raise errors.CreatorError("Failed to chroot to %s." %target)
+ finally:
+ chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt)
+
+ @classmethod
+ def do_pack(cls, base_on):
+ import subprocess
+
+ def __mkinitrd(instance):
+ kernelver = instance._get_kernel_versions().values()[0][0]
+ args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ]
+ try:
+ subprocess.call(args, preexec_fn = instance._chroot)
+
+ except OSError, (err, msg):
+ raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg)
+
+ def __run_post_cleanups(instance):
+ kernelver = instance._get_kernel_versions().values()[0][0]
+ args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver]
+
+ try:
+ subprocess.call(args, preexec_fn = instance._chroot)
+ except OSError, (err, msg):
+ raise errors.CreatorError("Failed to run post cleanups: %s" % msg)
+
+ convertoropts = configmgr.convert
+ convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0]
+ convertor = liveusb.LiveUSBImageCreator(convertoropts)
+ imgtype = misc.get_image_type(base_on)
+ if imgtype == "btrfsimg":
+ fstype = "btrfs"
+ elif imgtype in ("ext3fsimg", "ext4fsimg"):
+ fstype = imgtype[:4]
+ else:
+ raise errors.CreatorError("Unsupported filesystem type: %s" % fstyp)
+ convertor._set_fstype(fstype)
+ try:
+ convertor.mount(base_on)
+ __mkinitrd(convertor)
+ convertor._create_bootconfig()
+ __run_post_cleanups(convertor)
+ convertor.launch_shell(convertoropts['shell'])
+ convertor.unmount()
+ convertor.package()
+ convertor.print_outimage_info()
+ finally:
+ shutil.rmtree(os.path.dirname(base_on), ignore_errors = True)
+
+ @classmethod
+ def do_unpack(cls, srcimg):
+ img = srcimg
+ imgsize = misc.get_file_size(img) * 1024L * 1024L
+ imgmnt = misc.mkdtemp()
+ disk = fs_related.SparseLoopbackDisk(img, imgsize)
+ imgloop = PartitionedMount({'/dev/sdb':disk}, imgmnt, skipformat = True)
+ imgloop.add_partition(imgsize/1024/1024, "/dev/sdb", "/", "vfat", boot=False)
+ try:
+ imgloop.mount()
+ except errors.MountError:
+ imgloop.cleanup()
+ raise
+
+ # legacy LiveOS filesystem layout support, remove for F9 or F10
+ if os.path.exists(imgmnt + "/squashfs.img"):
+ squashimg = imgmnt + "/squashfs.img"
+ else:
+ squashimg = imgmnt + "/LiveOS/squashfs.img"
+
+ tmpoutdir = misc.mkdtemp()
+ # unsquashfs requires outdir mustn't exist
+ shutil.rmtree(tmpoutdir, ignore_errors = True)
+ misc.uncompress_squashfs(squashimg, tmpoutdir)
+
+ try:
+ # legacy LiveOS filesystem layout support, remove for F9 or F10
+ if os.path.exists(tmpoutdir + "/os.img"):
+ os_image = tmpoutdir + "/os.img"
+ else:
+ os_image = tmpoutdir + "/LiveOS/ext3fs.img"
+
+ if not os.path.exists(os_image):
+ raise errors.CreatorError("'%s' is not a valid live CD ISO : neither "
+ "LiveOS/ext3fs.img nor os.img exist" %img)
+ imgname = os.path.basename(srcimg)
+ imgname = os.path.splitext(imgname)[0] + ".img"
+ rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname)
+ shutil.copyfile(os_image, rtimage)
+
+ finally:
+ imgloop.cleanup()
+ shutil.rmtree(tmpoutdir, ignore_errors = True)
+ shutil.rmtree(imgmnt, ignore_errors = True)
+
+ return rtimage
diff --git a/plugins/imager/loop_plugin.py b/plugins/imager/loop_plugin.py
new file mode 100644
index 0000000..f1e8fc8
--- /dev/null
+++ b/plugins/imager/loop_plugin.py
@@ -0,0 +1,248 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import tempfile
+
+from mic import chroot, msger, rt_util
+from mic.utils import misc, fs_related, errors, cmdln
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+from mic.imager.loop import LoopImageCreator, load_mountpoints
+
+from mic.pluginbase import ImagerPlugin
+class LoopPlugin(ImagerPlugin):
+ name = 'loop'
+
+ @classmethod
+ @cmdln.option("--compress-disk-image", dest="compress_image",
+ type='choice', choices=("gz", "bz2"), default=None,
+ help="Same with --compress-image")
+ # alias to compress-image for compatibility
+ @cmdln.option("--compress-image", dest="compress_image",
+ type='choice', choices=("gz", "bz2"), default=None,
+ help="Compress all loop images with 'gz' or 'bz2'")
+ @cmdln.option("--shrink", action='store_true', default=False,
+ help="Whether to shrink loop images to minimal size")
+ def do_create(self, subcmd, opts, *args):
+ """${cmd_name}: create loop image
+
+ Usage:
+ ${name} ${cmd_name} <ksfile> [OPTS]
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ raise errors.Usage("need one argument as the path of ks file")
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ creatoropts = configmgr.create
+ ksconf = args[0]
+
+ if not os.path.exists(ksconf):
+ raise errors.CreatorError("Can't find the file: %s" % ksconf)
+
+ recording_pkgs = []
+ if len(creatoropts['record_pkgs']) > 0:
+ recording_pkgs = creatoropts['record_pkgs']
+
+ if creatoropts['release'] is not None:
+ if 'name' not in recording_pkgs:
+ recording_pkgs.append('name')
+
+ ksconf = misc.normalize_ksfile(ksconf,
+ creatoropts['release'],
+ creatoropts['arch'])
+ configmgr._ksconf = ksconf
+
+ # Called After setting the configmgr._ksconf
+ # as the creatoropts['name'] is reset there.
+ if creatoropts['release'] is not None:
+ creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'],
+ creatoropts['release'],
+ creatoropts['name'])
+
+ # try to find the pkgmgr
+ pkgmgr = None
+ for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
+ if key == creatoropts['pkgmgr']:
+ pkgmgr = pcls
+ break
+
+ if not pkgmgr:
+ pkgmgrs = pluginmgr.get_plugins('backend').keys()
+ raise errors.CreatorError("Can't find package manager: %s "
+ "(availables: %s)" \
+ % (creatoropts['pkgmgr'],
+ ', '.join(pkgmgrs)))
+
+ if creatoropts['runtime']:
+ rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
+
+ creator = LoopImageCreator(creatoropts,
+ pkgmgr,
+ opts.compress_image,
+ opts.shrink)
+
+ if len(recording_pkgs) > 0:
+ creator._recording_pkgs = recording_pkgs
+
+ self.check_image_exists(creator.destdir,
+ creator.pack_to,
+ [creator.name + ".img"],
+ creatoropts['release'])
+
+ try:
+ creator.check_depend_tools()
+ creator.mount(None, creatoropts["cachedir"])
+ creator.install()
+ creator.configure(creatoropts["repomd"])
+ creator.copy_kernel()
+ creator.unmount()
+ creator.package(creatoropts["outdir"])
+
+ if creatoropts['release'] is not None:
+ creator.release_output(ksconf,
+ creatoropts['outdir'],
+ creatoropts['release'])
+ creator.print_outimage_info()
+
+ except errors.CreatorError:
+ raise
+ finally:
+ creator.cleanup()
+
+ msger.info("Finished.")
+ return 0
+
+ @classmethod
+ def _do_chroot_tar(cls, target):
+ mountfp_xml = os.path.splitext(target)[0] + '.xml'
+ if not os.path.exists(mountfp_xml):
+ raise errors.CreatorError("No mount point file found for this tar "
+ "image, please check %s" % mountfp_xml)
+
+ import tarfile
+ tar = tarfile.open(target, 'r')
+ tmpdir = misc.mkdtemp()
+ tar.extractall(path=tmpdir)
+ tar.close()
+
+ mntdir = misc.mkdtemp()
+
+ loops = []
+ for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml):
+ if fstype in ("ext2", "ext3", "ext4"):
+ myDiskMount = fs_related.ExtDiskMount
+ elif fstype == "btrfs":
+ myDiskMount = fs_related.BtrfsDiskMount
+ elif fstype in ("vfat", "msdos"):
+ myDiskMount = fs_related.VfatDiskMount
+ else:
+ msger.error("Cannot support fstype: %s" % fstype)
+
+ name = os.path.join(tmpdir, name)
+ size = size * 1024L * 1024L
+ loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size),
+ os.path.join(mntdir, mp.lstrip('/')),
+ fstype, size, label)
+
+ try:
+ msger.verbose("Mount %s to %s" % (mp, mntdir + mp))
+ fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/')))
+ loop.mount()
+
+ except:
+ loop.cleanup()
+ for lp in reversed(loops):
+ chroot.cleanup_after_chroot("img", lp, None, mntdir)
+
+ shutil.rmtree(tmpdir, ignore_errors=True)
+ raise
+
+ loops.append(loop)
+
+ try:
+ chroot.chroot(mntdir, None, "/usr/bin/env HOME=/root /bin/bash")
+ except:
+ raise errors.CreatorError("Failed to chroot to %s." % target)
+ finally:
+ for loop in reversed(loops):
+ chroot.cleanup_after_chroot("img", loop, None, mntdir)
+
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+ @classmethod
+ def do_chroot(cls, target):
+ if target.endswith('.tar'):
+ import tarfile
+ if tarfile.is_tarfile(target):
+ LoopPlugin._do_chroot_tar(target)
+ return
+ else:
+ raise errors.CreatorError("damaged tarball for loop images")
+
+ img = target
+ imgsize = misc.get_file_size(img) * 1024L * 1024L
+ imgtype = misc.get_image_type(img)
+ if imgtype == "btrfsimg":
+ fstype = "btrfs"
+ myDiskMount = fs_related.BtrfsDiskMount
+ elif imgtype in ("ext3fsimg", "ext4fsimg"):
+ fstype = imgtype[:4]
+ myDiskMount = fs_related.ExtDiskMount
+ else:
+ raise errors.CreatorError("Unsupported filesystem type: %s" \
+ % imgtype)
+
+ extmnt = misc.mkdtemp()
+ extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize),
+ extmnt,
+ fstype,
+ 4096,
+ "%s label" % fstype)
+ try:
+ extloop.mount()
+
+ except errors.MountError:
+ extloop.cleanup()
+ shutil.rmtree(extmnt, ignore_errors=True)
+ raise
+
+ try:
+ envcmd = fs_related.find_binary_inchroot("env", extmnt)
+ if envcmd:
+ cmdline = "%s HOME=/root /bin/bash" % envcmd
+ else:
+ cmdline = "/bin/bash"
+ chroot.chroot(extmnt, None, cmdline)
+ except:
+ raise errors.CreatorError("Failed to chroot to %s." % img)
+ finally:
+ chroot.cleanup_after_chroot("img", extloop, None, extmnt)
+
+ @classmethod
+ def do_unpack(cls, srcimg):
+ image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"),
+ "target.img")
+ msger.info("Copying file system ...")
+ shutil.copyfile(srcimg, image)
+ return image
diff --git a/plugins/imager/raw_plugin.py b/plugins/imager/raw_plugin.py
new file mode 100644
index 0000000..47f6512
--- /dev/null
+++ b/plugins/imager/raw_plugin.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python -tt
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os
+import shutil
+import re
+import tempfile
+
+from mic import chroot, msger, rt_util
+from mic.utils import misc, fs_related, errors, runner, cmdln
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+from mic.utils.partitionedfs import PartitionedMount
+
+import mic.imager.raw as raw
+
+from mic.pluginbase import ImagerPlugin
+class RawPlugin(ImagerPlugin):
+ name = 'raw'
+
+ @classmethod
+ @cmdln.option("--compress-disk-image", dest="compress_image", type='choice',
+ choices=("gz", "bz2"), default=None,
+ help="Same with --compress-image")
+ @cmdln.option("--compress-image", dest="compress_image", type='choice',
+ choices=("gz", "bz2"), default = None,
+ help="Compress all raw images before package")
+ def do_create(self, subcmd, opts, *args):
+ """${cmd_name}: create raw image
+
+ Usage:
+ ${name} ${cmd_name} <ksfile> [OPTS]
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ raise errors.Usage("need one argument as the path of ks file")
+
+ if len(args) != 1:
+ raise errors.Usage("Extra arguments given")
+
+ creatoropts = configmgr.create
+ ksconf = args[0]
+
+ if not os.path.exists(ksconf):
+ raise errors.CreatorError("Can't find the file: %s" % ksconf)
+
+ recording_pkgs = []
+ if len(creatoropts['record_pkgs']) > 0:
+ recording_pkgs = creatoropts['record_pkgs']
+
+ if creatoropts['release'] is not None:
+ if 'name' not in recording_pkgs:
+ recording_pkgs.append('name')
+
+ ksconf = misc.normalize_ksfile(ksconf,
+ creatoropts['release'],
+ creatoropts['arch'])
+
+ configmgr._ksconf = ksconf
+
+ # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
+ if creatoropts['release'] is not None:
+ creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
+
+ # try to find the pkgmgr
+ pkgmgr = None
+ for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
+ if key == creatoropts['pkgmgr']:
+ pkgmgr = pcls
+ break
+
+ if not pkgmgr:
+ pkgmgrs = pluginmgr.get_plugins('backend').keys()
+ raise errors.CreatorError("Can't find package manager: %s (availables: %s)" % (creatoropts['pkgmgr'], ', '.join(pkgmgrs)))
+
+ if creatoropts['runtime']:
+ rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
+
+ creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image)
+
+ if len(recording_pkgs) > 0:
+ creator._recording_pkgs = recording_pkgs
+
+ images = ["%s-%s.raw" % (creator.name, part['name'])
+ for part in creator.get_diskinfo()]
+ self.check_image_exists(creator.destdir,
+ creator.pack_to,
+ images,
+ creatoropts['release'])
+
+ try:
+ creator.check_depend_tools()
+ creator.mount(None, creatoropts["cachedir"])
+ creator.install()
+ creator.configure(creatoropts["repomd"])
+ creator.copy_kernel()
+ creator.unmount()
+ creator.package(creatoropts["outdir"])
+ if creatoropts['release'] is not None:
+ creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
+ creator.print_outimage_info()
+
+ except errors.CreatorError:
+ raise
+ finally:
+ creator.cleanup()
+
+ msger.info("Finished.")
+ return 0
+
+ @classmethod
+ def do_chroot(cls, target):
+ img = target
+ imgsize = misc.get_file_size(img) * 1024L * 1024L
+ partedcmd = fs_related.find_binary_path("parted")
+ disk = fs_related.SparseLoopbackDisk(img, imgsize)
+ imgmnt = misc.mkdtemp()
+ imgloop = PartitionedMount({'/dev/sdb':disk}, imgmnt, skipformat = True)
+ img_fstype = "ext3"
+
+ msger.info("Partition Table:")
+ partnum = []
+ for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines():
+ # no use strip to keep line output here
+ if "Number" in line:
+ msger.raw(line)
+ if line.strip() and line.strip()[0].isdigit():
+ partnum.append(line.strip()[0])
+ msger.raw(line)
+
+ rootpart = None
+ if len(partnum) > 1:
+ rootpart = msger.choice("please choose root partition", partnum)
+
+ # Check the partitions from raw disk.
+ # if choose root part, the mark it as mounted
+ if rootpart:
+ root_mounted = True
+ else:
+ root_mounted = False
+ partition_mounts = 0
+ for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines():
+ line = line.strip()
+
+ # Lines that start with number are the partitions,
+ # because parted can be translated we can't refer to any text lines.
+ if not line or not line[0].isdigit():
+ continue
+
+ # Some vars have extra , as list seperator.
+ line = line.replace(",","")
+
+ # Example of parted output lines that are handled:
+ # Number Start End Size Type File system Flags
+ # 1 512B 3400000511B 3400000000B primary
+ # 2 3400531968B 3656384511B 255852544B primary linux-swap(v1)
+ # 3 3656384512B 3720347647B 63963136B primary fat16 boot, lba
+
+ partition_info = re.split("\s+",line)
+
+ size = partition_info[3].split("B")[0]
+
+ if len(partition_info) < 6 or partition_info[5] in ["boot"]:
+ # No filesystem can be found from partition line. Assuming
+ # btrfs, because that is the only MeeGo fs that parted does
+ # not recognize properly.
+ # TODO: Can we make better assumption?
+ fstype = "btrfs"
+ elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]:
+ fstype = partition_info[5]
+ elif partition_info[5] in ["fat16","fat32"]:
+ fstype = "vfat"
+ elif "swap" in partition_info[5]:
+ fstype = "swap"
+ else:
+ raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5])
+
+ if rootpart and rootpart == line[0]:
+ mountpoint = '/'
+ elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
+ # TODO: Check that this is actually the valid root partition from /etc/fstab
+ mountpoint = "/"
+ root_mounted = True
+ elif fstype == "swap":
+ mountpoint = "swap"
+ else:
+ # TODO: Assing better mount points for the rest of the partitions.
+ partition_mounts += 1
+ mountpoint = "/media/partition_%d" % partition_mounts
+
+ if "boot" in partition_info:
+ boot = True
+ else:
+ boot = False
+
+ msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot))
+ # TODO: add_partition should take bytes as size parameter.
+ imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot)
+
+ try:
+ imgloop.mount()
+
+ except errors.MountError:
+ imgloop.cleanup()
+ raise
+
+ try:
+ envcmd = fs_related.find_binary_inchroot("env", imgmnt)
+ if envcmd:
+ cmdline = "%s HOME=/root /bin/bash" % envcmd
+ else:
+ cmdline = "/bin/bash"
+ chroot.chroot(imgmnt, None, cmdline)
+ except:
+ raise errors.CreatorError("Failed to chroot to %s." %img)
+ finally:
+ chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
+
+ @classmethod
+ def do_unpack(cls, srcimg):
+ srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
+ srcmnt = misc.mkdtemp("srcmnt")
+ disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
+ srcloop = PartitionedMount({'/dev/sdb':disk}, srcmnt, skipformat = True)
+
+ srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
+ try:
+ srcloop.mount()
+
+ except errors.MountError:
+ srcloop.cleanup()
+ raise
+
+ image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
+ args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
+
+ msger.info("`dd` image ...")
+ rc = runner.show(args)
+ srcloop.cleanup()
+ shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
+
+ if rc != 0:
+ raise errors.CreatorError("Failed to dd")
+ else:
+ return image
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..fe98c34
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[install]
+prefix=$PREFIX
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f68c1c0
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+import os, sys
+import glob
+from distutils.core import setup
+try:
+ import setuptools
+ # enable "setup.py develop", optional
+except ImportError:
+ pass
+
+MOD_NAME = 'mic'
+
+version_path = 'VERSION'
+if not os.path.isfile(version_path):
+ print 'No VERSION file in topdir, abort'
+ sys.exit(1)
+
+try:
+ # first line should be the version number
+ version = open(version_path).readline().strip()
+ if not version:
+ print 'VERSION file is invalid, abort'
+ sys.exit(1)
+
+ ver_file = open('%s/__version__.py' % MOD_NAME, 'w')
+ ver_file.write("VERSION = \"%s\"\n" % version)
+ ver_file.close()
+except IOError:
+ print 'WARNING: Cannot write version number file'
+
+# --install-layout is recognized after 2.5
+if sys.version_info[:2] > (2, 5):
+ if len(sys.argv) > 1 and 'install' in sys.argv:
+ try:
+ import platform
+ (dist, ver, id) = platform.linux_distribution()
+
+ # for debian-like distros, mods will be installed to
+ # ${PYTHONLIB}/dist-packages
+ if dist in ('debian', 'Ubuntu'):
+ sys.argv.append('--install-layout=deb')
+ except:
+ pass
+
+PACKAGES = [MOD_NAME,
+ MOD_NAME + '/utils',
+ MOD_NAME + '/imager',
+ MOD_NAME + '/kickstart',
+ MOD_NAME + '/kickstart/custom_commands',
+ MOD_NAME + '/3rdparty/pykickstart',
+ MOD_NAME + '/3rdparty/pykickstart/commands',
+ MOD_NAME + '/3rdparty/pykickstart/handlers',
+ MOD_NAME + '/3rdparty/pykickstart/urlgrabber',
+ ]
+
+IMAGER_PLUGINS = glob.glob(os.path.join("plugins", "imager", "*.py"))
+BACKEND_PLUGINS = glob.glob(os.path.join("plugins", "backend", "*.py"))
+
+# the following code to do a simple parse for '--prefix' opts
+prefix = sys.prefix
+is_next = False
+for arg in sys.argv:
+ if is_next:
+ prefix = arg
+ break
+ if '--prefix=' in arg:
+ prefix = arg[9:]
+ break
+ elif '--prefix' == arg:
+ is_next = True
+
+# get the installation path of mic.conf
+prefix = os.path.abspath(os.path.expanduser(prefix)).rstrip('/')
+if prefix.lstrip('/') == 'usr':
+ etc_prefix = '/etc'
+else:
+ etc_prefix = os.path.join(prefix, 'etc')
+
+conffile = 'distfiles/mic.conf'
+if os.path.isfile('%s/mic/mic.conf' % etc_prefix):
+ conffile += '.new'
+
+# apply prefix to mic.conf.in to generate actual mic.conf
+conf_str = file('distfiles/mic.conf.in').read()
+conf_str = conf_str.replace('@PREFIX@', prefix)
+with file(conffile, 'w') as wf:
+ wf.write(conf_str)
+
+try:
+ os.environ['PREFIX'] = prefix
+ setup(name=MOD_NAME,
+ version = version,
+ description = 'Image Creator for Linux Distributions',
+ author='Jian-feng Ding, Qiang Zhang, Gui Chen',
+ author_email='jian-feng.ding@intel.com, qiang.z.zhang@intel.com, gui.chen@intel.com',
+ url='https://github.com/jfding/mic',
+ scripts=[
+ 'tools/mic',
+ ],
+ packages = PACKAGES,
+ data_files = [("%s/lib/mic/plugins/imager" % prefix, IMAGER_PLUGINS),
+ ("%s/lib/mic/plugins/backend" % prefix, BACKEND_PLUGINS),
+ ("%s/mic" % etc_prefix, [conffile])]
+ )
+finally:
+ # remove dynamic file distfiles/mic.conf
+ os.unlink(conffile)
+
diff --git a/tools/mic b/tools/mic
new file mode 100755
index 0000000..d0c6fed
--- /dev/null
+++ b/tools/mic
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2011 Intel, Inc.
+#
+# 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; version 2 of the License
+#
+# 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., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import os, sys, errno
+from mic import msger, creator
+from mic.utils import cmdln, misc, errors
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+from mic.__version__ import VERSION
+
+class MicCmd(cmdln.Cmdln):
+ """
+ Usage: mic SUBCOMMAND [OPTS] [ARGS...]
+
+ mic Means the Image Creation tool
+ Try 'mic help SUBCOMMAND' for help on a specific subcommand.
+
+ ${command_list}
+ global ${option_list}
+ ${help_list}
+ """
+
+ name = 'mic'
+ version = VERSION
+
+ def print_version(self):
+ msger.raw("%s %s (%s)" % (self.name,
+ self.version,
+ misc.get_distro_str()))
+
+ def get_optparser(self):
+ optparser = cmdln.CmdlnOptionParser(self, version=self.version)
+ # hook optparse print_version here
+ optparser.print_version = self.print_version
+ optparser.add_option('-d', '--debug', action='store_true',
+ dest='debug',
+ help='print debug message')
+ optparser.add_option('-v', '--verbose', action='store_true',
+ dest='verbose',
+ help='verbose information')
+ return optparser
+
+ def postoptparse(self):
+ if self.options.verbose:
+ msger.set_loglevel('verbose')
+
+ if self.options.debug:
+ try:
+ import rpm
+ rpm.setVerbosity(rpm.RPMLOG_NOTICE)
+ except ImportError:
+ pass
+
+ msger.set_loglevel('debug')
+
+ self.print_version()
+
+ def help_create(self):
+ cr = creator.Creator()
+ cr.optparser = cr.get_optparser()
+ doc = cr.__doc__
+ doc = cr._help_reindent(doc)
+ doc = cr._help_preprocess(doc, None)
+ doc = doc.replace(cr.name, "${cmd_name}", 1)
+ doc = doc.rstrip() + '\n'
+ return doc
+
+ @cmdln.alias("cr")
+ def do_create(self, argv):
+ try:
+ cr = creator.Creator()
+ cr.main(argv[1:])
+ except:
+ raise
+
+ def _root_confirm(self):
+ if os.geteuid() != 0:
+ msger.error('Root permission is required to continue, abort')
+
+ @cmdln.alias("cv")
+ @cmdln.option("-S", "--shell",
+ action="store_true", dest="shell", default=False,
+ help="Launch shell before packaging the converted image")
+ def do_convert(self, subcmd, opts, *args):
+ """${cmd_name}: convert image format
+
+ Usage:
+ mic convert <imagefile> <destformat>
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ # print help
+ handler = self._get_cmd_handler('convert')
+ if hasattr(handler, "optparser"):
+ handler.optparser.print_help()
+ return 1
+
+ if len(args) == 1:
+ raise errors.Usage("It need 2 arguments (1 given)")
+ elif len(args) == 2:
+ (srcimg, destformat) = args
+ else:
+ raise errors.Usage("Extra argument given")
+
+ if not os.path.exists(srcimg):
+ raise errors.CreatorError("Cannot find the image: %s" % srcimg)
+
+ self._root_confirm()
+
+ configmgr.convert['shell'] = opts.shell
+
+ srcformat = misc.get_image_type(srcimg)
+ if srcformat == "ext3fsimg":
+ srcformat = "loop"
+
+ srcimager = None
+ destimager = None
+ for iname, icls in pluginmgr.get_plugins('imager').iteritems():
+ if iname == srcformat and hasattr(icls, "do_unpack"):
+ srcimager = icls
+ if iname == destformat and hasattr(icls, "do_pack"):
+ destimager = icls
+
+ if (srcimager and destimager) is None:
+ raise errors.CreatorError("Can't convert from %s to %s" \
+ % (srcformat, destformat))
+
+ else:
+ maptab = {
+ "livecd": "iso",
+ "liveusb": "usbimg",
+ "loop": "img",
+ }
+
+ if destformat in maptab:
+ imgname = os.path.splitext(os.path.basename(srcimg))[0]
+ dstname = "{0}.{1}".format(imgname, maptab[destformat])
+
+ if os.path.exists(dstname):
+ if msger.ask("Converted image %s seems existed, "
+ "remove and continue?" % dstname):
+ os.unlink(dstname)
+ else:
+ raise errors.Abort("Canceled")
+
+ base_on = srcimager.do_unpack(srcimg)
+ destimager.do_pack(base_on)
+
+ @cmdln.alias("ch")
+ @cmdln.option('-s', '--saveto',
+ action='store', dest='saveto', default=None,
+ help="Save the unpacked image to specified dir")
+ def do_chroot(self, subcmd, opts, *args):
+ """${cmd_name}: chroot into an image
+
+ Usage:
+ mic chroot <imagefile>
+
+ ${cmd_option_list}
+ """
+
+ if not args:
+ # print help
+ handler = self._get_cmd_handler('chroot')
+ if hasattr(handler, "optparser"):
+ handler.optparser.print_help()
+ return 1
+
+ if len(args) == 1:
+ targetimage = args[0]
+ else:
+ raise errors.Usage("Extra argument given")
+
+ if not os.path.exists(targetimage):
+ raise errors.CreatorError("Cannot find the image: %s"
+ % targetimage)
+
+ self._root_confirm()
+
+ configmgr.chroot['saveto'] = opts.saveto
+
+ imagetype = misc.get_image_type(targetimage)
+ if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg"):
+ imagetype = "loop"
+
+ chrootclass = None
+ for pname, pcls in pluginmgr.get_plugins('imager').iteritems():
+ if pname == imagetype and hasattr(pcls, "do_chroot"):
+ chrootclass = pcls
+ break
+
+ if not chrootclass:
+ raise errors.CreatorError("Cannot support image type: %s" \
+ % imagetype)
+
+ chrootclass.do_chroot(targetimage)
+
+if __name__ == "__main__":
+ try:
+ mic = MicCmd()
+ sys.exit(mic.main())
+
+ except KeyboardInterrupt:
+ msger.error('\n^C catched, program aborted.')
+
+ # catch 'no space left' exception, etc
+ except IOError, e:
+ if e.errno == errno.ENOSPC:
+ msger.error('\nNo space left on device')
+ raise
+
+ except errors.Usage, usage:
+ msger.error(str(usage))
+
+ except errors.Abort, msg:
+ msger.info(str(msg))
+
+ except errors.CreatorError, err:
+ if msger.get_loglevel() == 'debug':
+ import traceback
+ msger.error(traceback.format_exc())
+ else:
+ msger.error('\n'+str(err))