From c527fd1f14c27855a37f2e8ac5346ce8d940ced2 Mon Sep 17 00:00:00 2001 From: Tudor Florea Date: Thu, 16 Oct 2014 03:05:19 +0200 Subject: initial commit for Enea Linux 4.0-140929 Migrated from the internal git server on the daisy-enea-point-release branch Signed-off-by: Tudor Florea --- scripts/lib/bsp/__init__.py | 22 + scripts/lib/bsp/engine.py | 1780 ++++++++++++++++++++ scripts/lib/bsp/help.py | 1043 ++++++++++++ scripts/lib/bsp/kernel.py | 1071 ++++++++++++ .../target/arch/arm/conf/machine/{{=machine}}.conf | 105 ++ .../{{ if xserver == \"y\": }} xorg.conf" | 33 + ... == \"y\": }} xserver-xf86-config_0.1.bbappend" | 1 + .../arm/recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-non_hardware.cfg" | 30 + .../{{=machine}}-preempt-rt.scc" | 13 + .../{{=machine}}-standard.scc" | 13 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 320 ++++ .../{{=machine}}.scc" | 7 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 25 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 32 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 32 + ...tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" | 32 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 32 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 32 + .../bsp/substrate/target/arch/common/COPYING.MIT | 17 + .../lib/bsp/substrate/target/arch/common/README | 118 ++ .../substrate/target/arch/common/README.sources | 17 + .../substrate/target/arch/common/conf/layer.conf | 10 + .../formfactor/formfactor/{{=machine}}/machconfig | 5 + .../recipes-bsp/formfactor/formfactor_0.0.bbappend | 2 + .../recipes-kernel/linux/kernel-list.noinstall | 26 + ...choice == \"custom\": }} linux-yocto-custom.bb" | 57 + .../defconfig" | 5 + .../{{=machine}}-user-config.cfg" | 8 + .../{{=machine}}-user-patches.scc" | 8 + .../{{=machine}}.cfg" | 3 + .../{{=machine}}.scc" | 17 + .../arch/i386/conf/machine/{{=machine}}.conf | 68 + .../{{ if xserver == \"y\": }} xorg.conf" | 0 ... == \"y\": }} xserver-xf86-config_0.1.bbappend" | 1 + .../recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-preempt-rt.scc" | 15 + .../{{=machine}}-standard.scc" | 15 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 54 + .../{{=machine}}.scc" | 20 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 25 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 32 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 32 + ...tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" | 32 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 32 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 32 + .../bsp/substrate/target/arch/layer/COPYING.MIT | 17 + scripts/lib/bsp/substrate/target/arch/layer/README | 64 + .../substrate/target/arch/layer/conf/layer.conf | 10 + .../target/arch/layer/layer-questions.noinstall | 14 + .../example.patch" | 12 + ..._name}}_{{=example_bbappend_version}}.bbappend" | 8 + .../{{=example_recipe_name}}-0.1/example.patch" | 12 + .../{{=example_recipe_name}}-0.1/helloworld.c" | 8 + .../example/{{=example_recipe_name}}_0.1.bb" | 23 + .../arch/mips/conf/machine/{{=machine}}.conf | 38 + .../recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-preempt-rt.scc" | 9 + .../{{=machine}}-standard.scc" | 9 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 1 + .../{{=machine}}.scc" | 7 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 25 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 32 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 32 + ...tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" | 32 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 32 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 32 + .../arch/powerpc/conf/machine/{{=machine}}.conf | 74 + .../recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-preempt-rt.scc" | 9 + .../{{=machine}}-standard.scc" | 9 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 163 ++ .../{{=machine}}.scc" | 9 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 25 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 32 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 32 + ...tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" | 32 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 32 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 32 + .../arch/qemu/conf/machine/{{=machine}}.conf | 68 + .../init-ifupdown/{{=machine}}/interfaces | 5 + .../init-ifupdown/init-ifupdown_1.0.bbappend | 1 + .../xserver-xf86-config/{{=machine}}/xorg.conf | 77 + .../xorg-xserver/xserver-xf86-config_0.1.bbappend | 1 + .../recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-preempt-rt.scc" | 9 + .../{{=machine}}-standard.scc" | 16 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 0 .../{{=machine}}.scc" | 4 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 49 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 55 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 55 + ...-tiny_3.14\": }} linux-yocto-tiny_3.4.bbappend" | 55 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 55 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 55 + .../arch/x86_64/conf/machine/{{=machine}}.conf | 58 + .../{{ if xserver == \"y\": }} xorg.conf" | 0 ... == \"y\": }} xserver-xf86-config_0.1.bbappend" | 1 + .../recipes-kernel/linux/kernel-list.noinstall | 5 + .../{{=machine}}-preempt-rt.scc" | 15 + .../{{=machine}}-standard.scc" | 15 + .../{{=machine}}-tiny.scc" | 9 + .../{{=machine}}-user-config.cfg" | 0 .../{{=machine}}-user-features.scc" | 0 .../{{=machine}}-user-patches.scc" | 0 .../{{=machine}}.cfg" | 47 + .../{{=machine}}.scc" | 13 + ...linux-yocto-dev\": }} linux-yocto-dev.bbappend" | 25 + ...cto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" | 32 + ...tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" | 32 + ...tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" | 32 + ...nux-yocto_3.10\": }} linux-yocto_3.10.bbappend" | 32 + ...nux-yocto_3.14\": }} linux-yocto_3.14.bbappend" | 32 + scripts/lib/bsp/tags.py | 47 + scripts/lib/image/__init__.py | 22 + scripts/lib/image/canned-wks/directdisk.wks | 10 + scripts/lib/image/canned-wks/mkefidisk.wks | 11 + scripts/lib/image/canned-wks/uboot.wks | 17 + scripts/lib/image/config/wic.conf | 11 + scripts/lib/image/engine.py | 287 ++++ scripts/lib/image/help.py | 311 ++++ scripts/lib/mic/3rdparty/pykickstart/__init__.py | 0 scripts/lib/mic/3rdparty/pykickstart/base.py | 466 +++++ .../mic/3rdparty/pykickstart/commands/__init__.py | 26 + .../3rdparty/pykickstart/commands/authconfig.py | 40 + .../mic/3rdparty/pykickstart/commands/autopart.py | 119 ++ .../mic/3rdparty/pykickstart/commands/autostep.py | 55 + .../3rdparty/pykickstart/commands/bootloader.py | 265 +++ .../mic/3rdparty/pykickstart/commands/clearpart.py | 86 + .../mic/3rdparty/pykickstart/commands/device.py | 125 ++ .../3rdparty/pykickstart/commands/deviceprobe.py | 40 + .../3rdparty/pykickstart/commands/displaymode.py | 68 + .../mic/3rdparty/pykickstart/commands/dmraid.py | 91 + .../3rdparty/pykickstart/commands/driverdisk.py | 184 ++ .../lib/mic/3rdparty/pykickstart/commands/fcoe.py | 114 ++ .../mic/3rdparty/pykickstart/commands/firewall.py | 193 +++ .../mic/3rdparty/pykickstart/commands/firstboot.py | 62 + .../lib/mic/3rdparty/pykickstart/commands/group.py | 88 + .../3rdparty/pykickstart/commands/ignoredisk.py | 139 ++ .../3rdparty/pykickstart/commands/interactive.py | 58 + .../lib/mic/3rdparty/pykickstart/commands/iscsi.py | 133 ++ .../mic/3rdparty/pykickstart/commands/iscsiname.py | 54 + .../lib/mic/3rdparty/pykickstart/commands/key.py | 64 + .../mic/3rdparty/pykickstart/commands/keyboard.py | 55 + .../lib/mic/3rdparty/pykickstart/commands/lang.py | 60 + .../3rdparty/pykickstart/commands/langsupport.py | 58 + .../mic/3rdparty/pykickstart/commands/lilocheck.py | 54 + .../mic/3rdparty/pykickstart/commands/logging.py | 66 + .../mic/3rdparty/pykickstart/commands/logvol.py | 304 ++++ .../3rdparty/pykickstart/commands/mediacheck.py | 53 + .../mic/3rdparty/pykickstart/commands/method.py | 186 ++ .../mic/3rdparty/pykickstart/commands/monitor.py | 106 ++ .../lib/mic/3rdparty/pykickstart/commands/mouse.py | 70 + .../mic/3rdparty/pykickstart/commands/multipath.py | 111 ++ .../mic/3rdparty/pykickstart/commands/network.py | 363 ++++ .../mic/3rdparty/pykickstart/commands/partition.py | 353 ++++ .../lib/mic/3rdparty/pykickstart/commands/raid.py | 365 ++++ .../mic/3rdparty/pykickstart/commands/reboot.py | 79 + .../lib/mic/3rdparty/pykickstart/commands/repo.py | 249 +++ .../mic/3rdparty/pykickstart/commands/rescue.py | 68 + .../mic/3rdparty/pykickstart/commands/rootpw.py | 93 + .../mic/3rdparty/pykickstart/commands/selinux.py | 64 + .../mic/3rdparty/pykickstart/commands/services.py | 71 + .../lib/mic/3rdparty/pykickstart/commands/skipx.py | 54 + .../lib/mic/3rdparty/pykickstart/commands/sshpw.py | 105 ++ .../mic/3rdparty/pykickstart/commands/timezone.py | 86 + .../mic/3rdparty/pykickstart/commands/updates.py | 60 + .../mic/3rdparty/pykickstart/commands/upgrade.py | 106 ++ .../lib/mic/3rdparty/pykickstart/commands/user.py | 173 ++ .../lib/mic/3rdparty/pykickstart/commands/vnc.py | 114 ++ .../mic/3rdparty/pykickstart/commands/volgroup.py | 102 ++ .../mic/3rdparty/pykickstart/commands/xconfig.py | 184 ++ .../mic/3rdparty/pykickstart/commands/zerombr.py | 69 + .../lib/mic/3rdparty/pykickstart/commands/zfcp.py | 134 ++ scripts/lib/mic/3rdparty/pykickstart/constants.py | 57 + scripts/lib/mic/3rdparty/pykickstart/errors.py | 103 ++ .../mic/3rdparty/pykickstart/handlers/__init__.py | 0 .../mic/3rdparty/pykickstart/handlers/control.py | 1307 ++++++++++++++ .../lib/mic/3rdparty/pykickstart/handlers/f10.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f11.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f12.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f13.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f14.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f15.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f16.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f7.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f8.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/f9.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/fc3.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/fc4.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/fc5.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/fc6.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/rhel3.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/rhel4.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/rhel5.py | 24 + .../lib/mic/3rdparty/pykickstart/handlers/rhel6.py | 24 + scripts/lib/mic/3rdparty/pykickstart/ko.py | 37 + scripts/lib/mic/3rdparty/pykickstart/options.py | 204 +++ scripts/lib/mic/3rdparty/pykickstart/parser.py | 702 ++++++++ scripts/lib/mic/3rdparty/pykickstart/sections.py | 244 +++ .../3rdparty/pykickstart/urlgrabber/__init__.py | 53 + .../3rdparty/pykickstart/urlgrabber/byterange.py | 463 +++++ .../mic/3rdparty/pykickstart/urlgrabber/grabber.py | 1477 ++++++++++++++++ .../3rdparty/pykickstart/urlgrabber/keepalive.py | 617 +++++++ .../mic/3rdparty/pykickstart/urlgrabber/mirror.py | 458 +++++ .../3rdparty/pykickstart/urlgrabber/progress.py | 530 ++++++ .../3rdparty/pykickstart/urlgrabber/sslfactory.py | 90 + scripts/lib/mic/3rdparty/pykickstart/version.py | 197 +++ scripts/lib/mic/__init__.py | 4 + scripts/lib/mic/__version__.py | 1 + scripts/lib/mic/bootstrap.py | 279 +++ scripts/lib/mic/chroot.py | 343 ++++ scripts/lib/mic/conf.py | 197 +++ scripts/lib/mic/creator.py | 351 ++++ scripts/lib/mic/imager/__init__.py | 0 scripts/lib/mic/imager/baseimager.py | 1263 ++++++++++++++ scripts/lib/mic/imager/direct.py | 384 +++++ scripts/lib/mic/imager/fs.py | 99 ++ scripts/lib/mic/imager/livecd.py | 750 +++++++++ scripts/lib/mic/imager/liveusb.py | 308 ++++ scripts/lib/mic/imager/loop.py | 418 +++++ scripts/lib/mic/imager/raw.py | 501 ++++++ scripts/lib/mic/kickstart/__init__.py | 892 ++++++++++ .../lib/mic/kickstart/custom_commands/__init__.py | 17 + .../lib/mic/kickstart/custom_commands/desktop.py | 95 ++ .../mic/kickstart/custom_commands/installerfw.py | 63 + .../lib/mic/kickstart/custom_commands/micboot.py | 49 + .../mic/kickstart/custom_commands/micpartition.py | 57 + .../lib/mic/kickstart/custom_commands/micrepo.py | 127 ++ .../lib/mic/kickstart/custom_commands/partition.py | 482 ++++++ .../lib/mic/kickstart/custom_commands/wicboot.py | 57 + scripts/lib/mic/msger.py | 309 ++++ scripts/lib/mic/plugin.py | 121 ++ scripts/lib/mic/pluginbase.py | 158 ++ scripts/lib/mic/plugins/backend/yumpkgmgr.py | 490 ++++++ scripts/lib/mic/plugins/backend/zypppkgmgr.py | 973 +++++++++++ scripts/lib/mic/plugins/hook/.py | 0 scripts/lib/mic/plugins/hook/empty_hook.py | 3 + scripts/lib/mic/plugins/imager/direct_plugin.py | 107 ++ scripts/lib/mic/plugins/imager/fs_plugin.py | 143 ++ scripts/lib/mic/plugins/imager/livecd_plugin.py | 255 +++ scripts/lib/mic/plugins/imager/liveusb_plugin.py | 260 +++ scripts/lib/mic/plugins/imager/loop_plugin.py | 255 +++ scripts/lib/mic/plugins/imager/raw_plugin.py | 275 +++ scripts/lib/mic/plugins/source/bootimg-efi.py | 169 ++ scripts/lib/mic/plugins/source/bootimg-pcbios.py | 195 +++ scripts/lib/mic/plugins/source/rootfs.py | 71 + scripts/lib/mic/plugins/source/uboot.py | 173 ++ scripts/lib/mic/rt_util.py | 223 +++ scripts/lib/mic/test | 1 + scripts/lib/mic/utils/BmapCreate.py | 298 ++++ scripts/lib/mic/utils/Fiemap.py | 252 +++ scripts/lib/mic/utils/__init__.py | 0 scripts/lib/mic/utils/cmdln.py | 1586 +++++++++++++++++ scripts/lib/mic/utils/errors.py | 71 + scripts/lib/mic/utils/fs_related.py | 1060 ++++++++++++ scripts/lib/mic/utils/gpt_parser.py | 331 ++++ scripts/lib/mic/utils/grabber.py | 97 ++ scripts/lib/mic/utils/misc.py | 1065 ++++++++++++ scripts/lib/mic/utils/oe/__init__.py | 22 + scripts/lib/mic/utils/oe/misc.py | 144 ++ scripts/lib/mic/utils/oe/package_manager.py | 810 +++++++++ scripts/lib/mic/utils/partitionedfs.py | 782 +++++++++ scripts/lib/mic/utils/proxy.py | 183 ++ scripts/lib/mic/utils/rpmmisc.py | 600 +++++++ scripts/lib/mic/utils/runner.py | 109 ++ 286 files changed, 39563 insertions(+) create mode 100644 scripts/lib/bsp/__init__.py create mode 100644 scripts/lib/bsp/engine.py create mode 100644 scripts/lib/bsp/help.py create mode 100644 scripts/lib/bsp/kernel.py create mode 100644 scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-non_hardware.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT create mode 100644 scripts/lib/bsp/substrate/target/arch/common/README create mode 100644 scripts/lib/bsp/substrate/target/arch/common/README.sources create mode 100644 scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig create mode 100644 scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend create mode 100644 scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom.bb" create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/defconfig" create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.scc" create mode 100644 scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT create mode 100644 scripts/lib/bsp/substrate/target/arch/layer/README create mode 100644 scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch" create mode 100644 "scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch" create mode 100644 "scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c" create mode 100644 "scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb" create mode 100644 scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend create mode 100644 scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.4.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" create mode 100644 scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" create mode 100644 "scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" create mode 100644 scripts/lib/bsp/tags.py create mode 100644 scripts/lib/image/__init__.py create mode 100644 scripts/lib/image/canned-wks/directdisk.wks create mode 100644 scripts/lib/image/canned-wks/mkefidisk.wks create mode 100644 scripts/lib/image/canned-wks/uboot.wks create mode 100644 scripts/lib/image/config/wic.conf create mode 100644 scripts/lib/image/engine.py create mode 100644 scripts/lib/image/help.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/__init__.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/base.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/device.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/group.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/key.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/lang.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/logging.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/method.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/network.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/partition.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/raid.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/repo.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/services.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/updates.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/user.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/constants.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/errors.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/control.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/ko.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/options.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/parser.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/sections.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py create mode 100644 scripts/lib/mic/3rdparty/pykickstart/version.py create mode 100644 scripts/lib/mic/__init__.py create mode 100644 scripts/lib/mic/__version__.py create mode 100644 scripts/lib/mic/bootstrap.py create mode 100644 scripts/lib/mic/chroot.py create mode 100644 scripts/lib/mic/conf.py create mode 100644 scripts/lib/mic/creator.py create mode 100644 scripts/lib/mic/imager/__init__.py create mode 100644 scripts/lib/mic/imager/baseimager.py create mode 100644 scripts/lib/mic/imager/direct.py create mode 100644 scripts/lib/mic/imager/fs.py create mode 100644 scripts/lib/mic/imager/livecd.py create mode 100644 scripts/lib/mic/imager/liveusb.py create mode 100644 scripts/lib/mic/imager/loop.py create mode 100644 scripts/lib/mic/imager/raw.py create mode 100644 scripts/lib/mic/kickstart/__init__.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/__init__.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/desktop.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/installerfw.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/micboot.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/micpartition.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/micrepo.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/partition.py create mode 100644 scripts/lib/mic/kickstart/custom_commands/wicboot.py create mode 100644 scripts/lib/mic/msger.py create mode 100644 scripts/lib/mic/plugin.py create mode 100644 scripts/lib/mic/pluginbase.py create mode 100644 scripts/lib/mic/plugins/backend/yumpkgmgr.py create mode 100755 scripts/lib/mic/plugins/backend/zypppkgmgr.py create mode 100644 scripts/lib/mic/plugins/hook/.py create mode 100644 scripts/lib/mic/plugins/hook/empty_hook.py create mode 100644 scripts/lib/mic/plugins/imager/direct_plugin.py create mode 100644 scripts/lib/mic/plugins/imager/fs_plugin.py create mode 100644 scripts/lib/mic/plugins/imager/livecd_plugin.py create mode 100644 scripts/lib/mic/plugins/imager/liveusb_plugin.py create mode 100644 scripts/lib/mic/plugins/imager/loop_plugin.py create mode 100644 scripts/lib/mic/plugins/imager/raw_plugin.py create mode 100644 scripts/lib/mic/plugins/source/bootimg-efi.py create mode 100644 scripts/lib/mic/plugins/source/bootimg-pcbios.py create mode 100644 scripts/lib/mic/plugins/source/rootfs.py create mode 100644 scripts/lib/mic/plugins/source/uboot.py create mode 100644 scripts/lib/mic/rt_util.py create mode 100644 scripts/lib/mic/test create mode 100644 scripts/lib/mic/utils/BmapCreate.py create mode 100644 scripts/lib/mic/utils/Fiemap.py create mode 100644 scripts/lib/mic/utils/__init__.py create mode 100644 scripts/lib/mic/utils/cmdln.py create mode 100644 scripts/lib/mic/utils/errors.py create mode 100644 scripts/lib/mic/utils/fs_related.py create mode 100644 scripts/lib/mic/utils/gpt_parser.py create mode 100644 scripts/lib/mic/utils/grabber.py create mode 100644 scripts/lib/mic/utils/misc.py create mode 100644 scripts/lib/mic/utils/oe/__init__.py create mode 100644 scripts/lib/mic/utils/oe/misc.py create mode 100644 scripts/lib/mic/utils/oe/package_manager.py create mode 100644 scripts/lib/mic/utils/partitionedfs.py create mode 100644 scripts/lib/mic/utils/proxy.py create mode 100644 scripts/lib/mic/utils/rpmmisc.py create mode 100644 scripts/lib/mic/utils/runner.py (limited to 'scripts/lib') diff --git a/scripts/lib/bsp/__init__.py b/scripts/lib/bsp/__init__.py new file mode 100644 index 0000000000..8bbb6e1530 --- /dev/null +++ b/scripts/lib/bsp/__init__.py @@ -0,0 +1,22 @@ +# +# Yocto BSP tools library +# +# Copyright (c) 2012, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# AUTHORS +# Tom Zanussi +# diff --git a/scripts/lib/bsp/engine.py b/scripts/lib/bsp/engine.py new file mode 100644 index 0000000000..681720d20a --- /dev/null +++ b/scripts/lib/bsp/engine.py @@ -0,0 +1,1780 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2012, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module implements the templating engine used by 'yocto-bsp' to +# create BSPs. The BSP templates are simply the set of files expected +# to appear in a generated BSP, marked up with a small set of tags +# used to customize the output. The engine parses through the +# templates and generates a Python program containing all the logic +# and input elements needed to display and retrieve BSP-specific +# information from the user. The resulting program uses those results +# to generate the final BSP files. +# +# AUTHORS +# Tom Zanussi +# + +import os +import sys +from abc import ABCMeta, abstractmethod +from tags import * +import shlex +import json +import subprocess +import shutil + +class Line(): + """ + Generic (abstract) container representing a line that will appear + in the BSP-generating program. + """ + __metaclass__ = ABCMeta + + def __init__(self, line): + self.line = line + self.generated_line = "" + self.prio = sys.maxint + self.discard = False + + @abstractmethod + def gen(self, context = None): + """ + Generate the final executable line that will appear in the + BSP-generation program. + """ + pass + + def escape(self, line): + """ + Escape single and double quotes and backslashes until I find + something better (re.escape() escapes way too much). + """ + return line.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") + + def parse_error(self, msg, lineno, line): + raise SyntaxError("%s: %s" % (msg, line)) + + +class NormalLine(Line): + """ + Container for normal (non-tag) lines. + """ + def __init__(self, line): + Line.__init__(self, line) + self.is_filename = False + self.is_dirname = False + self.out_filebase = None + + def gen(self, context = None): + if self.is_filename: + line = "current_file = \"" + os.path.join(self.out_filebase, self.escape(self.line)) + "\"; of = open(current_file, \"w\")" + elif self.is_dirname: + dirname = os.path.join(self.out_filebase, self.escape(self.line)) + line = "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" + else: + line = "of.write(\"" + self.escape(self.line) + "\\n\")" + return line + + +class CodeLine(Line): + """ + Container for Python code tag lines. + """ + def __init__(self, line): + Line.__init__(self, line) + + def gen(self, context = None): + return self.line + + +class Assignment: + """ + Representation of everything we know about {{=name }} tags. + Instances of these are used by Assignment lines. + """ + def __init__(self, start, end, name): + self.start = start + self.end = end + self.name = name + + +class AssignmentLine(NormalLine): + """ + Container for normal lines containing assignment tags. Assignment + tags must be in ascending order of 'start' value. + """ + def __init__(self, line): + NormalLine.__init__(self, line) + self.assignments = [] + + def add_assignment(self, start, end, name): + self.assignments.append(Assignment(start, end, name)) + + def gen(self, context = None): + line = self.escape(self.line) + + for assignment in self.assignments: + replacement = "\" + " + assignment.name + " + \"" + idx = line.find(ASSIGN_TAG) + line = line[:idx] + replacement + line[idx + assignment.end - assignment.start:] + if self.is_filename: + return "current_file = \"" + os.path.join(self.out_filebase, line) + "\"; of = open(current_file, \"w\")" + elif self.is_dirname: + dirname = os.path.join(self.out_filebase, line) + return "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" + else: + return "of.write(\"" + line + "\\n\")" + + +class InputLine(Line): + """ + Base class for Input lines. + """ + def __init__(self, props, tag, lineno): + Line.__init__(self, tag) + self.props = props + self.lineno = lineno + + try: + self.prio = int(props["prio"]) + except KeyError: + self.prio = sys.maxint + + def gen(self, context = None): + try: + depends_on = self.props["depends-on"] + try: + depends_on_val = self.props["depends-on-val"] + except KeyError: + self.parse_error("No 'depends-on-val' for 'depends-on' property", + self.lineno, self.line) + except KeyError: + pass + + +class EditBoxInputLine(InputLine): + """ + Base class for 'editbox' Input lines. + + props: + name: example - "Load address" + msg: example - "Please enter the load address" + result: + Sets the value of the variable specified by 'name' to + whatever the user typed. + """ + def __init__(self, props, tag, lineno): + InputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + InputLine.gen(self, context) + name = self.props["name"] + if not name: + self.parse_error("No input 'name' property found", + self.lineno, self.line) + msg = self.props["msg"] + if not msg: + self.parse_error("No input 'msg' property found", + self.lineno, self.line) + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + msg += " [default: " + default_choice + "]" + + line = name + " = default(raw_input(\"" + msg + " \"), " + name + ")" + + return line + + +class GitRepoEditBoxInputLine(EditBoxInputLine): + """ + Base class for 'editbox' Input lines for user input of remote git + repos. This class verifies the existence and connectivity of the + specified git repo. + + props: + name: example - "Load address" + msg: example - "Please enter the load address" + result: + Sets the value of the variable specified by 'name' to + whatever the user typed. + """ + def __init__(self, props, tag, lineno): + EditBoxInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + EditBoxInputLine.gen(self, context) + name = self.props["name"] + if not name: + self.parse_error("No input 'name' property found", + self.lineno, self.line) + msg = self.props["msg"] + if not msg: + self.parse_error("No input 'msg' property found", + self.lineno, self.line) + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + msg += " [default: " + default_choice + "]" + + line = name + " = get_verified_git_repo(\"" + msg + "\"," + name + ")" + + return line + + +class FileEditBoxInputLine(EditBoxInputLine): + """ + Base class for 'editbox' Input lines for user input of existing + files. This class verifies the existence of the specified file. + + props: + name: example - "Load address" + msg: example - "Please enter the load address" + result: + Sets the value of the variable specified by 'name' to + whatever the user typed. + """ + def __init__(self, props, tag, lineno): + EditBoxInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + EditBoxInputLine.gen(self, context) + name = self.props["name"] + if not name: + self.parse_error("No input 'name' property found", + self.lineno, self.line) + msg = self.props["msg"] + if not msg: + self.parse_error("No input 'msg' property found", + self.lineno, self.line) + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + msg += " [default: " + default_choice + "]" + + line = name + " = get_verified_file(\"" + msg + "\"," + name + ", True)" + + return line + + +class BooleanInputLine(InputLine): + """ + Base class for boolean Input lines. + props: + name: example - "keyboard" + msg: example - "Got keyboard?" + result: + Sets the value of the variable specified by 'name' to "yes" or "no" + example - keyboard = "yes" + """ + def __init__(self, props, tag, lineno): + InputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + InputLine.gen(self, context) + name = self.props["name"] + if not name: + self.parse_error("No input 'name' property found", + self.lineno, self.line) + msg = self.props["msg"] + if not msg: + self.parse_error("No input 'msg' property found", + self.lineno, self.line) + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + msg += " [default: " + default_choice + "]" + + line = name + " = boolean(raw_input(\"" + msg + " \"), " + name + ")" + + return line + + +class ListInputLine(InputLine): + """ + Base class for List-based Input lines. e.g. Choicelist, Checklist. + """ + __metaclass__ = ABCMeta + + def __init__(self, props, tag, lineno): + InputLine.__init__(self, props, tag, lineno) + self.choices = [] + + def gen_choicepair_list(self): + """Generate a list of 2-item val:desc lists from self.choices.""" + if not self.choices: + return None + + choicepair_list = list() + + for choice in self.choices: + choicepair = [] + choicepair.append(choice.val) + choicepair.append(choice.desc) + choicepair_list.append(choicepair) + + return choicepair_list + + def gen_degenerate_choicepair_list(self, choices): + """Generate a list of 2-item val:desc with val=desc from passed-in choices.""" + choicepair_list = list() + + for choice in choices: + choicepair = [] + choicepair.append(choice) + choicepair.append(choice) + choicepair_list.append(choicepair) + + return choicepair_list + + def exec_listgen_fn(self, context = None): + """ + Execute the list-generating function contained as a string in + the "gen" property. + """ + retval = None + try: + fname = self.props["gen"] + modsplit = fname.split('.') + mod_fn = modsplit.pop() + mod = '.'.join(modsplit) + + __import__(mod) + # python 2.7 has a better way to do this using importlib.import_module + m = sys.modules[mod] + + fn = getattr(m, mod_fn) + if not fn: + self.parse_error("couldn't load function specified for 'gen' property ", + self.lineno, self.line) + retval = fn(context) + if not retval: + self.parse_error("function specified for 'gen' property returned nothing ", + self.lineno, self.line) + except KeyError: + pass + + return retval + + def gen_choices_str(self, choicepairs): + """ + Generate a numbered list of choices from a list of choicepairs + for display to the user. + """ + choices_str = "" + + for i, choicepair in enumerate(choicepairs): + choices_str += "\t" + str(i + 1) + ") " + choicepair[1] + "\n" + + return choices_str + + def gen_choices_val_str(self, choicepairs): + """ + Generate an array of choice values corresponding to the + numbered list generated by gen_choices_str(). + """ + choices_val_list = "[" + + for i, choicepair in enumerate(choicepairs): + choices_val_list += "\"" + choicepair[0] + "\"," + choices_val_list += "]" + + return choices_val_list + + def gen_choices_val_list(self, choicepairs): + """ + Generate an array of choice values corresponding to the + numbered list generated by gen_choices_str(). + """ + choices_val_list = [] + + for i, choicepair in enumerate(choicepairs): + choices_val_list.append(choicepair[0]) + + return choices_val_list + + def gen_choices_list(self, context = None, checklist = False): + """ + Generate an array of choice values corresponding to the + numbered list generated by gen_choices_str(). + """ + choices = self.exec_listgen_fn(context) + if choices: + if len(choices) == 0: + self.parse_error("No entries available for input list", + self.lineno, self.line) + choicepairs = self.gen_degenerate_choicepair_list(choices) + else: + if len(self.choices) == 0: + self.parse_error("No entries available for input list", + self.lineno, self.line) + choicepairs = self.gen_choicepair_list() + + return choicepairs + + def gen_choices(self, context = None, checklist = False): + """ + Generate an array of choice values corresponding to the + numbered list generated by gen_choices_str(), display it to + the user, and process the result. + """ + msg = self.props["msg"] + if not msg: + self.parse_error("No input 'msg' property found", + self.lineno, self.line) + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + msg += " [default: " + default_choice + "]" + + choicepairs = self.gen_choices_list(context, checklist) + + choices_str = self.gen_choices_str(choicepairs) + choices_val_list = self.gen_choices_val_list(choicepairs) + if checklist: + choiceval = default(find_choicevals(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) + else: + choiceval = default(find_choiceval(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) + + return choiceval + + +def find_choiceval(choice_str, choice_list): + """ + Take number as string and return val string from choice_list, + empty string if oob. choice_list is a simple python list. + """ + choice_val = "" + + try: + choice_idx = int(choice_str) + if choice_idx <= len(choice_list): + choice_idx -= 1 + choice_val = choice_list[choice_idx] + except ValueError: + pass + + return choice_val + + +def find_choicevals(choice_str, choice_list): + """ + Take numbers as space-separated string and return vals list from + choice_list, empty list if oob. choice_list is a simple python + list. + """ + choice_vals = [] + + choices = choice_str.split() + for choice in choices: + choice_vals.append(find_choiceval(choice, choice_list)) + + return choice_vals + + +def default(input_str, name): + """ + Return default if no input_str, otherwise stripped input_str. + """ + if not input_str: + return name + + return input_str.strip() + + +def verify_git_repo(giturl): + """ + Verify that the giturl passed in can be connected to. This can be + used as a check for the existence of the given repo and/or basic + git remote connectivity. + + Returns True if the connection was successful, fals otherwise + """ + if not giturl: + return False + + gitcmd = "git ls-remote %s > /dev/null 2>&1" % (giturl) + rc = subprocess.call(gitcmd, shell=True) + if rc == 0: + return True + + return False + + +def get_verified_git_repo(input_str, name): + """ + Return git repo if verified, otherwise loop forever asking user + for filename. + """ + msg = input_str.strip() + " " + + giturl = default(raw_input(msg), name) + + while True: + if verify_git_repo(giturl): + return giturl + giturl = default(raw_input(msg), name) + + +def get_verified_file(input_str, name, filename_can_be_null): + """ + Return filename if the file exists, otherwise loop forever asking + user for filename. + """ + msg = input_str.strip() + " " + + filename = default(raw_input(msg), name) + + while True: + if not filename and filename_can_be_null: + return filename + if os.path.isfile(filename): + return filename + filename = default(raw_input(msg), name) + + +def replace_file(replace_this, with_this): + """ + Replace the given file with the contents of filename, retaining + the original filename. + """ + try: + replace_this.close() + shutil.copy(with_this, replace_this.name) + except IOError: + pass + + +def boolean(input_str, name): + """ + Return lowercase version of first char in string, or value in name. + """ + if not input_str: + return name + + str = input_str.lower().strip() + if str and str[0] == "y" or str[0] == "n": + return str[0] + else: + return name + + +def strip_base(input_str): + """ + strip '/base' off the end of input_str, so we can use 'base' in + the branch names we present to the user. + """ + if input_str and input_str.endswith("/base"): + return input_str[:-len("/base")] + return input_str.strip() + + +deferred_choices = {} + +def gen_choices_defer(input_line, context, checklist = False): + """ + Save the context hashed the name of the input item, which will be + passed to the gen function later. + """ + name = input_line.props["name"] + + try: + nameappend = input_line.props["nameappend"] + except KeyError: + nameappend = "" + + try: + branches_base = input_line.props["branches_base"] + except KeyError: + branches_base = "" + + filename = input_line.props["filename"] + + closetag_start = filename.find(CLOSE_TAG) + + if closetag_start != -1: + filename = filename[closetag_start + len(CLOSE_TAG):] + + filename = filename.strip() + filename = os.path.splitext(filename)[0] + + captured_context = capture_context(context) + context["filename"] = filename + captured_context["filename"] = filename + context["nameappend"] = nameappend + captured_context["nameappend"] = nameappend + context["branches_base"] = branches_base + captured_context["branches_base"] = branches_base + + deferred_choice = (input_line, captured_context, checklist) + key = name + "_" + filename + "_" + nameappend + deferred_choices[key] = deferred_choice + + +def invoke_deferred_choices(name): + """ + Invoke the choice generation function using the context hashed by + 'name'. + """ + deferred_choice = deferred_choices[name] + input_line = deferred_choice[0] + context = deferred_choice[1] + checklist = deferred_choice[2] + + context["name"] = name + + choices = input_line.gen_choices(context, checklist) + + return choices + + +class ChoicelistInputLine(ListInputLine): + """ + Base class for choicelist Input lines. + props: + name: example - "xserver_choice" + msg: example - "Please select an xserver for this machine" + result: + Sets the value of the variable specified by 'name' to whichever Choice was chosen + example - xserver_choice = "xserver_vesa" + """ + def __init__(self, props, tag, lineno): + ListInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + InputLine.gen(self, context) + + gen_choices_defer(self, context) + name = self.props["name"] + nameappend = context["nameappend"] + filename = context["filename"] + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" + + return line + + +class ListValInputLine(InputLine): + """ + Abstract base class for choice and checkbox Input lines. + """ + def __init__(self, props, tag, lineno): + InputLine.__init__(self, props, tag, lineno) + + try: + self.val = self.props["val"] + except KeyError: + self.parse_error("No input 'val' property found", self.lineno, self.line) + + try: + self.desc = self.props["msg"] + except KeyError: + self.parse_error("No input 'msg' property found", self.lineno, self.line) + + +class ChoiceInputLine(ListValInputLine): + """ + Base class for choicelist item Input lines. + """ + def __init__(self, props, tag, lineno): + ListValInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + return None + + +class ChecklistInputLine(ListInputLine): + """ + Base class for checklist Input lines. + """ + def __init__(self, props, tag, lineno): + ListInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + InputLine.gen(self, context) + + gen_choices_defer(self, context, True) + name = self.props["name"] + nameappend = context["nameappend"] + filename = context["filename"] + + try: + default_choice = self.props["default"] + except KeyError: + default_choice = "" + + line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" + + return line + + +class CheckInputLine(ListValInputLine): + """ + Base class for checklist item Input lines. + """ + def __init__(self, props, tag, lineno): + ListValInputLine.__init__(self, props, tag, lineno) + + def gen(self, context = None): + return None + + +class SubstrateBase(object): + """ + Base class for both expanded and unexpanded file and dir container + objects. + """ + def __init__(self, filename, filebase, out_filebase): + self.filename = filename + self.filebase = filebase + self.out_filebase = out_filebase + self.raw_lines = [] + self.expanded_lines = [] + self.prev_choicelist = None + + def parse_error(self, msg, lineno, line): + raise SyntaxError("%s: [%s: %d]: %s" % (msg, self.filename, lineno, line)) + + def expand_input_tag(self, tag, lineno): + """ + Input tags consist of the word 'input' at the beginning, + followed by name:value property pairs which are converted into + a dictionary. + """ + propstr = tag[len(INPUT_TAG):] + + props = dict(prop.split(":", 1) for prop in shlex.split(propstr)) + props["filename"] = self.filename + + input_type = props[INPUT_TYPE_PROPERTY] + if not props[INPUT_TYPE_PROPERTY]: + self.parse_error("No input 'type' property found", lineno, tag) + + if input_type == "boolean": + return BooleanInputLine(props, tag, lineno) + if input_type == "edit": + return EditBoxInputLine(props, tag, lineno) + if input_type == "edit-git-repo": + return GitRepoEditBoxInputLine(props, tag, lineno) + if input_type == "edit-file": + return FileEditBoxInputLine(props, tag, lineno) + elif input_type == "choicelist": + self.prev_choicelist = ChoicelistInputLine(props, tag, lineno) + return self.prev_choicelist + elif input_type == "choice": + if not self.prev_choicelist: + self.parse_error("Found 'choice' input tag but no previous choicelist", + lineno, tag) + choice = ChoiceInputLine(props, tag, lineno) + self.prev_choicelist.choices.append(choice) + return choice + elif input_type == "checklist": + return ChecklistInputLine(props, tag, lineno) + elif input_type == "check": + return CheckInputLine(props, tag, lineno) + + def expand_assignment_tag(self, start, line, lineno): + """ + Expand all tags in a line. + """ + expanded_line = AssignmentLine(line.rstrip()) + + while start != -1: + end = line.find(CLOSE_TAG, start) + if end == -1: + self.parse_error("No close tag found for assignment tag", lineno, line) + else: + name = line[start + len(ASSIGN_TAG):end].strip() + expanded_line.add_assignment(start, end + len(CLOSE_TAG), name) + start = line.find(ASSIGN_TAG, end) + + return expanded_line + + def expand_tag(self, line, lineno): + """ + Returns a processed tag line, or None if there was no tag + + The rules for tags are very simple: + - No nested tags + - Tags start with {{ and end with }} + - An assign tag, {{=, can appear anywhere and will + be replaced with what the assignment evaluates to + - Any other tag occupies the whole line it is on + - if there's anything else on the tag line, it's an error + - if it starts with 'input', it's an input tag and + will only be used for prompting and setting variables + - anything else is straight Python + - tags are in effect only until the next blank line or tag or 'pass' tag + - we don't have indentation in tags, but we need some way to end a block + forcefully without blank lines or other tags - that's the 'pass' tag + - todo: implement pass tag + - directories and filenames can have tags as well, but only assignment + and 'if' code lines + - directories and filenames are the only case where normal tags can + coexist with normal text on the same 'line' + """ + start = line.find(ASSIGN_TAG) + if start != -1: + return self.expand_assignment_tag(start, line, lineno) + + start = line.find(OPEN_TAG) + if start == -1: + return None + + end = line.find(CLOSE_TAG, 0) + if end == -1: + self.parse_error("No close tag found for open tag", lineno, line) + + tag = line[start + len(OPEN_TAG):end].strip() + + if not tag.lstrip().startswith(INPUT_TAG): + return CodeLine(tag) + + return self.expand_input_tag(tag, lineno) + + def expand_file_or_dir_name(self): + """ + Expand file or dir names into codeline. Dirnames and + filenames can only have assignments or if statements. First + translate if statements into CodeLine + (dirname or filename + creation). + """ + lineno = 0 + + line = self.filename[len(self.filebase):] + if line.startswith("/"): + line = line[1:] + opentag_start = -1 + + start = line.find(OPEN_TAG) + while start != -1: + if not line[start:].startswith(ASSIGN_TAG): + opentag_start = start + break + start += len(ASSIGN_TAG) + start = line.find(OPEN_TAG, start) + + if opentag_start != -1: + end = line.find(CLOSE_TAG, opentag_start) + if end == -1: + self.parse_error("No close tag found for open tag", lineno, line) + # we have a {{ tag i.e. code + tag = line[opentag_start + len(OPEN_TAG):end].strip() + + if not tag.lstrip().startswith(IF_TAG): + self.parse_error("Only 'if' tags are allowed in file or directory names", + lineno, line) + self.expanded_lines.append(CodeLine(tag)) + + # everything after }} is the actual filename (possibly with assignments) + # everything before is the pathname + line = line[:opentag_start] + line[end + len(CLOSE_TAG):].strip() + + assign_start = line.find(ASSIGN_TAG) + if assign_start != -1: + assignment_tag = self.expand_assignment_tag(assign_start, line, lineno) + if isinstance(self, SubstrateFile): + assignment_tag.is_filename = True + assignment_tag.out_filebase = self.out_filebase + elif isinstance(self, SubstrateDir): + assignment_tag.is_dirname = True + assignment_tag.out_filebase = self.out_filebase + self.expanded_lines.append(assignment_tag) + return + + normal_line = NormalLine(line) + if isinstance(self, SubstrateFile): + normal_line.is_filename = True + normal_line.out_filebase = self.out_filebase + elif isinstance(self, SubstrateDir): + normal_line.is_dirname = True + normal_line.out_filebase = self.out_filebase + self.expanded_lines.append(normal_line) + + def expand(self): + """ + Expand the file or dir name first, eventually this ends up + creating the file or dir. + """ + self.expand_file_or_dir_name() + + +class SubstrateFile(SubstrateBase): + """ + Container for both expanded and unexpanded substrate files. + """ + def __init__(self, filename, filebase, out_filebase): + SubstrateBase.__init__(self, filename, filebase, out_filebase) + + def read(self): + if self.raw_lines: + return + f = open(self.filename) + self.raw_lines = f.readlines() + + def expand(self): + """Expand the contents of all template tags in the file.""" + SubstrateBase.expand(self) + self.read() + + for lineno, line in enumerate(self.raw_lines): + expanded_line = self.expand_tag(line, lineno + 1) # humans not 0-based + if not expanded_line: + expanded_line = NormalLine(line.rstrip()) + self.expanded_lines.append(expanded_line) + + def gen(self, context = None): + """Generate the code that generates the BSP.""" + base_indent = 0 + + indent = new_indent = base_indent + + for line in self.expanded_lines: + genline = line.gen(context) + if not genline: + continue + if isinstance(line, InputLine): + line.generated_line = genline + continue + if genline.startswith(OPEN_START): + if indent == 1: + base_indent = 1 + if indent: + if genline == BLANKLINE_STR or (not genline.startswith(NORMAL_START) + and not genline.startswith(OPEN_START)): + indent = new_indent = base_indent + if genline.endswith(":"): + new_indent = base_indent + 1 + line.generated_line = (indent * INDENT_STR) + genline + indent = new_indent + + +class SubstrateDir(SubstrateBase): + """ + Container for both expanded and unexpanded substrate dirs. + """ + def __init__(self, filename, filebase, out_filebase): + SubstrateBase.__init__(self, filename, filebase, out_filebase) + + def expand(self): + SubstrateBase.expand(self) + + def gen(self, context = None): + """Generate the code that generates the BSP.""" + indent = new_indent = 0 + for line in self.expanded_lines: + genline = line.gen(context) + if not genline: + continue + if genline.endswith(":"): + new_indent = 1 + else: + new_indent = 0 + line.generated_line = (indent * INDENT_STR) + genline + indent = new_indent + + +def expand_target(target, all_files, out_filebase): + """ + Expand the contents of all template tags in the target. This + means removing tags and categorizing or creating lines so that + future passes can process and present input lines and generate the + corresponding lines of the Python program that will be exec'ed to + actually produce the final BSP. 'all_files' includes directories. + """ + for root, dirs, files in os.walk(target): + for file in files: + if file.endswith("~") or file.endswith("#"): + continue + f = os.path.join(root, file) + sfile = SubstrateFile(f, target, out_filebase) + sfile.expand() + all_files.append(sfile) + + for dir in dirs: + d = os.path.join(root, dir) + sdir = SubstrateDir(d, target, out_filebase) + sdir.expand() + all_files.append(sdir) + + +def gen_program_machine_lines(machine, program_lines): + """ + Use the input values we got from the command line. + """ + line = "machine = \"" + machine + "\"" + program_lines.append(line) + + line = "layer_name = \"" + machine + "\"" + program_lines.append(line) + + +def sort_inputlines(input_lines): + """Sort input lines according to priority (position).""" + input_lines.sort(key = lambda l: l.prio) + + +def find_parent_dependency(lines, depends_on): + for i, line in lines: + if isinstance(line, CodeLine): + continue + if line.props["name"] == depends_on: + return i + + return -1 + + +def process_inputline_dependencies(input_lines, all_inputlines): + """If any input lines depend on others, put the others first.""" + for line in input_lines: + if isinstance(line, InputLineGroup): + group_inputlines = [] + process_inputline_dependencies(line.group, group_inputlines) + line.group = group_inputlines + all_inputlines.append(line) + continue + + if isinstance(line, CodeLine) or isinstance(line, NormalLine): + all_inputlines.append(line) + continue + + try: + depends_on = line.props["depends-on"] + depends_codeline = "if " + line.props["depends-on"] + " == \"" + line.props["depends-on-val"] + "\":" + all_inputlines.append(CodeLine(depends_codeline)) + all_inputlines.append(line) + except KeyError: + all_inputlines.append(line) + + +def conditional_filename(filename): + """ + Check if the filename itself contains a conditional statement. If + so, return a codeline for it. + """ + opentag_start = filename.find(OPEN_TAG) + + if opentag_start != -1: + if filename[opentag_start:].startswith(ASSIGN_TAG): + return None + end = filename.find(CLOSE_TAG, opentag_start) + if end == -1: + print "No close tag found for open tag in filename %s" % filename + sys.exit(1) + + # we have a {{ tag i.e. code + tag = filename[opentag_start + len(OPEN_TAG):end].strip() + if not tag.lstrip().startswith(IF_TAG): + print "Only 'if' tags are allowed in file or directory names, filename: %s" % filename + sys.exit(1) + + return CodeLine(tag) + + return None + + +class InputLineGroup(InputLine): + """ + InputLine that does nothing but group other input lines + corresponding to all the input lines in a SubstrateFile so they + can be generated as a group. prio is the only property used. + """ + def __init__(self, codeline): + InputLine.__init__(self, {}, "", 0) + self.group = [] + self.prio = sys.maxint + self.group.append(codeline) + + def append(self, line): + self.group.append(line) + if line.prio < self.prio: + self.prio = line.prio + + def len(self): + return len(self.group) + + +def gather_inputlines(files): + """ + Gather all the InputLines - we want to generate them first. + """ + all_inputlines = [] + input_lines = [] + + for file in files: + if isinstance(file, SubstrateFile): + group = None + basename = os.path.basename(file.filename) + + codeline = conditional_filename(basename) + if codeline: + group = InputLineGroup(codeline) + + have_condition = False + condition_to_write = None + for line in file.expanded_lines: + if isinstance(line, CodeLine): + have_condition = True + condition_to_write = line + continue + if isinstance(line, InputLine): + if group: + if condition_to_write: + condition_to_write.prio = line.prio + condition_to_write.discard = True + group.append(condition_to_write) + condition_to_write = None + group.append(line) + else: + if condition_to_write: + condition_to_write.prio = line.prio + condition_to_write.discard = True + input_lines.append(condition_to_write) + condition_to_write = None + input_lines.append(line) + else: + if condition_to_write: + condition_to_write = None + if have_condition: + if not line.line.strip(): + line.discard = True + input_lines.append(line) + have_condition = False + + if group and group.len() > 1: + input_lines.append(group) + + sort_inputlines(input_lines) + process_inputline_dependencies(input_lines, all_inputlines) + + return all_inputlines + + +def run_program_lines(linelist, codedump): + """ + For a single file, print all the python code into a buf and execute it. + """ + buf = "\n".join(linelist) + + if codedump: + of = open("bspgen.out", "w") + of.write(buf) + of.close() + exec buf + + +def gen_target(files, context = None): + """ + Generate the python code for each file. + """ + for file in files: + file.gen(context) + + +def gen_program_header_lines(program_lines): + """ + Generate any imports we need. + """ + program_lines.append("current_file = \"\"") + + +def gen_supplied_property_vals(properties, program_lines): + """ + Generate user-specified entries for input values instead of + generating input prompts. + """ + for name, val in properties.iteritems(): + program_line = name + " = \"" + val + "\"" + program_lines.append(program_line) + + +def gen_initial_property_vals(input_lines, program_lines): + """ + Generate null or default entries for input values, so we don't + have undefined variables. + """ + for line in input_lines: + if isinstance(line, InputLineGroup): + gen_initial_property_vals(line.group, program_lines) + continue + + if isinstance(line, InputLine): + try: + name = line.props["name"] + try: + default_val = "\"" + line.props["default"] + "\"" + except: + default_val = "\"\"" + program_line = name + " = " + default_val + program_lines.append(program_line) + except KeyError: + pass + + +def gen_program_input_lines(input_lines, program_lines, context, in_group = False): + """ + Generate only the input lines used for prompting the user. For + that, we only have input lines and CodeLines that affect the next + input line. + """ + indent = new_indent = 0 + + for line in input_lines: + if isinstance(line, InputLineGroup): + gen_program_input_lines(line.group, program_lines, context, True) + continue + if not line.line.strip(): + continue + + genline = line.gen(context) + if not genline: + continue + if genline.endswith(":"): + new_indent += 1 + else: + if indent > 1 or (not in_group and indent): + new_indent -= 1 + + line.generated_line = (indent * INDENT_STR) + genline + program_lines.append(line.generated_line) + + indent = new_indent + + +def gen_program_lines(target_files, program_lines): + """ + Generate the program lines that make up the BSP generation + program. This appends the generated lines of all target_files to + program_lines, and skips input lines, which are dealt with + separately, or omitted. + """ + for file in target_files: + if file.filename.endswith("noinstall"): + continue + + for line in file.expanded_lines: + if isinstance(line, InputLine): + continue + if line.discard: + continue + + program_lines.append(line.generated_line) + + +def create_context(machine, arch, scripts_path): + """ + Create a context object for use in deferred function invocation. + """ + context = {} + + context["machine"] = machine + context["arch"] = arch + context["scripts_path"] = scripts_path + + return context + + +def capture_context(context): + """ + Create a context object for use in deferred function invocation. + """ + captured_context = {} + + captured_context["machine"] = context["machine"] + captured_context["arch"] = context["arch"] + captured_context["scripts_path"] = context["scripts_path"] + + return captured_context + + +def expand_targets(context, bsp_output_dir, expand_common=True): + """ + Expand all the tags in both the common and machine-specific + 'targets'. + + If expand_common is False, don't expand the common target (this + option is used to create special-purpose layers). + """ + target_files = [] + + machine = context["machine"] + arch = context["arch"] + scripts_path = context["scripts_path"] + + lib_path = scripts_path + '/lib' + bsp_path = lib_path + '/bsp' + arch_path = bsp_path + '/substrate/target/arch' + + if expand_common: + common = os.path.join(arch_path, "common") + expand_target(common, target_files, bsp_output_dir) + + arches = os.listdir(arch_path) + if arch not in arches or arch == "common": + print "Invalid karch, exiting\n" + sys.exit(1) + + target = os.path.join(arch_path, arch) + expand_target(target, target_files, bsp_output_dir) + + gen_target(target_files, context) + + return target_files + + +def yocto_common_create(machine, target, scripts_path, layer_output_dir, codedump, properties_file, properties_str="", expand_common=True): + """ + Common layer-creation code + + machine - user-defined machine name (if needed, will generate 'machine' var) + target - the 'target' the layer will be based on, must be one in + scripts/lib/bsp/substrate/target/arch + scripts_path - absolute path to yocto /scripts dir + layer_output_dir - dirname to create for layer + codedump - dump generated code to bspgen.out + properties_file - use values from this file if nonempty i.e no prompting + properties_str - use values from this string if nonempty i.e no prompting + expand_common - boolean, use the contents of (for bsp layers) arch/common + """ + if os.path.exists(layer_output_dir): + print "\nlayer output dir already exists, exiting. (%s)" % layer_output_dir + sys.exit(1) + + properties = None + + if properties_file: + try: + infile = open(properties_file, "r") + except IOError: + print "Couldn't open properties file %s for reading, exiting" % properties_file + sys.exit(1) + + properties = json.load(infile) + + if properties_str and not properties: + properties = json.loads(properties_str) + + os.mkdir(layer_output_dir) + + context = create_context(machine, target, scripts_path) + target_files = expand_targets(context, layer_output_dir, expand_common) + + input_lines = gather_inputlines(target_files) + + program_lines = [] + + gen_program_header_lines(program_lines) + + gen_initial_property_vals(input_lines, program_lines) + + if properties: + gen_supplied_property_vals(properties, program_lines) + + gen_program_machine_lines(machine, program_lines) + + if not properties: + gen_program_input_lines(input_lines, program_lines, context) + + gen_program_lines(target_files, program_lines) + + run_program_lines(program_lines, codedump) + + +def yocto_layer_create(layer_name, scripts_path, layer_output_dir, codedump, properties_file, properties=""): + """ + Create yocto layer + + layer_name - user-defined layer name + scripts_path - absolute path to yocto /scripts dir + layer_output_dir - dirname to create for layer + codedump - dump generated code to bspgen.out + properties_file - use values from this file if nonempty i.e no prompting + properties - use values from this string if nonempty i.e no prompting + """ + yocto_common_create(layer_name, "layer", scripts_path, layer_output_dir, codedump, properties_file, properties, False) + + print "\nNew layer created in %s.\n" % (layer_output_dir) + print "Don't forget to add it to your BBLAYERS (for details see %s\README)." % (layer_output_dir) + + +def yocto_bsp_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties=None): + """ + Create bsp + + machine - user-defined machine name + arch - the arch the bsp will be based on, must be one in + scripts/lib/bsp/substrate/target/arch + scripts_path - absolute path to yocto /scripts dir + bsp_output_dir - dirname to create for BSP + codedump - dump generated code to bspgen.out + properties_file - use values from this file if nonempty i.e no prompting + properties - use values from this string if nonempty i.e no prompting + """ + yocto_common_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties) + + print "\nNew %s BSP created in %s" % (arch, bsp_output_dir) + + +def print_dict(items, indent = 0): + """ + Print the values in a possibly nested dictionary. + """ + for key, val in items.iteritems(): + print " "*indent + "\"%s\" :" % key, + if type(val) == dict: + print "{" + print_dict(val, indent + 1) + print " "*indent + "}" + else: + print "%s" % val + + +def get_properties(input_lines): + """ + Get the complete set of properties for all the input items in the + BSP, as a possibly nested dictionary. + """ + properties = {} + + for line in input_lines: + if isinstance(line, InputLineGroup): + statement = line.group[0].line + group_properties = get_properties(line.group) + properties[statement] = group_properties + continue + + if not isinstance(line, InputLine): + continue + + if isinstance(line, ChoiceInputLine): + continue + + props = line.props + item = {} + name = props["name"] + for key, val in props.items(): + if not key == "name": + item[key] = val + properties[name] = item + + return properties + + +def yocto_layer_list_properties(arch, scripts_path, properties_file, expand_common=True): + """ + List the complete set of properties for all the input items in the + layer. If properties_file is non-null, write the complete set of + properties as a nested JSON object corresponding to a possibly + nested dictionary. + """ + context = create_context("unused", arch, scripts_path) + target_files = expand_targets(context, "unused", expand_common) + + input_lines = gather_inputlines(target_files) + + properties = get_properties(input_lines) + if properties_file: + try: + of = open(properties_file, "w") + except IOError: + print "Couldn't open properties file %s for writing, exiting" % properties_file + sys.exit(1) + + json.dump(properties, of) + + print_dict(properties) + + +def split_nested_property(property): + """ + A property name of the form x.y describes a nested property + i.e. the property y is contained within x and can be addressed + using standard JSON syntax for nested properties. Note that if a + property name itself contains '.', it should be contained in + double quotes. + """ + splittable_property = "" + in_quotes = False + for c in property: + if c == '.' and not in_quotes: + splittable_property += '\n' + continue + if c == '"': + in_quotes = not in_quotes + splittable_property += c + + split_properties = splittable_property.split('\n') + + if len(split_properties) > 1: + return split_properties + + return None + + +def find_input_line_group(substring, input_lines): + """ + Find and return the InputLineGroup containing the specified substring. + """ + for line in input_lines: + if isinstance(line, InputLineGroup): + if substring in line.group[0].line: + return line + + return None + + +def find_input_line(name, input_lines): + """ + Find the input line with the specified name. + """ + for line in input_lines: + if isinstance(line, InputLineGroup): + l = find_input_line(name, line.group) + if l: + return l + + if isinstance(line, InputLine): + try: + if line.props["name"] == name: + return line + if line.props["name"] + "_" + line.props["nameappend"] == name: + return line + except KeyError: + pass + + return None + + +def print_values(type, values_list): + """ + Print the values in the given list of values. + """ + if type == "choicelist": + for value in values_list: + print "[\"%s\", \"%s\"]" % (value[0], value[1]) + elif type == "boolean": + for value in values_list: + print "[\"%s\", \"%s\"]" % (value[0], value[1]) + + +def yocto_layer_list_property_values(arch, property, scripts_path, properties_file, expand_common=True): + """ + List the possible values for a given input property. If + properties_file is non-null, write the complete set of properties + as a JSON object corresponding to an array of possible values. + """ + context = create_context("unused", arch, scripts_path) + context["name"] = property + + target_files = expand_targets(context, "unused", expand_common) + + input_lines = gather_inputlines(target_files) + + properties = get_properties(input_lines) + + nested_properties = split_nested_property(property) + if nested_properties: + # currently the outer property of a nested property always + # corresponds to an input line group + input_line_group = find_input_line_group(nested_properties[0], input_lines) + if input_line_group: + input_lines[:] = input_line_group.group[1:] + # The inner property of a nested property name is the + # actual property name we want, so reset to that + property = nested_properties[1] + + input_line = find_input_line(property, input_lines) + if not input_line: + print "Couldn't find values for property %s" % property + return + + values_list = [] + + type = input_line.props["type"] + if type == "boolean": + values_list.append(["y", "n"]) + elif type == "choicelist" or type == "checklist": + try: + gen_fn = input_line.props["gen"] + if nested_properties: + context["filename"] = nested_properties[0] + try: + context["branches_base"] = input_line.props["branches_base"] + except KeyError: + context["branches_base"] = None + values_list = input_line.gen_choices_list(context, False) + except KeyError: + for choice in input_line.choices: + choicepair = [] + choicepair.append(choice.val) + choicepair.append(choice.desc) + values_list.append(choicepair) + + if properties_file: + try: + of = open(properties_file, "w") + except IOError: + print "Couldn't open properties file %s for writing, exiting" % properties_file + sys.exit(1) + + json.dump(values_list, of) + + print_values(type, values_list) + + +def yocto_bsp_list(args, scripts_path, properties_file): + """ + Print available architectures, or the complete list of properties + defined by the BSP, or the possible values for a particular BSP + property. + """ + if len(args) < 1: + return False + + if args[0] == "karch": + lib_path = scripts_path + '/lib' + bsp_path = lib_path + '/bsp' + arch_path = bsp_path + '/substrate/target/arch' + print "Architectures available:" + for arch in os.listdir(arch_path): + if arch == "common" or arch == "layer": + continue + print " %s" % arch + return True + else: + arch = args[0] + + if len(args) < 2 or len(args) > 3: + return False + + if len(args) == 2: + if args[1] == "properties": + yocto_layer_list_properties(arch, scripts_path, properties_file) + else: + return False + + if len(args) == 3: + if args[1] == "property": + yocto_layer_list_property_values(arch, args[2], scripts_path, properties_file) + else: + return False + + return True + + +def yocto_layer_list(args, scripts_path, properties_file): + """ + Print the complete list of input properties defined by the layer, + or the possible values for a particular layer property. + """ + if len(args) < 1: + return False + + if len(args) < 1 or len(args) > 2: + return False + + if len(args) == 1: + if args[0] == "properties": + yocto_layer_list_properties("layer", scripts_path, properties_file, False) + else: + return False + + if len(args) == 2: + if args[0] == "property": + yocto_layer_list_property_values("layer", args[1], scripts_path, properties_file, False) + else: + return False + + return True + + +def map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): + """ + Return the linux-yocto bsp branch to use with the specified + kbranch. This handles the -standard variants for 3.4 and 3.8; the + other variants don't need mappings. + """ + if need_new_kbranch == "y": + kbranch = new_kbranch + else: + kbranch = existing_kbranch + + if kbranch.startswith("standard/common-pc-64"): + return "bsp/common-pc-64/common-pc-64-standard.scc" + if kbranch.startswith("standard/common-pc"): + return "bsp/common-pc/common-pc-standard.scc" + else: + return "ktypes/standard/standard.scc" + + +def map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): + """ + Return the linux-yocto bsp branch to use with the specified + kbranch. This handles the -preempt-rt variants for 3.4 and 3.8; + the other variants don't need mappings. + """ + if need_new_kbranch == "y": + kbranch = new_kbranch + else: + kbranch = existing_kbranch + + if kbranch.startswith("standard/preempt-rt/common-pc-64"): + return "bsp/common-pc-64/common-pc-64-preempt-rt.scc" + if kbranch.startswith("standard/preempt-rt/common-pc"): + return "bsp/common-pc/common-pc-preempt-rt.scc" + else: + return "ktypes/preempt-rt/preempt-rt.scc" + + +def map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): + """ + Return the linux-yocto bsp branch to use with the specified + kbranch. This handles the -tiny variants for 3.4 and 3.8; the + other variants don't need mappings. + """ + if need_new_kbranch == "y": + kbranch = new_kbranch + else: + kbranch = existing_kbranch + + if kbranch.startswith("standard/tiny/common-pc"): + return "bsp/common-pc/common-pc-tiny.scc" + else: + return "ktypes/tiny/tiny.scc" diff --git a/scripts/lib/bsp/help.py b/scripts/lib/bsp/help.py new file mode 100644 index 0000000000..7c436d6be0 --- /dev/null +++ b/scripts/lib/bsp/help.py @@ -0,0 +1,1043 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2012, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module implements some basic help invocation functions along +# with the bulk of the help topic text for the Yocto BSP Tools. +# +# AUTHORS +# Tom Zanussi +# + +import subprocess +import logging + + +def subcommand_error(args): + logging.info("invalid subcommand %s" % args[0]) + + +def display_help(subcommand, subcommands): + """ + Display help for subcommand. + """ + if subcommand not in subcommands: + return False + + help = subcommands.get(subcommand, subcommand_error)[2] + pager = subprocess.Popen('less', stdin=subprocess.PIPE) + pager.communicate(help) + + return True + + +def yocto_help(args, usage_str, subcommands): + """ + Subcommand help dispatcher. + """ + if len(args) == 1 or not display_help(args[1], subcommands): + print(usage_str) + + +def invoke_subcommand(args, parser, main_command_usage, subcommands): + """ + Dispatch to subcommand handler borrowed from combo-layer. + Should use argparse, but has to work in 2.6. + """ + if not args: + logging.error("No subcommand specified, exiting") + parser.print_help() + elif args[0] == "help": + yocto_help(args, main_command_usage, subcommands) + elif args[0] not in subcommands: + logging.error("Unsupported subcommand %s, exiting\n" % (args[0])) + parser.print_help() + else: + usage = subcommands.get(args[0], subcommand_error)[1] + subcommands.get(args[0], subcommand_error)[0](args[1:], usage) + + +## +# yocto-bsp help and usage strings +## + +yocto_bsp_usage = """ + + Create a customized Yocto BSP layer. + + usage: yocto-bsp [--version] [--help] COMMAND [ARGS] + + Current 'yocto-bsp' commands are: + create Create a new Yocto BSP + list List available values for options and BSP properties + + See 'yocto-bsp help COMMAND' for more information on a specific command. +""" + +yocto_bsp_help_usage = """ + + usage: yocto-bsp help + + This command displays detailed help for the specified subcommand. +""" + +yocto_bsp_create_usage = """ + + Create a new Yocto BSP + + usage: yocto-bsp create [-o | --outdir ] + [-i | --infile ] + + This command creates a Yocto BSP based on the specified parameters. + The new BSP will be a new Yocto BSP layer contained by default within + the top-level directory specified as 'meta-bsp-name'. The -o option + can be used to place the BSP layer in a directory with a different + name and location. + + The value of the 'karch' parameter determines the set of files that + will be generated for the BSP, along with the specific set of + 'properties' that will be used to fill out the BSP-specific portions + of the BSP. The possible values for the 'karch' paramter can be + listed via 'yocto-bsp list karch'. + + NOTE: Once created, you should add your new layer to your + bblayers.conf file in order for it to be subsequently seen and + modified by the yocto-kernel tool. + + See 'yocto bsp help create' for more detailed instructions. +""" + +yocto_bsp_create_help = """ + +NAME + yocto-bsp create - Create a new Yocto BSP + +SYNOPSIS + yocto-bsp create [-o | --outdir ] + [-i | --infile ] + +DESCRIPTION + This command creates a Yocto BSP based on the specified + parameters. The new BSP will be a new Yocto BSP layer contained + by default within the top-level directory specified as + 'meta-bsp-name'. The -o option can be used to place the BSP layer + in a directory with a different name and location. + + The value of the 'karch' parameter determines the set of files + that will be generated for the BSP, along with the specific set of + 'properties' that will be used to fill out the BSP-specific + portions of the BSP. The possible values for the 'karch' paramter + can be listed via 'yocto-bsp list karch'. + + The BSP-specific properties that define the values that will be + used to generate a particular BSP can be specified on the + command-line using the -i option and supplying a JSON object + consisting of the set of name:value pairs needed by the BSP. + + If the -i option is not used, the user will be interactively + prompted for each of the required property values, which will then + be used as values for BSP generation. + + The set of properties available for a given architecture can be + listed using the 'yocto-bsp list' command. + + Specifying -c causes the Python code generated and executed to + create the BSP to be dumped to the 'bspgen.out' file in the + current directory, and is useful for debugging. + + NOTE: Once created, you should add your new layer to your + bblayers.conf file in order for it to be subsequently seen and + modified by the yocto-kernel tool. + + For example, assuming your poky repo is at /path/to/poky, your new + BSP layer is at /path/to/poky/meta-mybsp, and your build directory + is /path/to/build: + + $ gedit /path/to/build/conf/bblayers.conf + + BBLAYERS ?= " \\ + /path/to/poky/meta \\ + /path/to/poky/meta-yocto \\ + /path/to/poky/meta-mybsp \\ + " +""" + +yocto_bsp_list_usage = """ + + usage: yocto-bsp list karch + yocto-bsp list properties + [-o | --outfile ] + yocto-bsp list property + [-o | --outfile ] + + This command enumerates the complete set of possible values for a + specified option or property needed by the BSP creation process. + + The first form enumerates all the possible values that exist and can + be specified for the 'karch' parameter to the 'yocto bsp create' + command. + + The second form enumerates all the possible properties that exist and + must have values specified for them in the 'yocto bsp create' command + for the given 'karch'. + + The third form enumerates all the possible values that exist and can + be specified for any of the enumerable properties of the given + 'karch' in the 'yocto bsp create' command. + + See 'yocto-bsp help list' for more details. +""" + +yocto_bsp_list_help = """ + +NAME + yocto-bsp list - List available values for options and BSP properties + +SYNOPSIS + yocto-bsp list karch + yocto-bsp list properties + [--o | -outfile ] + yocto-bsp list property + [--o | -outfile ] + +DESCRIPTION + This command enumerates the complete set of possible values for a + specified option or property needed by the BSP creation process. + + The first form enumerates all the possible values that exist and + can be specified for the 'karch' parameter to the 'yocto bsp + create' command. Example output for the 'list karch' command: + + $ yocto-bsp list karch + Architectures available: + arm + powerpc + i386 + mips + x86_64 + qemu + + The second form enumerates all the possible properties that exist + and must have values specified for them in the 'yocto bsp create' + command for the given 'karch'. This command is mainly meant to + allow the development user interface alternatives to the default + text-based prompting interface. If the -o option is specified, + the list of properties, in addition to being displayed, will be + written to the specified file as a JSON object. In this case, the + object will consist of the set of name:value pairs corresponding + to the (possibly nested) dictionary of properties defined by the + input statements used by the BSP. Some example output for the + 'list properties' command: + + $ yocto-bsp list arm properties + "touchscreen" : { + "msg" : Does your BSP have a touchscreen? (y/N) + "default" : n + "type" : boolean + } + "uboot_loadaddress" : { + "msg" : Please specify a value for UBOOT_LOADADDRESS. + "default" : 0x80008000 + "type" : edit + "prio" : 40 + } + "kernel_choice" : { + "prio" : 10 + "default" : linux-yocto_3.2 + "depends-on" : use_default_kernel + "depends-on-val" : n + "msg" : Please choose the kernel to use in this BSP => + "type" : choicelist + "gen" : bsp.kernel.kernels + } + "if kernel_choice == "linux-yocto_3.0":" : { + "base_kbranch_linux_yocto_3_0" : { + "prio" : 20 + "default" : yocto/standard + "depends-on" : new_kbranch_linux_yocto_3_0 + "depends-on-val" : y + "msg" : Please choose a machine branch to base this BSP on => + "type" : choicelist + "gen" : bsp.kernel.all_branches + } + . + . + . + + Each entry in the output consists of the name of the input element + e.g. "touchscreen", followed by the properties defined for that + element enclosed in braces. This information should provide + sufficient information to create a complete user interface with. + Two features of the scheme provide for conditional input. First, + if a Python "if" statement appears in place of an input element + name, the set of enclosed input elements apply and should be + presented to the user only if the 'if' statement evaluates to + true. The test in the if statement will always reference another + input element in the list, which means that the element being + tested should be presented to the user before the elements + enclosed by the if block. Secondly, in a similar way, some + elements contain "depends-on" and depends-on-val" tags, which mean + that the affected input element should only be presented to the + user if the element it depends on has already been presented to + the user and the user has selected the specified value for that + element. + + The third form enumerates all the possible values that exist and + can be specified for any of the enumerable properties of the given + 'karch' in the 'yocto bsp create' command. If the -o option is + specified, the list of values for the given property, in addition + to being displayed, will be written to the specified file as a + JSON object. In this case, the object will consist of the set of + name:value pairs corresponding to the array of property values + associated with the property. + + $ yocto-bsp list i386 property xserver_choice + ["xserver_vesa", "VESA xserver support"] + ["xserver_i915", "i915 xserver support"] + + $ yocto-bsp list arm property base_kbranch_linux_yocto_3_0 + Getting branches from remote repo git://git.yoctoproject.org/linux-yocto-3.0... + ["yocto/base", "yocto/base"] + ["yocto/eg20t", "yocto/eg20t"] + ["yocto/gma500", "yocto/gma500"] + ["yocto/pvr", "yocto/pvr"] + ["yocto/standard/arm-versatile-926ejs", "yocto/standard/arm-versatile-926ejs"] + ["yocto/standard/base", "yocto/standard/base"] + ["yocto/standard/cedartrail", "yocto/standard/cedartrail"] + . + . + . + ["yocto/standard/qemu-ppc32", "yocto/standard/qemu-ppc32"] + ["yocto/standard/routerstationpro", "yocto/standard/routerstationpro"] + + The third form as well is meant mainly for developers of + alternative interfaces - it allows the developer to fetch the + possible values for a given input element on-demand. This + on-demand capability is especially valuable for elements that + require relatively expensive remote operations to fulfill, such as + the example that returns the set of branches available in a remote + git tree above. + +""" + +## +# yocto-kernel help and usage strings +## + +yocto_kernel_usage = """ + + Modify and list Yocto BSP kernel config items and patches. + + usage: yocto-kernel [--version] [--help] COMMAND [ARGS] + + Current 'yocto-kernel' commands are: + config list List the modifiable set of bare kernel config options for a BSP + config add Add or modify bare kernel config options for a BSP + config rm Remove bare kernel config options from a BSP + patch list List the patches associated with a BSP + patch add Patch the Yocto kernel for a BSP + patch rm Remove patches from a BSP + feature list List the features used by a BSP + feature add Have a BSP use a feature + feature rm Have a BSP stop using a feature + features list List the features available to BSPs + feature describe Describe a particular feature + feature create Create a new BSP-local feature + feature destroy Remove a BSP-local feature + + See 'yocto-kernel help COMMAND' for more information on a specific command. + +""" + + +yocto_kernel_help_usage = """ + + usage: yocto-kernel help + + This command displays detailed help for the specified subcommand. +""" + +yocto_kernel_config_list_usage = """ + + List the modifiable set of bare kernel config options for a BSP + + usage: yocto-kernel config list + + This command lists the 'modifiable' config items for a BSP i.e. the + items which are eligible for modification or removal by other + yocto-kernel commands. + + 'modifiable' config items are the config items contained a BSP's + user-config.cfg base config. +""" + + +yocto_kernel_config_list_help = """ + +NAME + yocto-kernel config list - List the modifiable set of bare kernel + config options for a BSP + +SYNOPSIS + yocto-kernel config list + +DESCRIPTION + This command lists the 'modifiable' config items for a BSP + i.e. the items which are eligible for modification or removal by + other yocto-kernel commands. +""" + + +yocto_kernel_config_add_usage = """ + + Add or modify bare kernel config options for a BSP + + usage: yocto-kernel config add [ ...] + + This command adds one or more CONFIG_XXX=x items to a BSP's user-config.cfg + base config. +""" + + +yocto_kernel_config_add_help = """ + +NAME + yocto-kernel config add - Add or modify bare kernel config options + for a BSP + +SYNOPSIS + yocto-kernel config add [ ...] + +DESCRIPTION + This command adds one or more CONFIG_XXX=x items to a BSP's + foo.cfg base config. + + NOTE: It's up to the user to determine whether or not the config + options being added make sense or not - this command does no + sanity checking or verification of any kind to ensure that a + config option really makes sense and will actually be set in in + the final config. For example, if a config option depends on + other config options, it will be turned off by kconfig if the + other options aren't set correctly. +""" + + +yocto_kernel_config_rm_usage = """ + + Remove bare kernel config options from a BSP + + usage: yocto-kernel config rm + + This command removes (turns off) one or more CONFIG_XXX items from a + BSP's user-config.cfg base config. + + The set of config items available to be removed by this command for a + BSP is listed and the user prompted for the specific items to remove. +""" + + +yocto_kernel_config_rm_help = """ + +NAME + yocto-kernel config rm - Remove bare kernel config options from a + BSP + +SYNOPSIS + yocto-kernel config rm + +DESCRIPTION + This command removes (turns off) one or more CONFIG_XXX items from a + BSP's user-config.cfg base config. + + The set of config items available to be removed by this command + for a BSP is listed and the user prompted for the specific items + to remove. +""" + + +yocto_kernel_patch_list_usage = """ + + List the patches associated with the kernel for a BSP + + usage: yocto-kernel patch list + + This command lists the patches associated with a BSP. + + NOTE: this only applies to patches listed in the kernel recipe's + user-patches.scc file (and currently repeated in its SRC_URI). +""" + + +yocto_kernel_patch_list_help = """ + +NAME + yocto-kernel patch list - List the patches associated with the kernel + for a BSP + +SYNOPSIS + yocto-kernel patch list + +DESCRIPTION + This command lists the patches associated with a BSP. + + NOTE: this only applies to patches listed in the kernel recipe's + user-patches.scc file (and currently repeated in its SRC_URI). +""" + + +yocto_kernel_patch_add_usage = """ + + Patch the Yocto kernel for a specific BSP + + usage: yocto-kernel patch add [ ...] + + This command adds one or more patches to a BSP's machine branch. The + patch will be added to the BSP's linux-yocto kernel user-patches.scc + file (and currently repeated in its SRC_URI) and will be guaranteed + to be applied in the order specified. +""" + + +yocto_kernel_patch_add_help = """ + +NAME + yocto-kernel patch add - Patch the Yocto kernel for a specific BSP + +SYNOPSIS + yocto-kernel patch add [ ...] + +DESCRIPTION + This command adds one or more patches to a BSP's machine branch. + The patch will be added to the BSP's linux-yocto kernel + user-patches.scc file (and currently repeated in its SRC_URI) and + will be guaranteed to be applied in the order specified. + + NOTE: It's up to the user to determine whether or not the patches + being added makes sense or not - this command does no sanity + checking or verification of any kind to ensure that a patch can + actually be applied to the BSP's kernel branch; it's assumed that + the user has already done that. +""" + + +yocto_kernel_patch_rm_usage = """ + + Remove a patch from the Yocto kernel for a specific BSP + + usage: yocto-kernel patch rm + + This command removes one or more patches from a BSP's machine branch. + The patch will be removed from the BSP's linux-yocto kernel + user-patches.scc file (and currently repeated in its SRC_URI) and + kernel SRC_URI dir. + + The set of patches available to be removed by this command for a BSP + is listed and the user prompted for the specific patches to remove. +""" + + +yocto_kernel_patch_rm_help = """ + +NAME + yocto-kernel patch rm - Remove a patch from the Yocto kernel for a specific BSP + +SYNOPSIS + yocto-kernel patch rm + +DESCRIPTION + This command removes one or more patches from a BSP's machine + branch. The patch will be removed from the BSP's linux-yocto + kernel user-patches.scc file (and currently repeated in its + SRC_URI). + + The set of patches available to be removed by this command for a + BSP is listed and the user prompted for the specific patches to + remove. +""" + +yocto_kernel_feature_list_usage = """ + + List the BSP features that are being used by a BSP + + usage: yocto-kernel feature list + + This command lists the features being used by a BSP i.e. the features + which are eligible for modification or removal by other yocto-kernel + commands. + + 'modifiable' features are the features listed in a BSP's + user-features.scc file. +""" + + +yocto_kernel_feature_list_help = """ + +NAME + yocto-kernel feature list - List the modifiable set of features + being used by a BSP + +SYNOPSIS + yocto-kernel feature list + +DESCRIPTION + This command lists the 'modifiable' features being used by a BSP + i.e. the features which are eligible for modification or removal + by other yocto-kernel commands. +""" + + +yocto_kernel_feature_add_usage = """ + + Add to or modify the list of features being used for a BSP + + usage: yocto-kernel feature add [/xxxx/yyyy/feature.scc ...] + + This command adds one or more feature items to a BSP's kernel + user-features.scc file, which is the file used to manage features in + a yocto-bsp-generated BSP. Features to be added must be specified as + fully-qualified feature names. +""" + + +yocto_kernel_feature_add_help = """ + +NAME + yocto-kernel feature add - Add to or modify the list of features + being used for a BSP + +SYNOPSIS + yocto-kernel feature add [/xxxx/yyyy/feature.scc ...] + +DESCRIPTION + This command adds one or more feature items to a BSP's + user-features.scc file, which is the file used to manage features + in a yocto-bsp-generated BSP. Features to be added must be + specified as fully-qualified feature names. +""" + + +yocto_kernel_feature_rm_usage = """ + + Remove a feature from the list of features being used for a BSP + + usage: yocto-kernel feature rm + + This command removes (turns off) one or more features from a BSP's + user-features.scc file, which is the file used to manage features in + a yocto-bsp-generated BSP. + + The set of features available to be removed by this command for a BSP + is listed and the user prompted for the specific items to remove. +""" + + +yocto_kernel_feature_rm_help = """ + +NAME + yocto-kernel feature rm - Remove a feature from the list of + features being used for a BSP + +SYNOPSIS + yocto-kernel feature rm + +DESCRIPTION + This command removes (turns off) one or more features from a BSP's + user-features.scc file, which is the file used to manage features + in a yocto-bsp-generated BSP. + + The set of features available to be removed by this command for a + BSP is listed and the user prompted for the specific items to + remove. +""" + + +yocto_kernel_available_features_list_usage = """ + + List the set of kernel features available to a BSP + + usage: yocto-kernel features list + + This command lists the complete set of kernel features available to a + BSP. This includes the features contained in linux-yocto meta + branches as well as recipe-space features defined locally to the BSP. +""" + + +yocto_kernel_available_features_list_help = """ + +NAME + yocto-kernel features list - List the set of kernel features + available to a BSP + +SYNOPSIS + yocto-kernel features list + +DESCRIPTION + This command lists the complete set of kernel features available + to a BSP. This includes the features contained in linux-yocto + meta branches as well as recipe-space features defined locally to + the BSP. +""" + + +yocto_kernel_feature_describe_usage = """ + + Print the description and compatibility information for a given kernel feature + + usage: yocto-kernel feature describe [/xxxx/yyyy/feature.scc ...] + + This command prints the description and compatibility of a specific + feature in the format 'description [compatibility]. +""" + + +yocto_kernel_feature_describe_help = """ + +NAME + yocto-kernel feature describe - print the description and + compatibility information for a given kernel feature + +SYNOPSIS + yocto-kernel feature describe [/xxxx/yyyy/feature.scc ...] + +DESCRIPTION + This command prints the description and compatibility of a + specific feature in the format 'description [compatibility]. If + the feature doesn't define a description or compatibility, a + string with generic unknown values will be printed. +""" + + +yocto_kernel_feature_create_usage = """ + + Create a recipe-space kernel feature in a BSP + + usage: yocto-kernel feature create newfeature.scc \ + "Feature Description" capabilities [ ...] [ ...] + + This command creates a new kernel feature from the bare config + options and patches specified on the command-line. +""" + + +yocto_kernel_feature_create_help = """ + +NAME + yocto-kernel feature create - create a recipe-space kernel feature + in a BSP + +SYNOPSIS + yocto-kernel feature create newfeature.scc \ + "Feature Description" capabilities [ ...] [ ...] + +DESCRIPTION + This command creates a new kernel feature from the bare config + options and patches specified on the command-line. The new + feature will be created in recipe-space, specifically in either + the kernel .bbappend's /files/cfg or /files/features subdirectory, + depending on whether or not the feature contains config items only + or config items along with patches. The named feature must end + with .scc and must not contain a feature directory to contain the + feature (this will be determined automatically), and a feature + decription in double-quotes along with a capabilities string + (which for the time being can be one of: 'all' or 'board'). +""" + + +yocto_kernel_feature_destroy_usage = """ + + Destroy a recipe-space kernel feature in a BSP + + usage: yocto-kernel feature destroy feature.scc + + This command destroys a kernel feature defined in the specified BSP's + recipe-space kernel definition. +""" + + +yocto_kernel_feature_destroy_help = """ + +NAME + yocto-kernel feature destroy feature.scc - destroy a + recipe-space kernel feature in a BSP + +SYNOPSIS + yocto-kernel feature destroy feature.scc + +DESCRIPTION + This command destroys a kernel feature defined in the specified + BSP's recipe-space kernel definition. The named feature must end + with .scc and must not contain a feature directory to contain the + feature (this will be determined automatically). If the kernel + feature is in use by a BSP, it can't be removed until the BSP + stops using it (see yocto-kernel feature rm to stop using it). +""" + +## +# yocto-layer help and usage strings +## + +yocto_layer_usage = """ + + Create a generic Yocto layer. + + usage: yocto-layer [--version] [--help] COMMAND [ARGS] + + Current 'yocto-layer' commands are: + create Create a new generic Yocto layer + list List available values for input options and properties + + See 'yocto-layer help COMMAND' for more information on a specific command. +""" + +yocto_layer_help_usage = """ + + usage: yocto-layer help + + This command displays detailed help for the specified subcommand. +""" + +yocto_layer_create_usage = """ + + Create a new generic Yocto layer + + usage: yocto-layer create [layer_priority] + [-o | --outdir ] + [-i | --infile ] + + This command creates a generic Yocto layer based on the specified + parameters. The new layer will be a new Yocto layer contained by + default within the top-level directory specified as + 'meta-layer-name'. The -o option can be used to place the layer in a + directory with a different name and location. + + If layer_priority is specified, a simple layer will be created using + the given layer priority, and the user will not be prompted for + further input. + + NOTE: Once created, you should add your new layer to your + bblayers.conf file in order for it to be subsequently seen and + modified by the yocto-kernel tool. Instructions for doing this can + be found in the README file generated in the layer's top-level + directory. + + See 'yocto layer help create' for more detailed instructions. +""" + +yocto_layer_create_help = """ + +NAME + yocto-layer create - Create a new generic Yocto layer + +SYNOPSIS + yocto-layer create [layer_priority] + [-o | --outdir ] + [-i | --infile ] + +DESCRIPTION + This command creates a generic Yocto layer based on the specified + parameters. The new layer will be a new Yocto layer contained by + default within the top-level directory specified as + 'meta-layer-name'. The -o option can be used to place the layer + in a directory with a different name and location. + + If layer_priority is specified, a simple layer will be created + using the given layer priority, and the user will not be prompted + for further input. + + The layer-specific properties that define the values that will be + used to generate the layer can be specified on the command-line + using the -i option and supplying a JSON object consisting of the + set of name:value pairs needed by the layer. + + If the -i option is not used, the user will be interactively + prompted for each of the required property values, which will then + be used as values for layer generation. + + The set of properties available can be listed using the + 'yocto-layer list' command. + + Specifying -c causes the Python code generated and executed to + create the layer to be dumped to the 'bspgen.out' file in the + current directory, and is useful for debugging. + + NOTE: Once created, you should add your new layer to your + bblayers.conf file in order for it to be subsequently seen and + modified by the yocto-kernel tool. Instructions for doing this + can be found in the README file generated in the layer's top-level + directory. + + For example, assuming your poky repo is at /path/to/poky, your new + layer is at /path/to/poky/meta-mylayer, and your build directory + is /path/to/build: + + $ gedit /path/to/build/conf/bblayers.conf + + BBLAYERS ?= " \\ + /path/to/poky/meta \\ + /path/to/poky/meta-yocto \\ + /path/to/poky/meta-mylayer \\ + " +""" + +yocto_layer_list_usage = """ + + usage: yocto-layer list properties + [-o | --outfile ] + yocto-layer list property + [-o | --outfile ] + + This command enumerates the complete set of possible values for a + specified option or property needed by the layer creation process. + + The first form enumerates all the possible properties that exist and + must have values specified for them in the 'yocto-layer create' + command. + + The second form enumerates all the possible values that exist and can + be specified for any of the enumerable properties in the 'yocto-layer + create' command. + + See 'yocto-layer help list' for more details. +""" + +yocto_layer_list_help = """ + +NAME + yocto-layer list - List available values for layer input options and properties + +SYNOPSIS + yocto-layer list properties + [--o | -outfile ] + yocto-layer list property + [--o | -outfile ] + +DESCRIPTION + This command enumerates the complete set of possible values for a + specified option or property needed by the layer creation process. + + The first form enumerates all the possible properties that exist + and must have values specified for them in the 'yocto-layer + create' command. This command is mainly meant to aid the + development of user interface alternatives to the default + text-based prompting interface. If the -o option is specified, + the list of properties, in addition to being displayed, will be + written to the specified file as a JSON object. In this case, the + object will consist of the set of name:value pairs corresponding + to the (possibly nested) dictionary of properties defined by the + input statements used by the BSP. Some example output for the + 'list properties' command: + + $ yocto-layer list properties + "example_bbappend_name" : { + "default" : example + "msg" : Please enter the name you'd like to use for your bbappend file: + "type" : edit + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + "create_example_recipe" : { + "default" : n + "msg" : Would you like to have an example recipe created? (y/n) + "type" : boolean + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + "example_recipe_name" : { + "default" : example + "msg" : Please enter the name you'd like to use for your example recipe: + "type" : edit + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + "layer_priority" : { + "default" : 6 + "msg" : Please enter the layer priority you'd like to use for the layer: + "type" : edit + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + "create_example_bbappend" : { + "default" : n + "msg" : Would you like to have an example bbappend file created? (y/n) + "type" : boolean + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + "example_bbappend_version" : { + "default" : 0.1 + "msg" : Please enter the version number you'd like to use for your bbappend file (this should match the recipe you're appending to): + "type" : edit + "prio" : 20 + "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall + } + + Each entry in the output consists of the name of the input element + e.g. "layer_priority", followed by the properties defined for that + element enclosed in braces. This information should provide + sufficient information to create a complete user interface. Two + features of the scheme provide for conditional input. First, if a + Python "if" statement appears in place of an input element name, + the set of enclosed input elements apply and should be presented + to the user only if the 'if' statement evaluates to true. The + test in the if statement will always reference another input + element in the list, which means that the element being tested + should be presented to the user before the elements enclosed by + the if block. Secondly, in a similar way, some elements contain + "depends-on" and depends-on-val" tags, which mean that the + affected input element should only be presented to the user if the + element it depends on has already been presented to the user and + the user has selected the specified value for that element. + + The second form enumerates all the possible values that exist and + can be specified for any of the enumerable properties in the + 'yocto-layer create' command. If the -o option is specified, the + list of values for the given property, in addition to being + displayed, will be written to the specified file as a JSON object. + In this case, the object will consist of the set of name:value + pairs corresponding to the array of property values associated + with the property. + + $ yocto-layer list property layer_priority + [no output - layer_priority is a text field that has no enumerable values] + + The second form as well is meant mainly for developers of + alternative interfaces - it allows the developer to fetch the + possible values for a given input element on-demand. This + on-demand capability is especially valuable for elements that + require relatively expensive remote operations to fulfill, such as + the example that returns the set of branches available in a remote + git tree above. + +""" + +## +# test code +## + +test_bsp_properties = { + 'smp': 'yes', + 'touchscreen': 'yes', + 'keyboard': 'no', + 'xserver': 'yes', + 'xserver_choice': 'xserver-i915', + 'features': ['goodfeature', 'greatfeature'], + 'tunefile': 'tune-quark', +} + diff --git a/scripts/lib/bsp/kernel.py b/scripts/lib/bsp/kernel.py new file mode 100644 index 0000000000..ba68b60fcb --- /dev/null +++ b/scripts/lib/bsp/kernel.py @@ -0,0 +1,1071 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2012, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module implements the kernel-related functions used by +# 'yocto-kernel' to manage kernel config items and patches for Yocto +# BSPs. +# +# AUTHORS +# Tom Zanussi +# + +import sys +import os +import shutil +from tags import * +import glob +import subprocess +from engine import create_context + + +def find_bblayers(): + """ + Find and return a sanitized list of the layers found in BBLAYERS. + """ + try: + builddir = os.environ["BUILDDIR"] + except KeyError: + print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" + sys.exit(1) + bblayers_conf = os.path.join(builddir, "conf/bblayers.conf") + + layers = [] + + bitbake_env_cmd = "bitbake -e" + bitbake_env_lines = subprocess.Popen(bitbake_env_cmd, shell=True, + stdout=subprocess.PIPE).stdout.read() + + if not bitbake_env_lines: + print "Couldn't get '%s' output, exiting." % bitbake_env_cmd + sys.exit(1) + + for line in bitbake_env_lines.split('\n'): + bblayers = get_line_val(line, "BBLAYERS") + if (bblayers): + break + + if not bblayers: + print "Couldn't find BBLAYERS in %s output, exiting." % \ + bitbake_env_cmd + sys.exit(1) + + raw_layers = bblayers.split() + + for layer in raw_layers: + if layer == 'BBLAYERS' or '=' in layer: + continue + layers.append(layer) + + return layers + + +def get_line_val(line, key): + """ + Extract the value from the VAR="val" string + """ + if line.startswith(key + "="): + stripped_line = line.split('=')[1] + stripped_line = stripped_line.replace('\"', '') + return stripped_line + return None + + +def find_meta_layer(): + """ + Find and return the meta layer in BBLAYERS. + """ + layers = find_bblayers() + + for layer in layers: + if layer.endswith("meta"): + return layer + + return None + + +def find_bsp_layer(machine): + """ + Find and return a machine's BSP layer in BBLAYERS. + """ + layers = find_bblayers() + + for layer in layers: + if layer.endswith(machine): + return layer + + print "Unable to find the BSP layer for machine %s." % machine + print "Please make sure it is listed in bblayers.conf" + sys.exit(1) + + +def gen_choices_str(choices): + """ + Generate a numbered list of choices from a list of choices for + display to the user. + """ + choices_str = "" + + for i, choice in enumerate(choices): + choices_str += "\t" + str(i + 1) + ") " + choice + "\n" + + return choices_str + + +def open_user_file(scripts_path, machine, userfile, mode): + """ + Find one of the user files (user-config.cfg, user-patches.scc) + associated with the machine (could be in files/, + linux-yocto-custom/, etc). Returns the open file if found, None + otherwise. + + The caller is responsible for closing the file returned. + """ + layer = find_bsp_layer(machine) + linuxdir = os.path.join(layer, "recipes-kernel/linux") + linuxdir_list = os.listdir(linuxdir) + for fileobj in linuxdir_list: + fileobj_path = os.path.join(linuxdir, fileobj) + if os.path.isdir(fileobj_path): + userfile_name = os.path.join(fileobj_path, userfile) + try: + f = open(userfile_name, mode) + return f + except IOError: + continue + return None + + +def read_config_items(scripts_path, machine): + """ + Find and return a list of config items (CONFIG_XXX) in a machine's + user-defined config fragment [${machine}-user-config.cfg]. + """ + config_items = [] + + f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "r") + lines = f.readlines() + for line in lines: + s = line.strip() + if s and not s.startswith("#"): + config_items.append(s) + f.close() + + return config_items + + +def write_config_items(scripts_path, machine, config_items): + """ + Write (replace) the list of config items (CONFIG_XXX) in a + machine's user-defined config fragment [${machine}=user-config.cfg]. + """ + f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "w") + for item in config_items: + f.write(item + "\n") + f.close() + + kernel_contents_changed(scripts_path, machine) + + +def yocto_kernel_config_list(scripts_path, machine): + """ + Display the list of config items (CONFIG_XXX) in a machine's + user-defined config fragment [${machine}-user-config.cfg]. + """ + config_items = read_config_items(scripts_path, machine) + + print "The current set of machine-specific kernel config items for %s is:" % machine + print gen_choices_str(config_items) + + +def yocto_kernel_config_rm(scripts_path, machine): + """ + Display the list of config items (CONFIG_XXX) in a machine's + user-defined config fragment [${machine}-user-config.cfg], prompt the user + for one or more to remove, and remove them. + """ + config_items = read_config_items(scripts_path, machine) + + print "Specify the kernel config items to remove:" + input = raw_input(gen_choices_str(config_items)) + rm_choices = input.split() + rm_choices.sort() + + removed = [] + + for choice in reversed(rm_choices): + try: + idx = int(choice) - 1 + except ValueError: + print "Invalid choice (%s), exiting" % choice + sys.exit(1) + if idx < 0 or idx >= len(config_items): + print "Invalid choice (%d), exiting" % (idx + 1) + sys.exit(1) + removed.append(config_items.pop(idx)) + + write_config_items(scripts_path, machine, config_items) + + print "Removed items:" + for r in removed: + print "\t%s" % r + + +def yocto_kernel_config_add(scripts_path, machine, config_items): + """ + Add one or more config items (CONFIG_XXX) to a machine's + user-defined config fragment [${machine}-user-config.cfg]. + """ + new_items = [] + dup_items = [] + + cur_items = read_config_items(scripts_path, machine) + + for item in config_items: + if not item.startswith("CONFIG") or (not "=y" in item and not "=m" in item): + print "Invalid config item (%s), exiting" % item + sys.exit(1) + if item not in cur_items and item not in new_items: + new_items.append(item) + else: + dup_items.append(item) + + if len(new_items) > 0: + cur_items.extend(new_items) + write_config_items(scripts_path, machine, cur_items) + print "Added item%s:" % ("" if len(new_items)==1 else "s") + for n in new_items: + print "\t%s" % n + + if len(dup_items) > 0: + output="The following item%s already exist%s in the current configuration, ignoring %s:" % \ + (("","s", "it") if len(dup_items)==1 else ("s", "", "them" )) + print output + for n in dup_items: + print "\t%s" % n + +def find_current_kernel(bsp_layer, machine): + """ + Determine the kernel and version currently being used in the BSP. + """ + machine_conf = os.path.join(bsp_layer, "conf/machine/" + machine + ".conf") + + preferred_kernel = preferred_kernel_version = preferred_version_varname = None + + f = open(machine_conf, "r") + lines = f.readlines() + for line in lines: + if line.strip().startswith("PREFERRED_PROVIDER_virtual/kernel"): + preferred_kernel = line.split()[-1] + preferred_kernel = preferred_kernel.replace('\"','') + preferred_version_varname = "PREFERRED_VERSION_" + preferred_kernel + if preferred_version_varname and line.strip().startswith(preferred_version_varname): + preferred_kernel_version = line.split()[-1] + preferred_kernel_version = preferred_kernel_version.replace('\"','') + preferred_kernel_version = preferred_kernel_version.replace('%','') + + if preferred_kernel and preferred_kernel_version: + return preferred_kernel + "_" + preferred_kernel_version + elif preferred_kernel: + return preferred_kernel + + +def find_filesdir(scripts_path, machine): + """ + Find the name of the 'files' dir associated with the machine + (could be in files/, linux-yocto-custom/, etc). Returns the name + of the files dir if found, None otherwise. + """ + layer = find_bsp_layer(machine) + filesdir = None + linuxdir = os.path.join(layer, "recipes-kernel/linux") + linuxdir_list = os.listdir(linuxdir) + for fileobj in linuxdir_list: + fileobj_path = os.path.join(linuxdir, fileobj) + if os.path.isdir(fileobj_path): + # this could be files/ or linux-yocto-custom/, we have no way of distinguishing + # so we take the first (and normally only) dir we find as the 'filesdir' + filesdir = fileobj_path + + return filesdir + + +def read_patch_items(scripts_path, machine): + """ + Find and return a list of patch items in a machine's user-defined + patch list [${machine}-user-patches.scc]. + """ + patch_items = [] + + f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "r") + lines = f.readlines() + for line in lines: + s = line.strip() + if s and not s.startswith("#"): + fields = s.split() + if not fields[0] == "patch": + continue + patch_items.append(fields[1]) + f.close() + + return patch_items + + +def write_patch_items(scripts_path, machine, patch_items): + """ + Write (replace) the list of patches in a machine's user-defined + patch list [${machine}-user-patches.scc]. + """ + f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "w") + for item in patch_items: + f.write("patch " + item + "\n") + f.close() + + kernel_contents_changed(scripts_path, machine) + + +def yocto_kernel_patch_list(scripts_path, machine): + """ + Display the list of patches in a machine's user-defined patch list + [${machine}-user-patches.scc]. + """ + patches = read_patch_items(scripts_path, machine) + + print "The current set of machine-specific patches for %s is:" % machine + print gen_choices_str(patches) + + +def yocto_kernel_patch_rm(scripts_path, machine): + """ + Remove one or more patches from a machine's user-defined patch + list [${machine}-user-patches.scc]. + """ + patches = read_patch_items(scripts_path, machine) + + print "Specify the patches to remove:" + input = raw_input(gen_choices_str(patches)) + rm_choices = input.split() + rm_choices.sort() + + removed = [] + + filesdir = find_filesdir(scripts_path, machine) + if not filesdir: + print "Couldn't rm patch(es) since we couldn't find a 'files' dir" + sys.exit(1) + + for choice in reversed(rm_choices): + try: + idx = int(choice) - 1 + except ValueError: + print "Invalid choice (%s), exiting" % choice + sys.exit(1) + if idx < 0 or idx >= len(patches): + print "Invalid choice (%d), exiting" % (idx + 1) + sys.exit(1) + filesdir_patch = os.path.join(filesdir, patches[idx]) + if os.path.isfile(filesdir_patch): + os.remove(filesdir_patch) + removed.append(patches[idx]) + patches.pop(idx) + + write_patch_items(scripts_path, machine, patches) + + print "Removed patches:" + for r in removed: + print "\t%s" % r + + +def yocto_kernel_patch_add(scripts_path, machine, patches): + """ + Add one or more patches to a machine's user-defined patch list + [${machine}-user-patches.scc]. + """ + existing_patches = read_patch_items(scripts_path, machine) + + for patch in patches: + if os.path.basename(patch) in existing_patches: + print "Couldn't add patch (%s) since it's already been added" % os.path.basename(patch) + sys.exit(1) + + filesdir = find_filesdir(scripts_path, machine) + if not filesdir: + print "Couldn't add patch (%s) since we couldn't find a 'files' dir to add it to" % os.path.basename(patch) + sys.exit(1) + + new_patches = [] + + for patch in patches: + if not os.path.isfile(patch): + print "Couldn't find patch (%s), exiting" % patch + sys.exit(1) + basename = os.path.basename(patch) + filesdir_patch = os.path.join(filesdir, basename) + shutil.copyfile(patch, filesdir_patch) + new_patches.append(basename) + + cur_items = read_patch_items(scripts_path, machine) + cur_items.extend(new_patches) + write_patch_items(scripts_path, machine, cur_items) + + print "Added patches:" + for n in new_patches: + print "\t%s" % n + + +def inc_pr(line): + """ + Add 1 to the PR value in the given bbappend PR line. For the PR + lines in kernel .bbappends after modifications. Handles PRs of + the form PR := "${PR}.1" as well as PR = "r0". + """ + idx = line.find("\"") + + pr_str = line[idx:] + pr_str = pr_str.replace('\"','') + fields = pr_str.split('.') + if len(fields) > 1: + fields[1] = str(int(fields[1]) + 1) + pr_str = "\"" + '.'.join(fields) + "\"\n" + else: + pr_val = pr_str[1:] + pr_str = "\"" + "r" + str(int(pr_val) + 1) + "\"\n" + idx2 = line.find("\"", idx + 1) + line = line[:idx] + pr_str + + return line + + +def kernel_contents_changed(scripts_path, machine): + """ + Do what we need to do to notify the system that the kernel + recipe's contents have changed. + """ + layer = find_bsp_layer(machine) + + kernel = find_current_kernel(layer, machine) + if not kernel: + print "Couldn't determine the kernel for this BSP, exiting." + sys.exit(1) + + kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend") + if not os.path.isfile(kernel_bbfile): + kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bb") + if not os.path.isfile(kernel_bbfile): + return + kernel_bbfile_prev = kernel_bbfile + ".prev" + shutil.copyfile(kernel_bbfile, kernel_bbfile_prev) + + ifile = open(kernel_bbfile_prev, "r") + ofile = open(kernel_bbfile, "w") + ifile_lines = ifile.readlines() + for ifile_line in ifile_lines: + if ifile_line.strip().startswith("PR"): + ifile_line = inc_pr(ifile_line) + ofile.write(ifile_line) + ofile.close() + ifile.close() + + +def kernels(context): + """ + Return the list of available kernels in the BSP i.e. corresponding + to the kernel .bbappends found in the layer. + """ + archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"]) + kerndir = os.path.join(archdir, "recipes-kernel/linux") + bbglob = os.path.join(kerndir, "*.bbappend") + + bbappends = glob.glob(bbglob) + + kernels = [] + + for kernel in bbappends: + filename = os.path.splitext(os.path.basename(kernel))[0] + idx = filename.find(CLOSE_TAG) + if idx != -1: + filename = filename[idx + len(CLOSE_TAG):].strip() + kernels.append(filename) + + kernels.append("custom") + + return kernels + + +def extract_giturl(file): + """ + Extract the git url of the kernel repo from the kernel recipe's + SRC_URI. + """ + url = None + f = open(file, "r") + lines = f.readlines() + for line in lines: + line = line.strip() + if line.startswith("SRC_URI"): + line = line[len("SRC_URI"):].strip() + if line.startswith("="): + line = line[1:].strip() + if line.startswith("\""): + line = line[1:].strip() + prot = "git" + for s in line.split(";"): + if s.startswith("git://"): + url = s + if s.startswith("protocol="): + prot = s.split("=")[1] + if url: + url = prot + url[3:] + return url + + +def find_giturl(context): + """ + Find the git url of the kernel repo from the kernel recipe's + SRC_URI. + """ + name = context["name"] + filebase = context["filename"] + scripts_path = context["scripts_path"] + + meta_layer = find_meta_layer() + + kerndir = os.path.join(meta_layer, "recipes-kernel/linux") + bbglob = os.path.join(kerndir, "*.bb") + bbs = glob.glob(bbglob) + for kernel in bbs: + filename = os.path.splitext(os.path.basename(kernel))[0] + if filename in filebase: + giturl = extract_giturl(kernel) + return giturl + + return None + + +def read_features(scripts_path, machine): + """ + Find and return a list of features in a machine's user-defined + features fragment [${machine}-user-features.scc]. + """ + features = [] + + f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "r") + lines = f.readlines() + for line in lines: + s = line.strip() + if s and not s.startswith("#"): + feature_include = s.split() + features.append(feature_include[1].strip()) + f.close() + + return features + + +def write_features(scripts_path, machine, features): + """ + Write (replace) the list of feature items in a + machine's user-defined features fragment [${machine}=user-features.cfg]. + """ + f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "w") + for item in features: + f.write("include " + item + "\n") + f.close() + + kernel_contents_changed(scripts_path, machine) + + +def yocto_kernel_feature_list(scripts_path, machine): + """ + Display the list of features used in a machine's user-defined + features fragment [${machine}-user-features.scc]. + """ + features = read_features(scripts_path, machine) + + print "The current set of machine-specific features for %s is:" % machine + print gen_choices_str(features) + + +def yocto_kernel_feature_rm(scripts_path, machine): + """ + Display the list of features used in a machine's user-defined + features fragment [${machine}-user-features.scc], prompt the user + for one or more to remove, and remove them. + """ + features = read_features(scripts_path, machine) + + print "Specify the features to remove:" + input = raw_input(gen_choices_str(features)) + rm_choices = input.split() + rm_choices.sort() + + removed = [] + + for choice in reversed(rm_choices): + try: + idx = int(choice) - 1 + except ValueError: + print "Invalid choice (%s), exiting" % choice + sys.exit(1) + if idx < 0 or idx >= len(features): + print "Invalid choice (%d), exiting" % (idx + 1) + sys.exit(1) + removed.append(features.pop(idx)) + + write_features(scripts_path, machine, features) + + print "Removed features:" + for r in removed: + print "\t%s" % r + + +def yocto_kernel_feature_add(scripts_path, machine, features): + """ + Add one or more features a machine's user-defined features + fragment [${machine}-user-features.scc]. + """ + new_items = [] + + for item in features: + if not item.endswith(".scc"): + print "Invalid feature (%s), exiting" % item + sys.exit(1) + new_items.append(item) + + cur_items = read_features(scripts_path, machine) + cur_items.extend(new_items) + + write_features(scripts_path, machine, cur_items) + + print "Added features:" + for n in new_items: + print "\t%s" % n + + +def find_feature_url(git_url): + """ + Find the url of the kern-features.rc kernel for the kernel repo + specified from the BSP's kernel recipe SRC_URI. + """ + feature_url = "" + if git_url.startswith("git://"): + git_url = git_url[len("git://"):].strip() + s = git_url.split("/") + if s[1].endswith(".git"): + s[1] = s[1][:len(s[1]) - len(".git")] + feature_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ + "/plain/meta/cfg/kern-features.rc?h=meta" + + return feature_url + + +def find_feature_desc(lines): + """ + Find the feature description and compatibility in the passed-in + set of lines. Returns a string string of the form 'desc + [compat]'. + """ + desc = "no description available" + compat = "unknown" + + for line in lines: + idx = line.find("KFEATURE_DESCRIPTION") + if idx != -1: + desc = line[idx + len("KFEATURE_DESCRIPTION"):].strip() + if desc.startswith("\""): + desc = desc[1:] + if desc.endswith("\""): + desc = desc[:-1] + else: + idx = line.find("KFEATURE_COMPATIBILITY") + if idx != -1: + compat = line[idx + len("KFEATURE_COMPATIBILITY"):].strip() + + return desc + " [" + compat + "]" + + +def print_feature_descs(layer, feature_dir): + """ + Print the feature descriptions for the features in feature_dir. + """ + kernel_files_features = os.path.join(layer, "recipes-kernel/linux/files/" + + feature_dir) + for root, dirs, files in os.walk(kernel_files_features): + for file in files: + if file.endswith("~") or file.endswith("#"): + continue + if file.endswith(".scc"): + fullpath = os.path.join(layer, "recipes-kernel/linux/files/" + + feature_dir + "/" + file) + f = open(fullpath) + feature_desc = find_feature_desc(f.readlines()) + print feature_dir + "/" + file + ": " + feature_desc + + +def yocto_kernel_available_features_list(scripts_path, machine): + """ + Display the list of all the kernel features available for use in + BSPs, as gathered from the set of feature sources. + """ + layer = find_bsp_layer(machine) + kernel = find_current_kernel(layer, machine) + if not kernel: + print "Couldn't determine the kernel for this BSP, exiting." + sys.exit(1) + + context = create_context(machine, "arch", scripts_path) + context["name"] = "name" + context["filename"] = kernel + giturl = find_giturl(context) + feature_url = find_feature_url(giturl) + + feature_cmd = "wget -q -O - " + feature_url + tmp = subprocess.Popen(feature_cmd, shell=True, stdout=subprocess.PIPE).stdout.read() + + print "The current set of kernel features available to %s is:\n" % machine + + if tmp: + tmpline = tmp.split("\n") + in_kernel_options = False + for line in tmpline: + if not "=" in line: + if in_kernel_options: + break + if "kernel-options" in line: + in_kernel_options = True + continue + if in_kernel_options: + feature_def = line.split("=") + feature_type = feature_def[0].strip() + feature = feature_def[1].strip() + desc = get_feature_desc(giturl, feature) + print "%s: %s" % (feature, desc) + + print "[local]" + + print_feature_descs(layer, "cfg") + print_feature_descs(layer, "features") + + +def find_feature_desc_url(git_url, feature): + """ + Find the url of the kernel feature in the kernel repo specified + from the BSP's kernel recipe SRC_URI. + """ + feature_desc_url = "" + if git_url.startswith("git://"): + git_url = git_url[len("git://"):].strip() + s = git_url.split("/") + if s[1].endswith(".git"): + s[1] = s[1][:len(s[1]) - len(".git")] + feature_desc_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ + "/plain/meta/cfg/kernel-cache/" + feature + "?h=meta" + + return feature_desc_url + + +def get_feature_desc(git_url, feature): + """ + Return a feature description of the form 'description [compatibility] + BSPs, as gathered from the set of feature sources. + """ + feature_desc_url = find_feature_desc_url(git_url, feature) + feature_desc_cmd = "wget -q -O - " + feature_desc_url + tmp = subprocess.Popen(feature_desc_cmd, shell=True, stdout=subprocess.PIPE).stdout.read() + + return find_feature_desc(tmp.split("\n")) + + +def yocto_kernel_feature_describe(scripts_path, machine, feature): + """ + Display the description of a specific kernel feature available for + use in a BSP. + """ + layer = find_bsp_layer(machine) + + kernel = find_current_kernel(layer, machine) + if not kernel: + print "Couldn't determine the kernel for this BSP, exiting." + sys.exit(1) + + context = create_context(machine, "arch", scripts_path) + context["name"] = "name" + context["filename"] = kernel + giturl = find_giturl(context) + + desc = get_feature_desc(giturl, feature) + + print desc + + +def check_feature_name(feature_name): + """ + Sanity-check the feature name for create/destroy. Return False if not OK. + """ + if not feature_name.endswith(".scc"): + print "Invalid feature name (must end with .scc) [%s], exiting" % feature_name + return False + + if "/" in feature_name: + print "Invalid feature name (don't specify directory) [%s], exiting" % feature_name + return False + + return True + + +def check_create_input(feature_items): + """ + Sanity-check the create input. Return False if not OK. + """ + if not check_feature_name(feature_items[0]): + return False + + if feature_items[1].endswith(".patch") or feature_items[1].startswith("CONFIG_"): + print "Missing description and/or compatibilty [%s], exiting" % feature_items[1] + return False + + if feature_items[2].endswith(".patch") or feature_items[2].startswith("CONFIG_"): + print "Missing description and/or compatibility [%s], exiting" % feature_items[1] + return False + + return True + + +def yocto_kernel_feature_create(scripts_path, machine, feature_items): + """ + Create a recipe-space kernel feature in a BSP. + """ + if not check_create_input(feature_items): + sys.exit(1) + + feature = feature_items[0] + feature_basename = feature.split(".")[0] + feature_description = feature_items[1] + feature_compat = feature_items[2] + + patches = [] + cfg_items = [] + + for item in feature_items[3:]: + if item.endswith(".patch"): + patches.append(item) + elif item.startswith("CONFIG"): + if ("=y" in item or "=m" in item): + cfg_items.append(item) + else: + print "Invalid feature item (must be .patch or CONFIG_*) [%s], exiting" % item + sys.exit(1) + + feature_dirname = "cfg" + if patches: + feature_dirname = "features" + + filesdir = find_filesdir(scripts_path, machine) + if not filesdir: + print "Couldn't add feature (%s), no 'files' dir found" % feature + sys.exit(1) + + featdir = os.path.join(filesdir, feature_dirname) + if not os.path.exists(featdir): + os.mkdir(featdir) + + for patch in patches: + if not os.path.isfile(patch): + print "Couldn't find patch (%s), exiting" % patch + sys.exit(1) + basename = os.path.basename(patch) + featdir_patch = os.path.join(featdir, basename) + shutil.copyfile(patch, featdir_patch) + + new_cfg_filename = os.path.join(featdir, feature_basename + ".cfg") + new_cfg_file = open(new_cfg_filename, "w") + for cfg_item in cfg_items: + new_cfg_file.write(cfg_item + "\n") + new_cfg_file.close() + + new_feature_filename = os.path.join(featdir, feature_basename + ".scc") + new_feature_file = open(new_feature_filename, "w") + new_feature_file.write("define KFEATURE_DESCRIPTION \"" + feature_description + "\"\n") + new_feature_file.write("define KFEATURE_COMPATIBILITY " + feature_compat + "\n\n") + + for patch in patches: + patch_dir, patch_file = os.path.split(patch) + new_feature_file.write("patch " + patch_file + "\n") + + new_feature_file.write("kconf non-hardware " + feature_basename + ".cfg\n") + new_feature_file.close() + + print "Added feature:" + print "\t%s" % feature_dirname + "/" + feature + + +def feature_in_use(scripts_path, machine, feature): + """ + Determine whether the specified feature is in use by the BSP. + Return True if so, False otherwise. + """ + features = read_features(scripts_path, machine) + for f in features: + if f == feature: + return True + return False + + +def feature_remove(scripts_path, machine, feature): + """ + Remove the specified feature from the available recipe-space + features defined for the BSP. + """ + features = read_features(scripts_path, machine) + new_features = [] + for f in features: + if f == feature: + continue + new_features.append(f) + write_features(scripts_path, machine, new_features) + + +def yocto_kernel_feature_destroy(scripts_path, machine, feature): + """ + Remove a recipe-space kernel feature from a BSP. + """ + if not check_feature_name(feature): + sys.exit(1) + + if feature_in_use(scripts_path, machine, "features/" + feature) or \ + feature_in_use(scripts_path, machine, "cfg/" + feature): + print "Feature %s is in use (use 'feature rm' to un-use it first), exiting" % feature + sys.exit(1) + + filesdir = find_filesdir(scripts_path, machine) + if not filesdir: + print "Couldn't destroy feature (%s), no 'files' dir found" % feature + sys.exit(1) + + feature_dirname = "features" + featdir = os.path.join(filesdir, feature_dirname) + if not os.path.exists(featdir): + print "Couldn't find feature directory (%s)" % feature_dirname + sys.exit(1) + + feature_fqn = os.path.join(featdir, feature) + if not os.path.exists(feature_fqn): + feature_dirname = "cfg" + featdir = os.path.join(filesdir, feature_dirname) + if not os.path.exists(featdir): + print "Couldn't find feature directory (%s)" % feature_dirname + sys.exit(1) + feature_fqn = os.path.join(featdir, feature_filename) + if not os.path.exists(feature_fqn): + print "Couldn't find feature (%s)" % feature + sys.exit(1) + + f = open(feature_fqn, "r") + lines = f.readlines() + for line in lines: + s = line.strip() + if s.startswith("patch ") or s.startswith("kconf "): + split_line = s.split() + filename = os.path.join(featdir, split_line[-1]) + if os.path.exists(filename): + os.remove(filename) + f.close() + os.remove(feature_fqn) + + feature_remove(scripts_path, machine, feature) + + print "Removed feature:" + print "\t%s" % feature_dirname + "/" + feature + + +def base_branches(context): + """ + Return a list of the base branches found in the kernel git repo. + """ + giturl = find_giturl(context) + + print "Getting branches from remote repo %s..." % giturl + + gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) + tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read() + + branches = [] + + if tmp: + tmpline = tmp.split("\n") + for line in tmpline: + if len(line)==0: + break; + if not line.endswith("base"): + continue; + idx = line.find("refs/heads/") + kbranch = line[idx + len("refs/heads/"):] + if kbranch.find("/") == -1 and kbranch.find("base") == -1: + continue + idx = kbranch.find("base") + branches.append(kbranch[:idx - 1]) + + return branches + + +def all_branches(context): + """ + Return a list of all the branches found in the kernel git repo. + """ + giturl = find_giturl(context) + + print "Getting branches from remote repo %s..." % giturl + + gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) + tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read() + + branches = [] + + base_prefixes = None + + try: + branches_base = context["branches_base"] + if branches_base: + base_prefixes = branches_base.split(":") + except KeyError: + pass + + arch = context["arch"] + + if tmp: + tmpline = tmp.split("\n") + for line in tmpline: + if len(line)==0: + break; + idx = line.find("refs/heads/") + kbranch = line[idx + len("refs/heads/"):] + kbranch_prefix = kbranch.rsplit("/", 1)[0] + + if base_prefixes: + for base_prefix in base_prefixes: + if kbranch_prefix == base_prefix: + branches.append(kbranch) + continue + + if (kbranch.find("/") != -1 and + (kbranch.find("standard") != -1 or kbranch.find("base") != -1) or + kbranch == "base"): + branches.append(kbranch) + continue + + return branches diff --git a/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..44a80d226c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf @@ -0,0 +1,105 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +{{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} +{{ if xserver == "y": }} +PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" +XSERVER ?= "xserver-xorg \ + xf86-input-evdev \ + xf86-input-mouse \ + xf86-video-omapfb \ + xf86-input-keyboard" + +# Ship all kernel modules by default +MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" + +# Allow for MMC booting (required by the NAND-less Beagleboard XM) +EXTRA_IMAGEDEPENDS += "u-boot" + +# Uncomment the following line to enable the hard floating point abi. Note that +# this breaks some binary libraries and 3D (neither of which ship with +# meta-yocto). For maximum compatibility, leave this disabled. +#DEFAULTTUNE ?= "cortexa8hf-neon" +{{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_cortexa8" }} +{{ input type:"choice" val:"tune_arm1136jf_s" msg:"arm1136jf-s tuning optimizations" }} +{{ input type:"choice" val:"tune_arm920t" msg:"arm920t tuning optimizations" }} +{{ input type:"choice" val:"tune_arm926ejs" msg:"arm926ejs tuning optimizations" }} +{{ input type:"choice" val:"tune_arm9tdmi" msg:"arm9tdmi tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexa5" msg:"cortexa5 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexa7" msg:"cortexa7 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexa8" msg:"cortexa8 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexa9" msg:"cortexa9 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexa15" msg:"cortexa15 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexm1" msg:"cortexm1 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexm3" msg:"cortexm3 tuning optimizations" }} +{{ input type:"choice" val:"tune_cortexr4" msg:"cortexr4 tuning optimizations" }} +{{ input type:"choice" val:"tune_ep9312" msg:"ep9312 tuning optimizations" }} +{{ input type:"choice" val:"tune_iwmmxt" msg:"iwmmxt tuning optimizations" }} +{{ input type:"choice" val:"tune_strongarm1100" msg:"strongarm1100 tuning optimizations" }} +{{ input type:"choice" val:"tune_xscale" msg:"xscale tuning optimizations" }} +{{ if tunefile == "tune_arm1136jf_s": }} +include conf/machine/include/tune-arm1136jf-s.inc +{{ if tunefile == "tune_arm920t": }} +include conf/machine/include/tune-arm920t.inc +{{ if tunefile == "tune_arm926ejs": }} +include conf/machine/include/tune-arm926ejs.inc +{{ if tunefile == "tune_arm9tdmi": }} +include conf/machine/include/tune-arm9tdmi.inc +{{ if tunefile == "tune_cortexa5": }} +include conf/machine/include/tune-cortexa5.inc +{{ if tunefile == "tune_cortexa7": }} +include conf/machine/include/tune-cortexa7.inc +{{ if tunefile == "tune_cortexa8": }} +include conf/machine/include/tune-cortexa8.inc +{{ if tunefile == "tune_cortexa9": }} +include conf/machine/include/tune-cortexa9.inc +{{ if tunefile == "tune_cortexa15": }} +include conf/machine/include/tune-cortexa15.inc +{{ if tunefile == "tune_cortexm1": }} +include conf/machine/include/tune-cortexm1.inc +{{ if tunefile == "tune_cortexm3": }} +include conf/machine/include/tune-cortexm3.inc +{{ if tunefile == "tune_cortexr4": }} +include conf/machine/include/tune-cortexr4.inc +{{ if tunefile == "tune_ep9312": }} +include conf/machine/include/tune-ep9312.inc +{{ if tunefile == "tune_iwmmxt": }} +include conf/machine/include/tune-iwmmxt.inc +{{ if tunefile == "tune_strongarm1100": }} +include conf/machine/include/tune-strongarm1100.inc +{{ if tunefile == "tune_xscale": }} +include conf/machine/include/tune-xscale.inc + +IMAGE_FSTYPES += "tar.bz2 jffs2" +EXTRA_IMAGECMD_jffs2 = "-lnp " + +# 2.6.37 and later kernels use OMAP_SERIAL, ttyO2 +# earlier kernels use ttyS2 +SERIAL_CONSOLE = "115200 ttyO2" + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +KERNEL_IMAGETYPE = "zImage" +KERNEL_DEVICETREE = "${S}/arch/arm/boot/dts/omap3-beagle.dts ${S}/arch/arm/boot/dts/omap3-beagle-xm.dts" + +SPL_BINARY = "MLO" +UBOOT_SUFFIX = "img" +{{ input type:"edit" name:"uboot_machine" prio:"40" msg:"Please specify a value for UBOOT_MACHINE:" default:"omap3_beagle_config" }} +UBOOT_MACHINE = "{{=uboot_machine}}" +{{ input type:"edit" name:"uboot_entrypoint" prio:"40" msg:"Please specify a value for UBOOT_ENTRYPOINT:" default:"0x80008000" }} +UBOOT_ENTRYPOINT = "{{=uboot_entrypoint}}" +{{ input type:"edit" name:"uboot_loadaddress" prio:"40" msg:"Please specify a value for UBOOT_LOADADDRESS:" default:"0x80008000" }} +UBOOT_LOADADDRESS = "{{=uboot_loadaddress}}" + +MACHINE_FEATURES = "usbgadget usbhost vfat alsa" diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" new file mode 100644 index 0000000000..264f3c91ad --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" @@ -0,0 +1,33 @@ +Section "Module" + Load "extmod" + Load "dbe" + Load "glx" + Load "freetype" + Load "type1" + Load "record" + Load "dri" +EndSection + +Section "Monitor" + Identifier "Builtin Default Monitor" +EndSection + +Section "Device" + Identifier "Builtin Default fbdev Device 0" + Driver "omapfb" +EndSection + +Section "Screen" + Identifier "Builtin Default fbdev Screen 0" + Device "Builtin Default fbdev Device 0" + Monitor "Builtin Default Monitor" +EndSection + +Section "ServerLayout" + Identifier "Builtin Default Layout" + Screen "Builtin Default fbdev Screen 0" +EndSection + +Section "ServerFlags" + Option "DontZap" "0" +EndSection diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-non_hardware.cfg" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-non_hardware.cfg" new file mode 100644 index 0000000000..361343bb58 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-non_hardware.cfg" @@ -0,0 +1,30 @@ +# +# Miscellaneous filesystems +# +CONFIG_NFS_DEF_FILE_IO_SIZE=1024 + +# +# Multiple Device Support +# +# CONFIG_MD is not set + +# Kernel Features +# +CONFIG_NO_HZ=y + +# +# CPUIdle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y + +# +# Kernel hacking +# +CONFIG_DEBUG_FS=y + +# +# Power management options +# +CONFIG_PM_DEBUG=y diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..56f7f0f1e3 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,13 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH arm + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for preempt-rt kernels +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..80640db4a2 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,13 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH arm + +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for standard kernels +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..51eaf2d32c --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH arm + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..10134c81f5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" @@ -0,0 +1,320 @@ +# +# System Type +# +CONFIG_ARCH_OMAP=y + +# +# TI OMAP Implementations +# +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y + +# +# TI OMAP Common Features +# +CONFIG_ARCH_OMAP2PLUS=y + +# +# OMAP Feature Selections +# +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_DM_TIMER=y +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_SMARTREFLEX=y +CONFIG_OMAP_SMARTREFLEX_CLASS3=y +CONFIG_OMAP_MBOX_FWK=m +CONFIG_OMAP_MBOX_KFIFO_SIZE=256 + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP3_BEAGLE=y + +# +# Processor Features +# +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_ERRATA_430973=y + +# +# Kernel Features +# +CONFIG_LEDS=y + + +# +# Serial drivers +# +CONFIG_SERIAL_OMAP=y +CONFIG_SERIAL_OMAP_CONSOLE=y + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_NEON=y + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_RUNTIME=y + +# +# Generic Driver Options +# +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_NAND=y + +CONFIG_MTD_NAND_OMAP2=y + +CONFIG_MTD_UBI=y + +# +# SCSI device support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_SMSC911X=y +CONFIG_USB_NET_SMSC95XX=y + +# +# Userland interfaces +# +CONFIG_INPUT_EVDEV=y + +# +# Input Device Drivers +# +CONFIG_KEYBOARD_TWL4030=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y + +# +# Miscellaneous I2C Chip support +# +CONFIG_I2C=y +CONFIG_I2C_OMAP=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_OMAP24XX=y + +# +# I2C GPIO expanders: +# +CONFIG_GPIO_TWL4030=y + +# +# SPI GPIO expanders: +# +CONFIG_OMAP_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Multifunction device drivers +# +CONFIG_TWL4030_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DUMMY=y +CONFIG_REGULATOR_TWL4030=y + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_DRM=m +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +CONFIG_OMAP2_VRAM=y +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_VRAM_SIZE=14 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set +CONFIG_OMAP2_DSS_DPI=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +# CONFIG_OMAP2_DSS_SDI is not set +CONFIG_OMAP2_DSS_DSI=y +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +CONFIG_FB_OMAP2_NUM_FBS=2 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC_DPI=y +CONFIG_PANEL_DVI=y +CONFIG_PANEL_SHARP_LS037V7DW01=y +# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_PANEL_TAAL is not set +CONFIG_PANEL_TPO_TD043MTEA1=m +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set + +# +# Console display driver support +# +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +# CONFIG_VGA_CONSOLE is not set + +# DMA Devices +CONFIG_DMADEVICES=y +CONFIG_DMA_OMAP=y +CONFIG_DMA_OF=y + +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_OMAP_TWL4030=y + +# +# USB Input Devices +# +CONFIG_USB=y +CONFIG_USB_SUPPORT=y + +# +# Miscellaneous USB options +# +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_OMAP2PLUS=y +CONFIG_USB_OMAP=y + +# +# OMAP 343x high speed USB support +# +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +CONFIG_USB_INVENTRA_DMA=y + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_OTG_UTILS=y +CONFIG_TWL4030_USB=y + +# USB gadget modules +CONFIG_USB_G_NCM=y +CONFIG_USB_MASS_STORAGE=y + +CONFIG_MMC=y + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_OMAP_HS=y + +# +# Real Time Clock +# +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_TWL4030=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_VFAT_FS=y + +# +# Multimedia core support +# + +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Advanced Power Management Emulation support +# +CONFIG_APM_EMULATION=y diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..24196e6f67 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,7 @@ +kconf hardware {{=machine}}.cfg +kconf non-hardware {{machine}}-non_hardware.cfg + +include features/usb-net/usb-net.scc + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,25 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..1e814c54d7 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..ca7f8c5978 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" \ No newline at end of file diff --git a/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT b/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT new file mode 100644 index 0000000000..fb950dc69f --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT @@ -0,0 +1,17 @@ +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/scripts/lib/bsp/substrate/target/arch/common/README b/scripts/lib/bsp/substrate/target/arch/common/README new file mode 100644 index 0000000000..928659f302 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/README @@ -0,0 +1,118 @@ +This README file contains information on building the meta-{{=machine}} +BSP layer, and booting the images contained in the /binary directory. +Please see the corresponding sections below for details. + + +Dependencies +============ + +This layer depends on: + + URI: git://git.openembedded.org/bitbake + branch: master + + URI: git://git.openembedded.org/openembedded-core + layers: meta + branch: master + + URI: git://git.yoctoproject.org/xxxx + layers: xxxx + branch: master + + +Patches +======= + +Please submit any patches against this BSP to the Yocto mailing list +(yocto@yoctoproject.org) and cc: the maintainer: + +Maintainer: XXX YYYYYY + +Please see the meta-xxxx/MAINTAINERS file for more details. + + +Table of Contents +================= + + I. Building the meta-{{=machine}} BSP layer + II. Booting the images in /binary + + +I. Building the meta-{{=machine}} BSP layer +======================================== + +--- replace with specific instructions for your layer --- + +In order to build an image with BSP support for a given release, you +need to download the corresponding BSP tarball from the 'Board Support +Package (BSP) Downloads' page of the Yocto Project website. + +Having done that, and assuming you extracted the BSP tarball contents +at the top-level of your yocto build tree, you can build a +{{=machine}} image by adding the location of the meta-{{=machine}} +layer to bblayers.conf, along with any other layers needed (to access +common metadata shared between BSPs) e.g.: + + yocto/meta-xxxx \ + yocto/meta-xxxx/meta-{{=machine}} \ + +To enable the {{=machine}} layer, add the {{=machine}} MACHINE to local.conf: + + MACHINE ?= "{{=machine}}" + +You should then be able to build a {{=machine}} image as such: + + $ source oe-init-build-env + $ bitbake core-image-sato + +At the end of a successful build, you should have a live image that +you can boot from a USB flash drive (see instructions on how to do +that below, in the section 'Booting the images from /binary'). + +As an alternative to downloading the BSP tarball, you can also work +directly from the meta-xxxx git repository. For each BSP in the +'meta-xxxx' repository, there are multiple branches, one corresponding +to each major release starting with 'laverne' (0.90), in addition to +the latest code which tracks the current master (note that not all +BSPs are present in every release). Instead of extracting a BSP +tarball at the top level of your yocto build tree, you can +equivalently check out the appropriate branch from the meta-xxxx +repository at the same location. + + +II. Booting the images in /binary +================================= + +--- replace with specific instructions for your platform --- + +This BSP contains bootable live images, which can be used to directly +boot Yocto off of a USB flash drive. + +Under Linux, insert a USB flash drive. Assuming the USB flash drive +takes device /dev/sdf, use dd to copy the live image to it. For +example: + +# dd if=core-image-sato-{{=machine}}-20101207053738.hddimg of=/dev/sdf +# sync +# eject /dev/sdf + +This should give you a bootable USB flash device. Insert the device +into a bootable USB socket on the target, and power on. This should +result in a system booted to the Sato graphical desktop. + +If you want a terminal, use the arrows at the top of the UI to move to +different pages of available applications, one of which is named +'Terminal'. Clicking that should give you a root terminal. + +If you want to ssh into the system, you can use the root terminal to +ifconfig the IP address and use that to ssh in. The root password is +empty, so to log in type 'root' for the user name and hit 'Enter' at +the Password prompt: and you should be in. + +---- + +If you find you're getting corrupt images on the USB (it doesn't show +the syslinux boot: prompt, or the boot: prompt contains strange +characters), try doing this first: + +# dd if=/dev/zero of=/dev/sdf bs=1M count=512 diff --git a/scripts/lib/bsp/substrate/target/arch/common/README.sources b/scripts/lib/bsp/substrate/target/arch/common/README.sources new file mode 100644 index 0000000000..3c4cb7b435 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/README.sources @@ -0,0 +1,17 @@ +The sources for the packages comprising the images shipped with this +BSP can be found at the following location: + +http://downloads.yoctoproject.org/mirror/sources/ + +The metadata used to generate the images shipped with this BSP, in +addition to the code contained in this BSP, can be found at the +following location: + +http://www.yoctoproject.org/downloads/yocto-1.1/poky-edison-6.0.tar.bz2 + +The metadata used to generate the images shipped with this BSP, in +addition to the code contained in this BSP, can also be found at the +following locations: + +git://git.yoctoproject.org/poky.git +git://git.yoctoproject.org/meta-xxxx diff --git a/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf b/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf new file mode 100644 index 0000000000..5529f45954 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf @@ -0,0 +1,10 @@ +# We have a conf and classes directory, add to BBPATH +BBPATH .= ":${LAYERDIR}" + +# We have a recipes-* directories, add to BBFILES +BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ + ${LAYERDIR}/recipes-*/*/*.bbappend" + +BBFILE_COLLECTIONS += "{{=machine}}" +BBFILE_PATTERN_{{=machine}} = "^${LAYERDIR}/" +BBFILE_PRIORITY_{{=machine}} = "6" diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig new file mode 100644 index 0000000000..3b85d3821f --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig @@ -0,0 +1,5 @@ +# Assume a USB mouse and keyboard are connected +{{ input type:"boolean" name:"touchscreen" msg:"Does your BSP have a touchscreen? (y/n)" default:"n" }} +HAVE_TOUCHSCREEN={{=touchscreen}} +{{ input type:"boolean" name:"keyboard" msg:"Does your BSP have a keyboard? (y/n)" default:"y" }} +HAVE_KEYBOARD={{=keyboard}} diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend new file mode 100644 index 0000000000..6d4804d127 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend @@ -0,0 +1,2 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..03b7d84ec2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,26 @@ +{{ if kernel_choice == "custom": }} +{{ input type:"boolean" name:"custom_kernel_remote" prio:"20" msg:"Is the custom kernel you'd like to use in a remote git repo? (y/n)" default:"y"}} + +{{ if kernel_choice == "custom" and custom_kernel_remote == "y": }} +{{ input type:"edit-git-repo" name:"custom_kernel_remote_path" prio:"20" msg:"Please enter the full URI to the remote git repo (the default corresponds to linux-stable v3.13.9)" default:"git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git"}} + +{{ if kernel_choice == "custom" and custom_kernel_remote == "n": }} +{{ input type:"edit-git-repo" name:"custom_kernel_local_path" prio:"20" msg:"You've indicated that you're not using a remote git repo. Please enter the full path to the local git repo you want to use (the default assumes a local linux-stable v3.13.9)" default:"/home/trz/yocto/kernels/linux-stable.git"}} + +{{ if kernel_choice == "custom": }} +{{ input type:"boolean" name:"custom_kernel_need_kbranch" prio:"20" msg:"Do you need to use a specific (non-master) branch? (y/n)" default:"n"}} + +{{ if kernel_choice == "custom" and custom_kernel_need_kbranch == "y": }} +{{ input type:"edit" name:"custom_kernel_kbranch" prio:"20" msg:"Please enter the branch you want to use (the default branch corresponds to the linux-stable 'linux-3.13.y' branch):" default:"linux-3.13.y"}} + +{{ if kernel_choice == "custom": }} +{{ input type:"edit" name:"custom_kernel_srcrev" prio:"20" msg:"Please enter the SRCREV (commit id) you'd like to use (use '${AUTOREV}' to track the current HEAD):" default:"${AUTOREV}"}} + +{{ if kernel_choice == "custom": }} +{{ input type:"edit" name:"custom_kernel_linux_version" prio:"20" msg:"Please enter the Linux version of the kernel you've specified:" default:"3.13.9"}} + +{{ if kernel_choice == "custom": }} +{{ input type:"edit" name:"custom_kernel_linux_version_extension" prio:"20" msg:"Please enter a Linux version extension if you want (it will show up at the end of the kernel name shown by uname):" default:"-custom"}} + +{{ if kernel_choice == "custom": }} +{{ input type:"edit-file" name:"custom_kernel_defconfig" prio:"20" msg:"It's recommended (but not required) that custom kernels be built using a defconfig. Please enter the full path to the defconfig for your kernel (NOTE: if you don't specify a defconfig the kernel probably won't build or boot):" default:""}} diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom.bb" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom.bb" new file mode 100644 index 0000000000..6d3cc6f743 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom.bb" @@ -0,0 +1,57 @@ +# This file was derived from the linux-yocto-custom.bb recipe in +# oe-core. +# +# linux-yocto-custom.bb: +# +# A yocto-bsp-generated kernel recipe that uses the linux-yocto and +# oe-core kernel classes to apply a subset of yocto kernel +# management to git managed kernel repositories. +# +# Warning: +# +# Building this kernel without providing a defconfig or BSP +# configuration will result in build or boot errors. This is not a +# bug. +# +# Notes: +# +# patches: patches can be merged into to the source git tree itself, +# added via the SRC_URI, or controlled via a BSP +# configuration. +# +# example configuration addition: +# SRC_URI += "file://smp.cfg" +# example patch addition: +# SRC_URI += "file://0001-linux-version-tweak.patch +# example feature addition: +# SRC_URI += "file://feature.scc" +# + +inherit kernel +require recipes-kernel/linux/linux-yocto.inc + +{{ if kernel_choice == "custom" and custom_kernel_remote == "y": }} +SRC_URI = "{{=custom_kernel_remote_path}};protocol=git;bareclone=1" +{{ if kernel_choice == "custom" and custom_kernel_remote == "n": }} +SRC_URI = "git://{{=custom_kernel_local_path}};protocol=file;bareclone=1" + +SRC_URI += "file://defconfig" + +SRC_URI += "file://{{=machine}}.scc \ + file://{{=machine}}.cfg \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + " + +{{ if kernel_choice == "custom" and custom_kernel_need_kbranch == "y" and custom_kernel_kbranch and custom_kernel_kbranch != "master": }} +KBRANCH = "{{=custom_kernel_kbranch}}" + +LINUX_VERSION ?= "{{=custom_kernel_linux_version}}" +LINUX_VERSION_EXTENSION ?= "{{=custom_kernel_linux_version_extension}}" + +SRCREV="{{=custom_kernel_srcrev}}" + +PR = "r0" +PV = "${LINUX_VERSION}+git${SRCPV}" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/defconfig" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/defconfig" new file mode 100644 index 0000000000..ceb0ffa30c --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/defconfig" @@ -0,0 +1,5 @@ +# +# Placeholder for custom default kernel configuration. yocto-bsp will +# replace this file with a user-specified defconfig. +# +{{ if custom_kernel_defconfig: replace_file(of, custom_kernel_defconfig) }} diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..17c8b503da --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-config.cfg" @@ -0,0 +1,8 @@ +# +# Used by yocto-kernel to manage config options. +# +# yocto-kernel may change the contents of this file in any +# way it sees fit, including removing comments like this, +# so don't manually make any modifications you don't want +# to lose. +# diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..7a598d9118 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}-user-patches.scc" @@ -0,0 +1,8 @@ +# +# Used by yocto-kernel to manage patches. +# +# yocto-kernel may change the contents of this file in any +# way it sees fit, including removing comments like this, +# so don't manually make any modifications you don't want +# to lose. +# diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.cfg" new file mode 100644 index 0000000000..95170b12eb --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.cfg" @@ -0,0 +1,3 @@ +# +# A convenient place to add config options, nothing more. +# diff --git "a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.scc" new file mode 100644 index 0000000000..2e3ca90793 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == \"custom\": }} linux-yocto-custom/{{=machine}}.scc" @@ -0,0 +1,17 @@ +# +# The top-level 'feature' for the {{=machine}} custom kernel. +# +# Essentially this is a convenient top-level container or starting +# point for adding lower-level config fragements and features. +# + +# {{=machine}}.cfg in the linux-yocto-custom subdir is just a +# convenient place for adding random config fragments. + +kconf hardware {{=machine}}.cfg + +# These are used by yocto-kernel to add config fragments and features. +# Don't remove if you plan on using yocto-kernel with this BSP. + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git a/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..932fd79bb9 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf @@ -0,0 +1,68 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +{{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_core2" }} +{{ input type:"choice" val:"tune_i586" msg:"i586 tuning optimizations" }} +{{ input type:"choice" val:"tune_atom" msg:"Atom tuning optimizations" }} +{{ input type:"choice" val:"tune_core2" msg:"Core2 tuning optimizations" }} +{{ if tunefile == "tune_i586": }} +require conf/machine/include/tune-i586.inc +{{ if tunefile == "tune_atom": }} +require conf/machine/include/tune-atom.inc +{{ if tunefile == "tune_core2": }} +DEFAULTTUNE="core2-32" +require conf/machine/include/tune-core2.inc + +require conf/machine/include/x86-base.inc + +MACHINE_FEATURES += "wifi efi pcbios" + +{{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} + +{{ if xserver == "y" and (kernel_choice == "linux-yocto_3.14" or kernel_choice == "linux-yocto_3.10"): }} +{{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} +{{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} +{{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} +{{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} + +{{ if xserver == "y" and kernel_choice == "custom": }} +{{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} +{{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} +{{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} +{{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} + +{{ if xserver == "y" and kernel_choice != "linux-yocto_3.14" and kernel_choice != "linux-yocto_3.10" and kernel_choice != "custom": xserver_choice = "xserver_i915" }} + +{{ if xserver == "y": }} +XSERVER ?= "${XSERVER_X86_BASE} \ + ${XSERVER_X86_EXT} \ +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} + ${XSERVER_X86_VESA} \ +{{ if xserver == "y" and xserver_choice == "xserver_i915": }} + ${XSERVER_X86_I915} \ +{{ if xserver == "y" and xserver_choice == "xserver_i965": }} + ${XSERVER_X86_I965} \ +{{ if xserver == "y": }} + " + +MACHINE_EXTRA_RRECOMMENDS += "linux-firmware v86d" + +GLIBC_ADDONS = "nptl" + +EXTRA_OECONF_append_pn-matchbox-panel-2 = " --with-battery=acpi" + +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} +APPEND += "video=vesafb vga=0x318" diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..bfefb0d0a0 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,15 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH i386 + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for preempt-rt kernels +include cfg/usb-mass-storage.scc +include cfg/boot-live.scc +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..60b670dffc --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,15 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH i386 + +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for standard kernels +include cfg/usb-mass-storage.scc +include cfg/boot-live.scc +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..ec44ef9485 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH i386 + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..e93c0b8a08 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" @@ -0,0 +1,54 @@ +CONFIG_X86_32=y +CONFIG_MATOM=y +CONFIG_PRINTK=y + +# Basic hardware support for the box - network, USB, PCI, sound +CONFIG_NETDEVICES=y +CONFIG_ATA=y +CONFIG_ATA_GENERIC=y +CONFIG_ATA_SFF=y +CONFIG_PCI=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USB=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_R8169=y +CONFIG_PATA_SCH=y +CONFIG_MMC_SDHCI_PCI=y +CONFIG_USB_EHCI_HCD=y +CONFIG_PCIEPORTBUS=y +CONFIG_NET=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_OHCI_HCD=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HDA_INTEL=y +CONFIG_SATA_AHCI=y +CONFIG_AGP=y +CONFIG_PM=y +CONFIG_ACPI=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_INPUT=y + +# Make sure these are on, otherwise the bootup won't be fun +CONFIG_EXT3_FS=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_MODULES=y +CONFIG_SHMEM=y +CONFIG_TMPFS=y +CONFIG_PACKET=y + +# Needed for booting (and using) USB memory sticks +CONFIG_BLK_DEV_LOOP=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y + +CONFIG_RD_GZIP=y + +# Needed for booting (and using) CD images +CONFIG_BLK_DEV_SR=y diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..eda1d62f11 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,20 @@ +kconf hardware {{=machine}}.cfg + +include features/intel-e1xxxx/intel-e100.scc +include features/intel-e1xxxx/intel-e1xxxx.scc + +{{ if xserver == "y" and xserver_choice == "xserver_i915" or xserver_choice == "xserver_i965": }} +include features/i915/i915.scc + +include features/serial/8250.scc +include features/ericsson-3g/f5521gw.scc + +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} +include cfg/vesafb.scc + +include cfg/usb-mass-storage.scc +include cfg/boot-live.scc +include features/power/intel.scc + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,25 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..c1f26540a7 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..948d568cd1 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" \ No newline at end of file diff --git a/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT b/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT new file mode 100644 index 0000000000..89de354795 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT @@ -0,0 +1,17 @@ +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/scripts/lib/bsp/substrate/target/arch/layer/README b/scripts/lib/bsp/substrate/target/arch/layer/README new file mode 100644 index 0000000000..943dfc4412 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/README @@ -0,0 +1,64 @@ +This README file contains information on the contents of the +{{=layer_name}} layer. + +Please see the corresponding sections below for details. + + +Dependencies +============ + +This layer depends on: + + URI: git://git.openembedded.org/bitbake + branch: master + + URI: git://git.openembedded.org/openembedded-core + layers: meta + branch: master + + URI: git://git.yoctoproject.org/xxxx + layers: xxxx + branch: master + + +Patches +======= + +Please submit any patches against the {{=layer_name}} layer to the +xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer: + +Maintainer: XXX YYYYYY + + +Table of Contents +================= + + I. Adding the {{=layer_name}} layer to your build + II. Misc + + +I. Adding the {{=layer_name}} layer to your build +================================================= + +--- replace with specific instructions for the {{=layer_name}} layer --- + +In order to use this layer, you need to make the build system aware of +it. + +Assuming the {{=layer_name}} layer exists at the top-level of your +yocto build tree, you can add it to the build system by adding the +location of the {{=layer_name}} layer to bblayers.conf, along with any +other layers needed. e.g.: + + BBLAYERS ?= " \ + /path/to/yocto/meta \ + /path/to/yocto/meta-yocto \ + /path/to/yocto/meta-yocto-bsp \ + /path/to/yocto/meta-{{=layer_name}} \ + " + + +II. Misc +======== + +--- replace with specific information about the {{=layer_name}} layer --- diff --git a/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf b/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf new file mode 100644 index 0000000000..bdffe17195 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf @@ -0,0 +1,10 @@ +# We have a conf and classes directory, add to BBPATH +BBPATH .= ":${LAYERDIR}" + +# We have recipes-* directories, add to BBFILES +BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ + ${LAYERDIR}/recipes-*/*/*.bbappend" + +BBFILE_COLLECTIONS += "{{=layer_name}}" +BBFILE_PATTERN_{{=layer_name}} = "^${LAYERDIR}/" +BBFILE_PRIORITY_{{=layer_name}} = "{{=layer_priority}}" diff --git a/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall b/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall new file mode 100644 index 0000000000..e2a89c3b5d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall @@ -0,0 +1,14 @@ +{{ input type:"edit" name:"layer_priority" prio:"20" msg:"Please enter the layer priority you'd like to use for the layer:" default:"6"}} + +{{ input type:"boolean" name:"create_example_recipe" prio:"20" msg:"Would you like to have an example recipe created? (y/n)" default:"n"}} + +{{ if create_example_recipe == "y": }} +{{ input type:"edit" name:"example_recipe_name" prio:"20" msg:"Please enter the name you'd like to use for your example recipe:" default:"example"}} + +{{ input type:"boolean" name:"create_example_bbappend" prio:"20" msg:"Would you like to have an example bbappend file created? (y/n)" default:"n"}} + +{{ if create_example_bbappend == "y": }} +{{ input type:"edit" name:"example_bbappend_name" prio:"20" msg:"Please enter the name you'd like to use for your bbappend file:" default:"example"}} + +{{ if create_example_bbappend == "y": }} +{{ input type:"edit" name:"example_bbappend_version" prio:"20" msg:"Please enter the version number you'd like to use for your bbappend file (this should match the recipe you're appending to):" default:"0.1"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch" "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch" new file mode 100644 index 0000000000..2000a34da5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch" @@ -0,0 +1,12 @@ +# +# This is a non-functional placeholder file, here for example purposes +# only. +# +# If you had a patch for your recipe, you'd put it in this directory +# and reference it from your recipe's SRC_URI: +# +# SRC_URI += "file://example.patch" +# +# Note that you could also rename the directory containing this patch +# to remove the version number or simply rename it 'files'. Doing so +# allows you to use the same directory for multiple recipes. diff --git "a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend" "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend" new file mode 100644 index 0000000000..2e50ff668d --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == \"y\": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend" @@ -0,0 +1,8 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-${PV}:" + +# +# This .bbappend doesn't yet do anything - replace this text with +# modifications to the example_0.1.bb recipe, or whatever recipe it is +# that you want to modify with this .bbappend (make sure you change +# the recipe name (PN) and version (PV) to match). +# diff --git "a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch" "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch" new file mode 100644 index 0000000000..2000a34da5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch" @@ -0,0 +1,12 @@ +# +# This is a non-functional placeholder file, here for example purposes +# only. +# +# If you had a patch for your recipe, you'd put it in this directory +# and reference it from your recipe's SRC_URI: +# +# SRC_URI += "file://example.patch" +# +# Note that you could also rename the directory containing this patch +# to remove the version number or simply rename it 'files'. Doing so +# allows you to use the same directory for multiple recipes. diff --git "a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c" "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c" new file mode 100644 index 0000000000..71f2e46b4e --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c" @@ -0,0 +1,8 @@ +#include + +int main(int argc, char **argv) +{ + printf("Hello World!\n"); + + return 0; +} diff --git "a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb" "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb" new file mode 100644 index 0000000000..14bf344da5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == \"y\": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb" @@ -0,0 +1,23 @@ +# +# This file was derived from the 'Hello World!' example recipe in the +# Yocto Project Development Manual. +# + +DESCRIPTION = "Simple helloworld application" +SECTION = "examples" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" +PR = "r0" + +SRC_URI = "file://helloworld.c" + +S = "${WORKDIR}" + +do_compile() { + ${CC} helloworld.c -o helloworld +} + +do_install() { + install -d ${D}${bindir} + install -m 0755 helloworld ${D}${bindir} +} diff --git a/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..2e704263e1 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf @@ -0,0 +1,38 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +require conf/machine/include/tune-mips32.inc + +MACHINE_FEATURES = "screen keyboard pci usbhost ext2 ext3 serial" + +KERNEL_IMAGETYPE = "vmlinux" +KERNEL_ALT_IMAGETYPE = "vmlinux.bin" +KERNEL_IMAGE_STRIP_EXTRA_SECTIONS = ".comment" + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +{{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} +{{ if xserver == "y": }} +PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" +XSERVER ?= "xserver-xorg \ + xf86-input-evdev \ + xf86-video-fbdev" + +SERIAL_CONSOLE = "115200 ttyS0" +USE_VT ?= "0" + +MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" + +IMAGE_FSTYPES ?= "jffs2 tar.bz2" +JFFS2_ERASEBLOCK = "0x10000" \ No newline at end of file diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..b0fb63ac6a --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH mips + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..326663a509 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH mips + +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..4514765eb3 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH mips + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..a1b333ca56 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" @@ -0,0 +1 @@ +CONFIG_MIPS=y diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..1ef01b6e3c --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,7 @@ +kconf hardware {{=machine}}.cfg + +include cfg/usb-mass-storage.scc +include cfg/fs/vfat.scc + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,25 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" new file mode 100644 index 0000000000..85544e812c --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..1e814c54d7 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..ca7f8c5978 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" \ No newline at end of file diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..78fb5db22b --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf @@ -0,0 +1,74 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +TARGET_FPU = "" + +{{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_ppce300c3" }} +{{ input type:"choice" val:"tune_ppc476" msg:"ppc476 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppc603e" msg:"ppc603e tuning optimizations" }} +{{ input type:"choice" val:"tune_ppc7400" msg:"ppc7400 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce300c2" msg:"ppce300c2 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce300c3" msg:"ppce300c3 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce500" msg:"ppce500 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce500mc" msg:"ppce500mc tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce500v2" msg:"ppce500v2 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce5500" msg:"ppce5500 tuning optimizations" }} +{{ input type:"choice" val:"tune_ppce6500" msg:"ppce6500 tuning optimizations" }} +{{ if tunefile == "tune_ppc476": }} +include conf/machine/include/tune-ppc476.inc +{{ if tunefile == "tune_ppc603e": }} +include conf/machine/include/tune-ppc603e.inc +{{ if tunefile == "tune_ppc7400": }} +include conf/machine/include/tune-ppc7400.inc +{{ if tunefile == "tune_ppce300c2": }} +include conf/machine/include/tune-ppce300c2.inc +{{ if tunefile == "tune_ppce300c3": }} +include conf/machine/include/tune-ppce300c3.inc +{{ if tunefile == "tune_ppce500": }} +include conf/machine/include/tune-ppce500.inc +{{ if tunefile == "tune_ppce500mc": }} +include conf/machine/include/tune-ppce500mc.inc +{{ if tunefile == "tune_ppce500v2": }} +include conf/machine/include/tune-ppce500v2.inc +{{ if tunefile == "tune_ppce5500": }} +include conf/machine/include/tune-ppce5500.inc +{{ if tunefile == "tune_ppce6500": }} +include conf/machine/include/tune-ppce6500.inc + +KERNEL_IMAGETYPE = "uImage" + +EXTRA_IMAGEDEPENDS += "u-boot" +UBOOT_MACHINE_{{=machine}} = "MPC8315ERDB_config" + +SERIAL_CONSOLE = "115200 ttyS0" + +MACHINE_FEATURES = "keyboard pci ext2 ext3 serial" + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +{{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} +{{ if xserver == "y": }} +PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" +XSERVER ?= "xserver-xorg \ + xf86-input-evdev \ + xf86-video-fbdev" + +PREFERRED_VERSION_u-boot ?= "v2013.07%" +{{ input type:"edit" name:"uboot_entrypoint" prio:"40" msg:"Please specify a value for UBOOT_ENTRYPOINT:" default:"0x00000000" }} +UBOOT_ENTRYPOINT = "{{=uboot_entrypoint}}" + +{{ input type:"edit" name:"kernel_devicetree" prio:"40" msg:"Please specify a [arch/powerpc/boot/dts/xxx] value for KERNEL_DEVICETREE:" default:"mpc8315erdb.dts" }} +KERNEL_DEVICETREE = "${S}/arch/powerpc/boot/dts/{{=kernel_devicetree}}" + +MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..1da7b0c892 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH powerpc + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..53a74a6ca2 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH powerpc + +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..4ca6224774 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH powerpc + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..9f37d07553 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" @@ -0,0 +1,163 @@ +.......................................................................... +. WARNING +. +. This file is a kernel configuration fragment, and not a full kernel +. configuration file. The final kernel configuration is made up of +. an assembly of processed fragments, each of which is designed to +. capture a specific part of the final configuration (e.g. platform +. configuration, feature configuration, and board specific hardware +. configuration). For more information on kernel configuration, please +. consult the product documentation. +. +.......................................................................... +CONFIG_PPC32=y +CONFIG_PPC_OF=y +CONFIG_PPC_UDBG_16550=y + +# +# Processor support +# +CONFIG_PPC_83xx=y + +# +# Platform support +# +CONFIG_MPC831x_RDB=y +# CONFIG_PPC_CHRP is not set +# CONFIG_PPC_PMAC is not set + +# +# Bus options +# +CONFIG_PCI=y + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_OF_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_AMDSTD=y + +# +# Mapping drivers for chip access +# +CONFIG_MTD_PHYSMAP_OF=y + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=y + +# +# Ethernet (1000 Mbit) +# +CONFIG_GIANFAR=y + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=2 + +# +# Watchdog Device Drivers +# +CONFIG_8xxx_WDT=y + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Hardware Bus support +# +CONFIG_I2C_MPC=y + +CONFIG_SENSORS_LM75=y + +CONFIG_MISC_DEVICES=y + +# +# Miscellaneous I2C Chip support +# +CONFIG_EEPROM_AT24=y + +# +# SPI support +# +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_MPC8xxx=y + +# +# SPI Protocol Masters +# +CONFIG_HWMON=y + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_LOGGING=y + +CONFIG_ATA=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_SATA_FSL=y +CONFIG_ATA_SFF=y + +# +# USB support +# +CONFIG_USB=m +CONFIG_USB_DEVICEFS=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_FSL=y +CONFIG_USB_STORAGE=m + +# +# Real Time Clock +# +CONFIG_RTC_CLASS=y + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_DS1307=y + +CONFIG_KGDB_8250=m + +CONFIG_CRYPTO_DEV_TALITOS=m + +CONFIG_FSL_DMA=y + +CONFIG_MMC=y +CONFIG_MMC_SPI=m + +CONFIG_USB_FSL_MPH_DR_OF=y diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..c9fd468180 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,9 @@ +kconf hardware {{=machine}}.cfg + +include cfg/usb-mass-storage.scc +include cfg/fs/vfat.scc + +include cfg/dmaengine.scc + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,25 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..00c8c68933 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..a61f5ccb80 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..aebda9b3a5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..782ac217d9 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf @@ -0,0 +1,68 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +{{ if qemuarch == "i386" or qemuarch == "x86_64": }} +PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" +PREFERRED_PROVIDER_virtual/libgl ?= "mesa" +PREFERRED_PROVIDER_virtual/libgles1 ?= "mesa" +PREFERRED_PROVIDER_virtual/libgles2 ?= "mesa" + +{{ input type:"choicelist" name:"qemuarch" prio:"5" msg:"Which qemu architecture would you like to use?" default:"i386" }} +{{ input type:"choice" val:"i386" msg:"i386 (32-bit)" }} +{{ input type:"choice" val:"x86_64" msg:"x86_64 (64-bit)" }} +{{ input type:"choice" val:"arm" msg:"ARM (32-bit)" }} +{{ input type:"choice" val:"powerpc" msg:"PowerPC (32-bit)" }} +{{ input type:"choice" val:"mips" msg:"MIPS (32-bit)" }} +{{ if qemuarch == "i386": }} +require conf/machine/include/qemu.inc +require conf/machine/include/tune-i586.inc +{{ if qemuarch == "x86_64": }} +require conf/machine/include/qemu.inc +require conf/machine/include/tune-x86_64.inc +{{ if qemuarch == "arm": }} +require conf/machine/include/qemu.inc +require conf/machine/include/tune-arm926ejs.inc +{{ if qemuarch == "powerpc": }} +require conf/machine/include/qemu.inc +require conf/machine/include/tune-ppc603e.inc +{{ if qemuarch == "mips": }} +require conf/machine/include/qemu.inc +require conf/machine/include/tune-mips32.inc + +{{ if qemuarch == "i386" or qemuarch == "x86_64": }} +MACHINE_FEATURES += "x86" +KERNEL_IMAGETYPE = "bzImage" +SERIAL_CONSOLE = "115200 ttyS0" +XSERVER = "xserver-xorg \ + ${@base_contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ + xf86-input-vmmouse \ + xf86-input-keyboard \ + xf86-input-evdev \ + xf86-video-vmware" + +{{ if qemuarch == "arm": }} +KERNEL_IMAGETYPE = "zImage" +SERIAL_CONSOLE = "115200 ttyAMA0" + +{{ if qemuarch == "powerpc": }} +KERNEL_IMAGETYPE = "vmlinux" +SERIAL_CONSOLE = "115200 ttyS0" + +{{ if qemuarch == "mips": }} +KERNEL_IMAGETYPE = "vmlinux" +KERNEL_ALT_IMAGETYPE = "vmlinux.bin" +SERIAL_CONSOLE = "115200 ttyS0" +MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces new file mode 100644 index 0000000000..16967763e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces @@ -0,0 +1,5 @@ +# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8) + +# The loopback interface +auto lo +iface lo inet loopback diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf new file mode 100644 index 0000000000..13519804bc --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf @@ -0,0 +1,77 @@ + +Section "Files" +EndSection + +Section "InputDevice" + Identifier "Generic Keyboard" + Driver "evdev" + Option "CoreKeyboard" + Option "Device" "/dev/input/by-path/platform-i8042-serio-0-event-kbd" + Option "XkbRules" "xorg" + Option "XkbModel" "evdev" + Option "XkbLayout" "us" +EndSection + +Section "InputDevice" + Identifier "Configured Mouse" +{{ if qemuarch == "arm" or qemuarch == "powerpc" or qemuarch == "mips": }} + Driver "mouse" +{{ if qemuarch == "i386" or qemuarch == "x86_64": }} + Driver "vmmouse" + + Option "CorePointer" + Option "Device" "/dev/input/mice" + Option "Protocol" "ImPS/2" + Option "ZAxisMapping" "4 5" + Option "Emulate3Buttons" "true" +EndSection + +Section "InputDevice" + Identifier "Qemu Tablet" + Driver "evdev" + Option "CorePointer" + Option "Device" "/dev/input/touchscreen0" + Option "USB" "on" +EndSection + +Section "Device" + Identifier "Graphics Controller" +{{ if qemuarch == "arm" or qemuarch == "powerpc" or qemuarch == "mips": }} + Driver "fbdev" +{{ if qemuarch == "i386" or qemuarch == "x86_64": }} + Driver "vmware" + +EndSection + +Section "Monitor" + Identifier "Generic Monitor" + Option "DPMS" + # 1024x600 59.85 Hz (CVT) hsync: 37.35 kHz; pclk: 49.00 MHz + Modeline "1024x600_60.00" 49.00 1024 1072 1168 1312 600 603 613 624 -hsync +vsync + # 640x480 @ 60Hz (Industry standard) hsync: 31.5kHz + ModeLine "640x480" 25.2 640 656 752 800 480 490 492 525 -hsync -vsync + # 640x480 @ 72Hz (VESA) hsync: 37.9kHz + ModeLine "640x480" 31.5 640 664 704 832 480 489 491 520 -hsync -vsync + # 640x480 @ 75Hz (VESA) hsync: 37.5kHz + ModeLine "640x480" 31.5 640 656 720 840 480 481 484 500 -hsync -vsync + # 640x480 @ 85Hz (VESA) hsync: 43.3kHz + ModeLine "640x480" 36.0 640 696 752 832 480 481 484 509 -hsync -vsync +EndSection + +Section "Screen" + Identifier "Default Screen" + Device "Graphics Controller" + Monitor "Generic Monitor" + SubSection "Display" + Modes "640x480" + EndSubSection +EndSection + +Section "ServerLayout" + Identifier "Default Layout" + Screen "Default Screen" + InputDevice "Generic Keyboard" + # InputDevice "Configured Mouse" + InputDevice "QEMU Tablet" + Option "AllowEmptyInput" "no" +EndSection diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..af34437d0a --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH {{=qemuarch}} + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..0e20023764 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,16 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH {{=qemuarch}} + +{{ if qemuarch == "i386" or qemuarch == "x86_64": }} +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if qemuarch == "arm": }} +include bsp/arm-versatile-926ejs/arm-versatile-926ejs-standard +{{ if qemuarch == "powerpc": }} +include bsp/qemu-ppc32/qemu-ppc32-standard +{{ if qemuarch == "mips": }} +include bsp/mti-malta32/mti-malta32-be-standard +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..10c4dac44d --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH {{=qemuarch}} + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..f3739be1e6 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,4 @@ +kconf hardware {{=machine}}.cfg + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..7599ecb0a5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,49 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..73b6e34839 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,55 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/qemuppc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.35" diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..da4e61ef83 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,55 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/common-pc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.35" diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.4.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.4.bbappend" new file mode 100644 index 0000000000..013883ffeb --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.4.bbappend" @@ -0,0 +1,55 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/common-pc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "0143c6ebb4a2d63b241df5f608b19f483f7eb9e0" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "8f55bee2403176a50cc0dd41811aa60fcf07243c" +#LINUX_VERSION = "3.14" diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..392ace6694 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,55 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "b170394a475b96ecc92cbc9e4b002bed0a9f69c5" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "c2ed0f16fdec628242a682897d5d86df4547cf24" +#LINUX_VERSION = "3.10.35" diff --git "a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..2cc9b87cf2 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,55 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "arm": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} + +{{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} + +{{ if need_new_kbranch == "y" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "i386": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "y" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n" and qemuarch == "mips": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "0143c6ebb4a2d63b241df5f608b19f483f7eb9e0" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "8f55bee2403176a50cc0dd41811aa60fcf07243c" +#LINUX_VERSION = "3.14" diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..53e8e92e6d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf @@ -0,0 +1,58 @@ +#@TYPE: Machine +#@NAME: {{=machine}} + +#@DESCRIPTION: Machine configuration for {{=machine}} systems + +{{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} +{{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} +{{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" + +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} +{{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} +PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" +PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" + +{{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_core2" }} +{{ input type:"choice" val:"tune_core2" msg:"Core2 tuning optimizations" }} +{{ input type:"choice" val:"tune_corei7" msg:"Corei7 tuning optimizations" }} +{{ if tunefile == "tune_core2": }} +DEFAULTTUNE ?= "core2-64" +require conf/machine/include/tune-core2.inc +{{ if tunefile == "tune_corei7": }} +DEFAULTTUNE ?= "corei7-64" +require conf/machine/include/tune-corei7.inc + +require conf/machine/include/x86-base.inc + +MACHINE_FEATURES += "wifi efi pcbios" + +{{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} + +{{ if xserver == "y": }} +{{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} + +{{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} +{{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} +{{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} +{{ if xserver == "y": }} +XSERVER ?= "${XSERVER_X86_BASE} \ + ${XSERVER_X86_EXT} \ +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} + ${XSERVER_X86_VESA} \ +{{ if xserver == "y" and xserver_choice == "xserver_i915": }} + ${XSERVER_X86_I915} \ +{{ if xserver == "y" and xserver_choice == "xserver_i965": }} + ${XSERVER_X86_I965} \ +{{ if xserver == "y": }} + " + +MACHINE_EXTRA_RRECOMMENDS += "linux-firmware v86d" + +GLIBC_ADDONS = "nptl" + +EXTRA_OECONF_append_pn-matchbox-panel-2 = " --with-battery=acpi" + +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} +APPEND += "video=vesafb vga=0x318" diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == \"y\": }} xorg.conf" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == \"y\": }} xserver-xf86-config_0.1.bbappend" @@ -0,0 +1 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall @@ -0,0 +1,5 @@ +{{ if kernel_choice != "custom": }} +{{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} + +{{ if kernel_choice != "custom" and use_default_kernel == "n": }} +{{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" new file mode 100644 index 0000000000..c9882590a8 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-preempt-rt.scc" @@ -0,0 +1,15 @@ +define KMACHINE {{=machine}} +define KTYPE preempt-rt +define KARCH x86_64 + +include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for preempt-rt kernels +include cfg/usb-mass-storage.scc +include cfg/boot-live.scc +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" new file mode 100644 index 0000000000..e500bad4b2 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-standard.scc" @@ -0,0 +1,15 @@ +define KMACHINE {{=machine}} +define KTYPE standard +define KARCH x86_64 + +include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc + +# default policy for standard kernels +include cfg/usb-mass-storage.scc +include cfg/boot-live.scc +include features/latencytop/latencytop.scc +include features/profiling/profiling.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" new file mode 100644 index 0000000000..e8e3c1c04d --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-tiny.scc" @@ -0,0 +1,9 @@ +define KMACHINE {{=machine}} +define KTYPE tiny +define KARCH x86_64 + +include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} +{{ if need_new_kbranch == "y": }} +branch {{=machine}} + +include {{=machine}}.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-config.cfg" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-features.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}-user-patches.scc" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" new file mode 100644 index 0000000000..b4b82d7ca0 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.cfg" @@ -0,0 +1,47 @@ +CONFIG_PRINTK=y + +# Basic hardware support for the box - network, USB, PCI, sound +CONFIG_NETDEVICES=y +CONFIG_ATA=y +CONFIG_ATA_GENERIC=y +CONFIG_ATA_SFF=y +CONFIG_PCI=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USB=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_R8169=y +CONFIG_PATA_SCH=y +CONFIG_MMC_SDHCI_PCI=y +CONFIG_USB_EHCI_HCD=y +CONFIG_PCIEPORTBUS=y +CONFIG_NET=y +CONFIG_USB_UHCI_HCD=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HDA_INTEL=y + +# Make sure these are on, otherwise the bootup won't be fun +CONFIG_EXT3_FS=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_MODULES=y +CONFIG_SHMEM=y +CONFIG_TMPFS=y +CONFIG_PACKET=y + +CONFIG_I2C=y +CONFIG_AGP=y +CONFIG_PM=y +CONFIG_ACPI=y +CONFIG_INPUT=y + +# Needed for booting (and using) USB memory sticks +CONFIG_BLK_DEV_LOOP=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y + +CONFIG_RD_GZIP=y diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" new file mode 100644 index 0000000000..db45140381 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != \"custom\": }} files/{{=machine}}.scc" @@ -0,0 +1,13 @@ +kconf hardware {{=machine}}.cfg + +include features/serial/8250.scc +{{ if xserver == "y" and xserver_choice == "xserver_vesa": }} +include cfg/vesafb.scc +{{ if xserver == "y" and xserver_choice == "xserver_i915" or xserver_choice == "xserver_i965": }} +include features/i915/i915.scc + +include cfg/usb-mass-storage.scc +include features/power/intel.scc + +kconf hardware {{=machine}}-user-config.cfg +include {{=machine}}-user-patches.scc diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-dev\": }} linux-yocto-dev.bbappend" @@ -0,0 +1,25 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" new file mode 100644 index 0000000000..00c8c68933 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-rt_3.10\": }} linux-yocto-rt_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-preempt-rt.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" +#SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.10\": }} linux-yocto-tiny_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.10.9" diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto-tiny_3.14\": }} linux-yocto-tiny_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-tiny.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" new file mode 100644 index 0000000000..162348114f --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.10\": }} linux-yocto_3.10.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "b170394a475b96ecc92cbc9e4b002bed0a9f69c5" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "c2ed0f16fdec628242a682897d5d86df4547cf24" +#LINUX_VERSION = "3.10.9" \ No newline at end of file diff --git "a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" new file mode 100644 index 0000000000..81e528bc33 --- /dev/null +++ "b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == \"linux-yocto_3.14\": }} linux-yocto_3.14.bbappend" @@ -0,0 +1,32 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +PR := "${PR}.1" + +COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" + +{{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} + +{{ if need_new_kbranch == "y": }} +{{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n": }} +{{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} + +{{ if need_new_kbranch == "n": }} +KBRANCH_{{=machine}} = "{{=existing_kbranch}}" + +{{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} +{{ if smp == "y": }} +KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" + +SRC_URI += "file://{{=machine}}-standard.scc \ + file://{{=machine}}-user-config.cfg \ + file://{{=machine}}-user-patches.scc \ + file://{{=machine}}-user-features.scc \ + " + +# uncomment and replace these SRCREVs with the real commit ids once you've had +# the appropriate changes committed to the upstream linux-yocto repo +#SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" +#SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" +#LINUX_VERSION = "3.14" diff --git a/scripts/lib/bsp/tags.py b/scripts/lib/bsp/tags.py new file mode 100644 index 0000000000..6d5feb0a59 --- /dev/null +++ b/scripts/lib/bsp/tags.py @@ -0,0 +1,47 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2012, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module provides a place to define common constants for the +# Yocto BSP Tools. +# +# AUTHORS +# Tom Zanussi +# + +OPEN_TAG = "{{" +CLOSE_TAG = "}}" +ASSIGN_TAG = "{{=" +INPUT_TAG = "input" +IF_TAG = "if" + +INDENT_STR = " " + +BLANKLINE_STR = "of.write(\"\\n\")" +NORMAL_START = "of.write" +OPEN_START = "current_file =" + +INPUT_TYPE_PROPERTY = "type" + +SRC_URI_FILE = "file://" + +GIT_CHECK_URI = "git://git.yoctoproject.org/linux-yocto-dev.git" + + + diff --git a/scripts/lib/image/__init__.py b/scripts/lib/image/__init__.py new file mode 100644 index 0000000000..1ff814e761 --- /dev/null +++ b/scripts/lib/image/__init__.py @@ -0,0 +1,22 @@ +# +# OpenEmbedded Image tools library +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# AUTHORS +# Tom Zanussi +# diff --git a/scripts/lib/image/canned-wks/directdisk.wks b/scripts/lib/image/canned-wks/directdisk.wks new file mode 100644 index 0000000000..397a929c74 --- /dev/null +++ b/scripts/lib/image/canned-wks/directdisk.wks @@ -0,0 +1,10 @@ +# short-description: Create a 'pcbios' direct disk image +# long-description: Creates a partitioned legacy BIOS disk image that the user +# can directly dd to boot media. + + +part /boot --source bootimg-pcbios --ondisk sda --fstype=msdos --label boot --active --align 1024 +part / --source rootfs --ondisk sda --fstype=ext3 --label platform --align 1024 + +bootloader --timeout=0 --append="rootwait rootfstype=ext3 video=vesafb vga=0x318 console=tty0" + diff --git a/scripts/lib/image/canned-wks/mkefidisk.wks b/scripts/lib/image/canned-wks/mkefidisk.wks new file mode 100644 index 0000000000..e976bc80dd --- /dev/null +++ b/scripts/lib/image/canned-wks/mkefidisk.wks @@ -0,0 +1,11 @@ +# short-description: Create an EFI disk image +# long-description: Creates a partitioned EFI disk image that the user +# can directly dd to boot media. + +part /boot --source bootimg-efi --ondisk sda --fstype=msdos --label msdos --active --align 1024 + +part / --source rootfs --ondisk sda --fstype=ext3 --label platform --align 1024 + +part swap --ondisk sda --size 44 --label swap1 --fstype=swap + +bootloader --timeout=10 --append="rootwait rootfstype=ext3 console=ttyPCH0,115200 console=tty0 vmalloc=256MB snd-hda-intel.enable_msi=0" diff --git a/scripts/lib/image/canned-wks/uboot.wks b/scripts/lib/image/canned-wks/uboot.wks new file mode 100644 index 0000000000..7de0572d0f --- /dev/null +++ b/scripts/lib/image/canned-wks/uboot.wks @@ -0,0 +1,17 @@ +# short-description: . Create a ramdisk image for U-Boot +# long-description: Creates a ramdisk image for U-Boot that user +# can directly load it into ram through tftp +# +# part - is a wic command that drive the process of generating a valid file system +# - --source=uboot : wic plugin that generates a ramdisk image for U-Boot +# - --fstype=ext2 : file system type( ext2 / ext3 / ext 4) +# +# %packages %end - option to provide a list of packages that will be installed +# into rootfs. All packages dependencies will be installed by +# package manager(default opkg). + + +part / --source=uboot --fstype=ext2 --label imageName --align 1024 + +%packages +%end diff --git a/scripts/lib/image/config/wic.conf b/scripts/lib/image/config/wic.conf new file mode 100644 index 0000000000..2a2750b4ee --- /dev/null +++ b/scripts/lib/image/config/wic.conf @@ -0,0 +1,11 @@ +[common] +; general settings +distro_name = OpenEmbedded + +[create] +; settings for create subcommand +; repourl=http://linux.com/ipk/all http://linux.com/ipk/target http://linux.com/ipk/arch +arch=powerpc +pkgmgr=opkg +runtime=native +install_pkgs=source diff --git a/scripts/lib/image/engine.py b/scripts/lib/image/engine.py new file mode 100644 index 0000000000..0643780f1a --- /dev/null +++ b/scripts/lib/image/engine.py @@ -0,0 +1,287 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION + +# This module implements the image creation engine used by 'wic' to +# create images. The engine parses through the OpenEmbedded kickstart +# (wks) file specified and generates images that can then be directly +# written onto media. +# +# AUTHORS +# Tom Zanussi +# + +import os +import sys +from abc import ABCMeta, abstractmethod +import shlex +import json +import subprocess +import shutil + +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 +from mic.utils.oe.misc import * + + +def verify_build_env(): + """ + Verify that the build environment is sane. + + Returns True if it is, false otherwise + """ + try: + builddir = os.environ["BUILDDIR"] + except KeyError: + print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" + sys.exit(1) + + return True + + +def find_bitbake_env_lines(image_name): + """ + If image_name is empty, plugins might still be able to use the + environment, so set it regardless. + """ + if image_name: + bitbake_env_cmd = "bitbake -e %s" % image_name + else: + bitbake_env_cmd = "bitbake -e" + rc, bitbake_env_lines = exec_cmd(bitbake_env_cmd) + if rc != 0: + print "Couldn't get '%s' output." % bitbake_env_cmd + return None + + return bitbake_env_lines + + +def find_artifacts(image_name): + """ + Gather the build artifacts for the current image (the image_name + e.g. core-image-minimal) for the current MACHINE set in local.conf + """ + bitbake_env_lines = get_bitbake_env_lines() + + rootfs_dir = kernel_dir = hdddir = staging_data_dir = native_sysroot = "" + + for line in bitbake_env_lines.split('\n'): + if (get_line_val(line, "IMAGE_ROOTFS")): + rootfs_dir = get_line_val(line, "IMAGE_ROOTFS") + continue + if (get_line_val(line, "STAGING_KERNEL_DIR")): + kernel_dir = get_line_val(line, "STAGING_KERNEL_DIR") + continue + if (get_line_val(line, "HDDDIR")): + hdddir = get_line_val(line, "HDDDIR") + continue + if (get_line_val(line, "STAGING_DATADIR")): + staging_data_dir = get_line_val(line, "STAGING_DATADIR") + continue + if (get_line_val(line, "STAGING_DIR_NATIVE")): + native_sysroot = get_line_val(line, "STAGING_DIR_NATIVE") + continue + + return (rootfs_dir, kernel_dir, hdddir, staging_data_dir, native_sysroot) + + +CANNED_IMAGE_DIR = "lib/image/canned-wks" # relative to scripts + +def find_canned_image(scripts_path, wks_file): + """ + Find a .wks file with the given name in the canned files dir. + + Return False if not found + """ + canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) + + for root, dirs, files in os.walk(canned_wks_dir): + for file in files: + if file.endswith("~") or file.endswith("#"): + continue + if file.endswith(".wks") and wks_file + ".wks" == file: + fullpath = os.path.join(canned_wks_dir, file) + return fullpath + return None + + +def list_canned_images(scripts_path): + """ + List the .wks files in the canned image dir, minus the extension. + """ + canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) + + for root, dirs, files in os.walk(canned_wks_dir): + for file in files: + if file.endswith("~") or file.endswith("#"): + continue + if file.endswith(".wks"): + fullpath = os.path.join(canned_wks_dir, file) + f = open(fullpath, "r") + lines = f.readlines() + for line in lines: + desc = "" + idx = line.find("short-description:") + if idx != -1: + desc = line[idx + len("short-description:"):].strip() + break + basename = os.path.splitext(file)[0] + print " %s\t\t%s" % (basename, desc) + + +def list_canned_image_help(scripts_path, fullpath): + """ + List the help and params in the specified canned image. + """ + canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) + + f = open(fullpath, "r") + lines = f.readlines() + found = False + for line in lines: + if not found: + idx = line.find("long-description:") + if idx != -1: + print + print line[idx + len("long-description:"):].strip() + found = True + continue + if not line.strip(): + break + idx = line.find("#") + if idx != -1: + print line[idx + len("#:"):].rstrip() + else: + break + + +def wic_create(args, wks_file, rootfs_dir, bootimg_dir, kernel_dir, + native_sysroot, hdddir, staging_data_dir, scripts_path, + image_output_dir, debug, properties_file, properties=None): + """ + Create image + + wks_file - user-defined OE kickstart file + rootfs_dir - absolute path to the build's /rootfs dir + bootimg_dir - absolute path to the build's boot artifacts directory + kernel_dir - absolute path to the build's kernel directory + native_sysroot - absolute path to the build's native sysroots dir + hdddir - absolute path to the build's HDDDIR dir + staging_data_dir - absolute path to the build's STAGING_DATA_DIR dir + scripts_path - absolute path to /scripts dir + image_output_dir - dirname to create for image + properties_file - use values from this file if nonempty i.e no prompting + properties - use values from this string if nonempty i.e no prompting + + Normally, the values for the build artifacts values are determined + by 'wic -e' from the output of the 'bitbake -e' command given an + image name e.g. 'core-image-minimal' and a given machine set in + local.conf. If that's the case, the variables get the following + values from the output of 'bitbake -e': + + rootfs_dir: IMAGE_ROOTFS + kernel_dir: STAGING_KERNEL_DIR + native_sysroot: STAGING_DIR_NATIVE + hdddir: HDDDIR + staging_data_dir: STAGING_DATA_DIR + + In the above case, bootimg_dir remains unset and the image + creation code determines which of the passed-in directories to + use. + + In the case where the values are passed in explicitly i.e 'wic -e' + is not used but rather the individual 'wic' options are used to + explicitly specify these values, hdddir and staging_data_dir will + be unset, but bootimg_dir must be explicit i.e. explicitly set to + either hdddir or staging_data_dir, depending on the image being + generated. The other values (rootfs_dir, kernel_dir, and + native_sysroot) correspond to the same values found above via + 'bitbake -e'). + + """ + try: + oe_builddir = os.environ["BUILDDIR"] + except KeyError: + print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" + sys.exit(1) + + direct_args = list() + direct_args.insert(0, oe_builddir) + direct_args.insert(0, image_output_dir) + direct_args.insert(0, wks_file) + direct_args.insert(0, rootfs_dir) + direct_args.insert(0, bootimg_dir) + direct_args.insert(0, kernel_dir) + direct_args.insert(0, native_sysroot) + direct_args.insert(0, hdddir) + direct_args.insert(0, staging_data_dir) + direct_args.insert(0, "direct") + + if debug: + msger.set_loglevel('debug') + + cr = creator.Creator() + + cr.main(direct_args) + + print "\nThe image(s) were created using OE kickstart file:\n %s" % wks_file + + +def wic_list(args, scripts_path, properties_file): + """ + Print the complete list of properties defined by the image, or the + possible values for a particular image property. + """ + if len(args) < 1: + return False + + if len(args) == 1: + if args[0] == "images": + list_canned_images(scripts_path) + return True + elif args[0] == "properties": + return True + else: + return False + + if len(args) == 2: + if args[0] == "properties": + wks_file = args[1] + print "print properties contained in wks file: %s" % wks_file + return True + elif args[0] == "property": + print "print property values for property: %s" % args[1] + return True + elif args[1] == "help": + wks_file = args[0] + fullpath = find_canned_image(scripts_path, wks_file) + if not fullpath: + print "No image named %s found, exiting. (Use 'wic list images' to list available images, or specify a fully-qualified OE kickstart (.wks) filename)\n" % wks_file + sys.exit(1) + list_canned_image_help(scripts_path, fullpath) + return True + else: + return False + + return False diff --git a/scripts/lib/image/help.py b/scripts/lib/image/help.py new file mode 100644 index 0000000000..cb3112cf08 --- /dev/null +++ b/scripts/lib/image/help.py @@ -0,0 +1,311 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module implements some basic help invocation functions along +# with the bulk of the help topic text for the OE Core Image Tools. +# +# AUTHORS +# Tom Zanussi +# + +import subprocess +import logging + + +def subcommand_error(args): + logging.info("invalid subcommand %s" % args[0]) + + +def display_help(subcommand, subcommands): + """ + Display help for subcommand. + """ + if subcommand not in subcommands: + return False + + help = subcommands.get(subcommand, subcommand_error)[2] + pager = subprocess.Popen('less', stdin=subprocess.PIPE) + pager.communicate(help) + + return True + + +def wic_help(args, usage_str, subcommands): + """ + Subcommand help dispatcher. + """ + if len(args) == 1 or not display_help(args[1], subcommands): + print(usage_str) + + +def invoke_subcommand(args, parser, main_command_usage, subcommands): + """ + Dispatch to subcommand handler borrowed from combo-layer. + Should use argparse, but has to work in 2.6. + """ + if not args: + logging.error("No subcommand specified, exiting") + parser.print_help() + elif args[0] == "help": + wic_help(args, main_command_usage, subcommands) + elif args[0] not in subcommands: + logging.error("Unsupported subcommand %s, exiting\n" % (args[0])) + parser.print_help() + else: + usage = subcommands.get(args[0], subcommand_error)[1] + subcommands.get(args[0], subcommand_error)[0](args[1:], usage) + + +## +# wic help and usage strings +## + +wic_usage = """ + + Create a customized OpenEmbedded image + + usage: wic [--version] [--help] COMMAND [ARGS] + + Current 'wic' commands are: + create Create a new OpenEmbedded image + list List available values for options and image properties + + See 'wic help COMMAND' for more information on a specific command. +""" + +wic_help_usage = """ + + usage: wic help + + This command displays detailed help for the specified subcommand. +""" + +wic_create_usage = """ + + Create a new OpenEmbedded image + + usage: wic create [-o | --outdir ] + [-i | --infile ] + [-e | --image-name] [-r, --rootfs-dir] [-b, --bootimg-dir] + [-k, --kernel-dir] [-n, --native-sysroot] [-s, --skip-build-check] + + This command creates an OpenEmbedded image based on the 'OE kickstart + commands' found in the . + + The -o option can be used to place the image in a directory with a + different name and location. + + See 'wic help create' for more detailed instructions. +""" + +wic_create_help = """ + +NAME + wic create - Create a new OpenEmbedded image + +SYNOPSIS + wic create [-o | --outdir ] + [-i | --infile ] + [-e | --image-name] [-r, --rootfs-dir] [-b, --bootimg-dir] + [-k, --kernel-dir] [-n, --native-sysroot] [-s, --skip-build-check] + +DESCRIPTION + This command creates an OpenEmbedded image based on the 'OE + kickstart commands' found in the . + + In order to do this, wic needs to know the locations of the + various build artifacts required to build the image. + + Users can explicitly specify the build artifact locations using + the -r, -b, -k, and -n options. See below for details on where + the corresponding artifacts are typically found in a normal + OpenEmbedded build. + + Alternatively, users can use the -e option to have 'mic' determine + those locations for a given image. If the -e option is used, the + user needs to have set the appropriate MACHINE variable in + local.conf, and have sourced the build environment. + + The -e option is used to specify the name of the image to use the + artifacts from e.g. core-image-sato. + + The -r option is used to specify the path to the /rootfs dir to + use as the .wks rootfs source. + + The -b option is used to specify the path to the dir containing + the boot artifacts (e.g. /EFI or /syslinux dirs) to use as the + .wks bootimg source. + + The -k option is used to specify the path to the dir containing + the kernel to use in the .wks bootimg. + + The -n option is used to specify the path to the native sysroot + containing the tools to use to build the image. + + The -s option is used to skip the build check. The build check is + a simple sanity check used to determine whether the user has + sourced the build environment so that the -e option can operate + correctly. If the user has specified the build artifact locations + explicitly, 'wic' assumes the user knows what he or she is doing + and skips the build check. + + When 'wic -e' is used, the locations for the build artifacts + values are determined by 'wic -e' from the output of the 'bitbake + -e' command given an image name e.g. 'core-image-minimal' and a + given machine set in local.conf. In that case, the image is + created as if the following 'bitbake -e' variables were used: + + -r: IMAGE_ROOTFS + -k: STAGING_KERNEL_DIR + -n: STAGING_DIR_NATIVE + -b: HDDDIR and STAGING_DATA_DIR (handlers decide which to use) + + If 'wic -e' is not used, the user needs to select the appropriate + value for -b (as well as -r, -k, and -n). + + The -o option can be used to place the image in a directory with a + different name and location. + + As an alternative to the wks file, the image-specific properties + that define the values that will be used to generate a particular + image can be specified on the command-line using the -i option and + supplying a JSON object consisting of the set of name:value pairs + needed by image creation. + + The set of properties available for a given image type can be + listed using the 'wic list' command. +""" + +wic_list_usage = """ + + List available OpenEmbedded image properties and values + + usage: wic list images + wic list help + wic list properties + wic list properties + wic list property + [-o | --outfile ] + + This command enumerates the set of available canned images as well as + help for those images. It also can be used to enumerate the complete + set of possible values for a specified option or property needed by + the image creation process. + + The first form enumerates all the available 'canned' images. + + The second form lists the detailed help information for a specific + 'canned' image. + + The third form enumerates all the possible values that exist and can + be specified in an OE kickstart (wks) file. + + The fourth form enumerates all the possible options that exist for + the set of properties specified in a given OE kickstart (ks) file. + + The final form enumerates all the possible values that exist and can + be specified for any given OE kickstart (wks) property. + + See 'wic help list' for more details. +""" + +wic_list_help = """ + +NAME + wic list - List available OpenEmbedded image properties and values + +SYNOPSIS + wic list images + wic list help + wic list properties + wic list properties + wic list property + [-o | --outfile ] + +DESCRIPTION + This command enumerates the complete set of possible values for a + specified option or property needed by the image creation process. + + This command enumerates the set of available canned images as well + as help for those images. It also can be used to enumerate the + complete set of possible values for a specified option or property + needed by the image creation process. + + The first form enumerates all the available 'canned' images. + These are actually just the set of .wks files that have been moved + into the /scripts/lib/image/canned-wks directory). + + The second form lists the detailed help information for a specific + 'canned' image. + + The third form enumerates all the possible values that exist and + can be specified in a OE kickstart (wks) file. The output of this + can be used by the third form to print the description and + possible values of a specific property. + + The fourth form enumerates all the possible options that exist for + the set of properties specified in a given OE kickstart (wks) + file. If the -o option is specified, the list of properties, in + addition to being displayed, will be written to the specified file + as a JSON object. In this case, the object will consist of the + set of name:value pairs corresponding to the (possibly nested) + dictionary of properties defined by the input statements used by + the image. Some example output for the 'list ' command: + + $ wic list test.ks + "part" : { + "mountpoint" : "/" + "fstype" : "ext3" + } + "part" : { + "mountpoint" : "/home" + "fstype" : "ext3" + "offset" : "10000" + } + "bootloader" : { + "type" : "efi" + } + . + . + . + + Each entry in the output consists of the name of the input element + e.g. "part", followed by the properties defined for that + element enclosed in braces. This information should provide + sufficient information to create a complete user interface with. + + The final form enumerates all the possible values that exist and + can be specified for any given OE kickstart (wks) property. If + the -o option is specified, the list of values for the given + property, in addition to being displayed, will be written to the + specified file as a JSON object. In this case, the object will + consist of the set of name:value pairs corresponding to the array + of property values associated with the property. + + $ wic list property part + ["mountpoint", "where the partition should be mounted"] + ["fstype", "filesytem type of the partition"] + ["ext3"] + ["ext4"] + ["btrfs"] + ["swap"] + ["offset", "offset of the partition within the image"] + +""" diff --git a/scripts/lib/mic/3rdparty/pykickstart/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib/mic/3rdparty/pykickstart/base.py b/scripts/lib/mic/3rdparty/pykickstart/base.py new file mode 100644 index 0000000000..e6c8f56f9d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/base.py @@ -0,0 +1,466 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py new file mode 100644 index 0000000000..da48ff50d5 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py @@ -0,0 +1,26 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py new file mode 100644 index 0000000000..9af9c0ff14 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py @@ -0,0 +1,40 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py new file mode 100644 index 0000000000..cf28b5c7f7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py @@ -0,0 +1,119 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py new file mode 100644 index 0000000000..e6ae71cefc --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py @@ -0,0 +1,55 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py new file mode 100644 index 0000000000..b227fac3be --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py @@ -0,0 +1,265 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py new file mode 100644 index 0000000000..a8089fcb99 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py @@ -0,0 +1,86 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/device.py b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py new file mode 100644 index 0000000000..321410e2e2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py @@ -0,0 +1,125 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py new file mode 100644 index 0000000000..9f462fdff7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py @@ -0,0 +1,40 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py new file mode 100644 index 0000000000..6a12d58ec2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py @@ -0,0 +1,68 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py new file mode 100644 index 0000000000..993575a041 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py @@ -0,0 +1,91 @@ +# +# Chris Lumens +# Peter Jones +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py new file mode 100644 index 0000000000..82a58c0e28 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py @@ -0,0 +1,184 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py new file mode 100644 index 0000000000..33208499b3 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py @@ -0,0 +1,114 @@ +# +# Hans de Goede +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py new file mode 100644 index 0000000000..24a01bd610 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py @@ -0,0 +1,193 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py new file mode 100644 index 0000000000..05c0ac11c6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py @@ -0,0 +1,62 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/group.py b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py new file mode 100644 index 0000000000..80ba5bdca6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py @@ -0,0 +1,88 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py new file mode 100644 index 0000000000..676d080836 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py @@ -0,0 +1,139 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py new file mode 100644 index 0000000000..fa3dc025b1 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py @@ -0,0 +1,58 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py new file mode 100644 index 0000000000..da5a544e86 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py @@ -0,0 +1,133 @@ +# +# Chris Lumens +# Peter Jones +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py new file mode 100644 index 0000000000..a87d0637d6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py @@ -0,0 +1,54 @@ +# +# Chris Lumens +# Peter Jones +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/key.py b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py new file mode 100644 index 0000000000..c20c4231f6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py @@ -0,0 +1,64 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py new file mode 100644 index 0000000000..babc2acd4c --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py @@ -0,0 +1,55 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py new file mode 100644 index 0000000000..cf5e46cda7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py @@ -0,0 +1,60 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py new file mode 100644 index 0000000000..73a9e537a9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py @@ -0,0 +1,58 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py new file mode 100644 index 0000000000..92b3f930b6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py @@ -0,0 +1,54 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py new file mode 100644 index 0000000000..698561994d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py @@ -0,0 +1,66 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py new file mode 100644 index 0000000000..c1b9cc3a61 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py @@ -0,0 +1,304 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py new file mode 100644 index 0000000000..388823a839 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py @@ -0,0 +1,53 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/method.py b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py new file mode 100644 index 0000000000..e21064acda --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py @@ -0,0 +1,186 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py new file mode 100644 index 0000000000..8c8c2c4fc9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py @@ -0,0 +1,106 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py new file mode 100644 index 0000000000..c643bcedc3 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py @@ -0,0 +1,70 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py new file mode 100644 index 0000000000..84ba755e68 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py @@ -0,0 +1,111 @@ +# +# Chris Lumens +# Peter Jones +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/network.py b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py new file mode 100644 index 0000000000..9b67f92831 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py @@ -0,0 +1,363 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py new file mode 100644 index 0000000000..e65e012d02 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py @@ -0,0 +1,353 @@ +# +# Chris Lumens +# +# 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): + if self.mountpoint: + return self.mountpoint == y.mountpoint + else: + return False + + 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) + if self.mountpoint: + mountpoint_str = "%s" % self.mountpoint + else: + mountpoint_str = "(No mount point)" + retval += "part %s%s\n" % (mountpoint_str, 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) + + pd = self.handler.PartData() + self._setToObj(self.op, opts, pd) + pd.lineno = self.lineno + if extra: + pd.mountpoint = extra[0] + if pd in self.dataList(): + warnings.warn(_("A partition with the mountpoint %s has already been defined.") % pd.mountpoint) + else: + pd.mountpoint = None + + 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/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py new file mode 100644 index 0000000000..0f4c92a107 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py @@ -0,0 +1,365 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py new file mode 100644 index 0000000000..391af14c22 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py @@ -0,0 +1,79 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py new file mode 100644 index 0000000000..543ef947c1 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py @@ -0,0 +1,249 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py new file mode 100644 index 0000000000..1893d4ea49 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py @@ -0,0 +1,68 @@ +# +# Alexander Todorov +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py new file mode 100644 index 0000000000..e038b4525d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py @@ -0,0 +1,93 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py new file mode 100644 index 0000000000..9f8059c76b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py @@ -0,0 +1,64 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/services.py b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py new file mode 100644 index 0000000000..2e0eab8007 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py @@ -0,0 +1,71 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py new file mode 100644 index 0000000000..36d1a8d5ba --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py @@ -0,0 +1,54 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py new file mode 100644 index 0000000000..e7867ebfb2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py @@ -0,0 +1,105 @@ +# +# Peter Jones +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py new file mode 100644 index 0000000000..f5441de593 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py @@ -0,0 +1,86 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py new file mode 100644 index 0000000000..53ec49f7b8 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py @@ -0,0 +1,60 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py new file mode 100644 index 0000000000..a68a82d378 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py @@ -0,0 +1,106 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/user.py b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py new file mode 100644 index 0000000000..189dc7585f --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py @@ -0,0 +1,173 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py new file mode 100644 index 0000000000..200ccfba2e --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py @@ -0,0 +1,114 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py new file mode 100644 index 0000000000..255c47f0ae --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py @@ -0,0 +1,102 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py new file mode 100644 index 0000000000..644ee86743 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py @@ -0,0 +1,184 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py new file mode 100644 index 0000000000..79555a9b27 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py @@ -0,0 +1,69 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py new file mode 100644 index 0000000000..1ed2694c89 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py @@ -0,0 +1,134 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/constants.py b/scripts/lib/mic/3rdparty/pykickstart/constants.py new file mode 100644 index 0000000000..5e12fc80ec --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/constants.py @@ -0,0 +1,57 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/errors.py b/scripts/lib/mic/3rdparty/pykickstart/errors.py new file mode 100644 index 0000000000..a234d99d43 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/errors.py @@ -0,0 +1,103 @@ +# +# errors.py: Kickstart error handling. +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py new file mode 100644 index 0000000000..d8c8f2b899 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py @@ -0,0 +1,1307 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py new file mode 100644 index 0000000000..17c8211bbf --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py new file mode 100644 index 0000000000..d21aee3e8b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py new file mode 100644 index 0000000000..cea3ecef6b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py new file mode 100644 index 0000000000..b94c738f79 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py new file mode 100644 index 0000000000..478f75d15e --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py new file mode 100644 index 0000000000..12aecb4c1a --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py new file mode 100644 index 0000000000..3c52f8d754 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py new file mode 100644 index 0000000000..5e856ea983 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py new file mode 100644 index 0000000000..1a978810f4 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py new file mode 100644 index 0000000000..116f1b57c9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py new file mode 100644 index 0000000000..a115dc2646 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py new file mode 100644 index 0000000000..fd47b732ef --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py new file mode 100644 index 0000000000..bcdc29d23a --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py new file mode 100644 index 0000000000..c83a929f84 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py new file mode 100644 index 0000000000..131763c2a8 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py new file mode 100644 index 0000000000..3496c43ea5 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py new file mode 100644 index 0000000000..abb7a8d36c --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py new file mode 100644 index 0000000000..7202419780 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py @@ -0,0 +1,24 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/ko.py b/scripts/lib/mic/3rdparty/pykickstart/ko.py new file mode 100644 index 0000000000..1350d19c70 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/ko.py @@ -0,0 +1,37 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/options.py b/scripts/lib/mic/3rdparty/pykickstart/options.py new file mode 100644 index 0000000000..341c5d7298 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/options.py @@ -0,0 +1,204 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/parser.py b/scripts/lib/mic/3rdparty/pykickstart/parser.py new file mode 100644 index 0000000000..840a448673 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/parser.py @@ -0,0 +1,702 @@ +# +# parser.py: Kickstart file parser. +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/sections.py b/scripts/lib/mic/3rdparty/pykickstart/sections.py new file mode 100644 index 0000000000..44df856b8d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/sections.py @@ -0,0 +1,244 @@ +# +# sections.py: Kickstart file sections. +# +# Chris Lumens +# +# 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/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py new file mode 100644 index 0000000000..7bcd9d5541 --- /dev/null +++ b/scripts/lib/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 , ' \ + 'Ryan Tomayko ' +__url__ = 'http://linux.duke.edu/projects/urlgrabber/' + +from grabber import urlgrab, urlopen, urlread diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py new file mode 100644 index 0000000000..001b4e32d6 --- /dev/null +++ b/scripts/lib/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=-". 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=-" 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/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py new file mode 100644 index 0000000000..fefdab36f6 --- /dev/null +++ b/scripts/lib/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 and make a local copy at + 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, XXXXXXXX', user) + 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 and make a local copy at + 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], \ + ' [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], \ + ' [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/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py new file mode 100644 index 0000000000..71393e2b8d --- /dev/null +++ b/scripts/lib/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 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 " % sys.argv[0] + else: + test(url, N) diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py new file mode 100644 index 0000000000..9664c6b5c5 --- /dev/null +++ b/scripts/lib/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': , + 'kwargs': { }} + + 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/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py new file mode 100644 index 0000000000..02db524e76 --- /dev/null +++ b/scripts/lib/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/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py new file mode 100644 index 0000000000..07848dac7c --- /dev/null +++ b/scripts/lib/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/scripts/lib/mic/3rdparty/pykickstart/version.py b/scripts/lib/mic/3rdparty/pykickstart/version.py new file mode 100644 index 0000000000..102cc37d80 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/version.py @@ -0,0 +1,197 @@ +# +# Chris Lumens +# +# 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/scripts/lib/mic/__init__.py b/scripts/lib/mic/__init__.py new file mode 100644 index 0000000000..63c1d9c846 --- /dev/null +++ b/scripts/lib/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/scripts/lib/mic/__version__.py b/scripts/lib/mic/__version__.py new file mode 100644 index 0000000000..60d7626cac --- /dev/null +++ b/scripts/lib/mic/__version__.py @@ -0,0 +1 @@ +VERSION = "0.14" diff --git a/scripts/lib/mic/bootstrap.py b/scripts/lib/mic/bootstrap.py new file mode 100644 index 0000000000..66c291b0a8 --- /dev/null +++ b/scripts/lib/mic/bootstrap.py @@ -0,0 +1,279 @@ +#!/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 sys +import tempfile +import shutil +import subprocess +import rpm +from mic import msger +from mic.utils import errors, proxy, misc +from mic.utils.rpmmisc import readRpmHeader, RPMInstallCallback +from mic.chroot import cleanup_mounts, setup_chrootenv, cleanup_chrootenv + +PATH_BOOTSTRAP = "/usr/sbin:/usr/bin:/sbin:/bin" + +RPMTRANS_FLAGS = [ + rpm.RPMTRANS_FLAG_ALLFILES, + rpm.RPMTRANS_FLAG_NOSCRIPTS, + rpm.RPMTRANS_FLAG_NOTRIGGERS, + ] + +RPMVSF_FLAGS = [ + rpm._RPMVSF_NOSIGNATURES, + rpm._RPMVSF_NODIGESTS + ] + +RPMPROB_FLAGS = [ + rpm.RPMPROB_FILTER_OLDPACKAGE, + rpm.RPMPROB_FILTER_REPLACEPKG, + rpm.RPMPROB_FILTER_IGNOREARCH + ] + +class MiniBackend(object): + def __init__(self, rootdir, arch=None, repomd=None): + self._ts = None + self.rootdir = os.path.abspath(rootdir) + self.arch = arch + self.repomd = repomd + self.dlpkgs = [] + self.localpkgs = {} + self.optionals = [] + self.preins = {} + self.postins = {} + self.scriptlets = False + + def __del__(self): + try: + del self.ts + except: + pass + + def get_ts(self): + if not self._ts: + self._ts = rpm.TransactionSet(self.rootdir) + self._ts.setFlags(reduce(lambda x, y: x|y, RPMTRANS_FLAGS)) + self._ts.setVSFlags(reduce(lambda x, y: x|y, RPMVSF_FLAGS)) + self._ts.setProbFilter(reduce(lambda x, y: x|y, RPMPROB_FLAGS)) + + return self._ts + + def del_ts(self): + if self._ts: + self._ts.closeDB() + self._ts = None + + ts = property(fget = lambda self: self.get_ts(), + fdel = lambda self: self.del_ts(), + doc="TransactionSet object") + + def selectPackage(self, pkg): + if not pkg in self.dlpkgs: + self.dlpkgs.append(pkg) + + def runInstall(self): + # FIXME: check space + self.downloadPkgs() + self.installPkgs() + + if not self.scriptlets: + return + + for pkg in self.preins.keys(): + prog, script = self.preins[pkg] + self.run_pkg_script(pkg, prog, script, '0') + for pkg in self.postins.keys(): + prog, script = self.postins[pkg] + self.run_pkg_script(pkg, prog, script, '1') + + def downloadPkgs(self): + nonexist = [] + for pkg in self.dlpkgs: + localpth = misc.get_package(pkg, self.repomd, self.arch) + if localpth: + self.localpkgs[pkg] = localpth + elif pkg in self.optionals: + # skip optional rpm + continue + else: + # mark nonexist rpm + nonexist.append(pkg) + + if nonexist: + raise errors.BootstrapError("Can't get rpm binary: %s" % + ','.join(nonexist)) + + def installPkgs(self): + for pkg in self.localpkgs.keys(): + rpmpath = self.localpkgs[pkg] + + hdr = readRpmHeader(self.ts, rpmpath) + + # save prein and postin scripts + self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN']) + self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN']) + + # mark pkg as install + self.ts.addInstall(hdr, rpmpath, 'u') + + # run transaction + self.ts.order() + cb = RPMInstallCallback(self.ts) + self.ts.run(cb.callback, '') + + def run_pkg_script(self, pkg, prog, script, arg): + mychroot = lambda: os.chroot(self.rootdir) + + if not script: + return + + if prog == "": + prog = "/usr/bin/lua" + + tmpdir = os.path.join(self.rootdir, "tmp") + if not os.path.exists(tmpdir): + os.makedirs(tmpdir) + tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg) + script = script.replace('\r', '') + os.write(tmpfd, script) + os.close(tmpfd) + os.chmod(tmpfp, 0700) + + try: + script_fp = os.path.join('/tmp', os.path.basename(tmpfp)) + subprocess.call([prog, script_fp, arg], preexec_fn=mychroot) + except (OSError, IOError), err: + msger.warning(str(err)) + finally: + os.unlink(tmpfp) + +class Bootstrap(object): + def __init__(self, rootdir, distro, arch=None): + self.rootdir = misc.mkdtemp(dir=rootdir, prefix=distro) + self.distro = distro + self.arch = arch + self.logfile = None + self.pkgslist = [] + self.repomd = None + + def __del__(self): + self.cleanup() + + def get_rootdir(self): + if os.path.exists(self.rootdir): + shutil.rmtree(self.rootdir, ignore_errors=True) + os.makedirs(self.rootdir) + return self.rootdir + + def dirsetup(self, rootdir=None): + _path = lambda pth: os.path.join(rootdir, pth.lstrip('/')) + + if not rootdir: + rootdir = self.rootdir + + try: + # make /tmp and /etc path + tmpdir = _path('/tmp') + if not os.path.exists(tmpdir): + os.makedirs(tmpdir) + etcdir = _path('/etc') + if not os.path.exists(etcdir): + os.makedirs(etcdir) + + # touch distro file + tzdist = _path('/etc/%s-release' % self.distro) + if not os.path.exists(tzdist): + with open(tzdist, 'w') as wf: + wf.write("bootstrap") + except: + pass + + def create(self, repomd, pkglist, optlist=()): + try: + pkgmgr = MiniBackend(self.get_rootdir()) + pkgmgr.arch = self.arch + pkgmgr.repomd = repomd + pkgmgr.optionals = list(optlist) + map(pkgmgr.selectPackage, pkglist + list(optlist)) + pkgmgr.runInstall() + except (OSError, IOError, errors.CreatorError), err: + raise errors.BootstrapError("%s" % err) + + def run(self, cmd, chdir, rootdir=None, bindmounts=None): + def mychroot(): + os.chroot(rootdir) + os.chdir(chdir) + + def sync_timesetting(rootdir): + try: + # sync time and zone info to bootstrap + if os.path.exists(rootdir + "/etc/localtime"): + os.unlink(rootdir + "/etc/localtime") + shutil.copyfile("/etc/localtime", rootdir + "/etc/localtime") + except: + pass + + def sync_passwdfile(rootdir): + try: + # sync passwd file to bootstrap, saving the user info + if os.path.exists(rootdir + "/etc/passwd"): + os.unlink(rootdir + "/etc/passwd") + shutil.copyfile("/etc/passwd", rootdir + "/etc/passwd") + except: + pass + + if not rootdir: + rootdir = self.rootdir + + if isinstance(cmd, list): + shell = False + else: + shell = True + + env = os.environ + env['PATH'] = "%s:%s" % (PATH_BOOTSTRAP, env['PATH']) + + retcode = 0 + gloablmounts = None + try: + proxy.set_proxy_environ() + gloablmounts = setup_chrootenv(rootdir, bindmounts, False) + sync_timesetting(rootdir) + sync_passwdfile(rootdir) + retcode = subprocess.call(cmd, preexec_fn=mychroot, env=env, shell=shell) + except (OSError, IOError): + # add additional information to original exception + value, tb = sys.exc_info()[1:] + value = '%s: %s' % (value, ' '.join(cmd)) + raise RuntimeError, value, tb + finally: + if self.logfile and os.path.isfile(self.logfile): + msger.log(file(self.logfile).read()) + cleanup_chrootenv(rootdir, bindmounts, gloablmounts) + proxy.unset_proxy_environ() + return retcode + + def cleanup(self): + try: + # clean mounts + cleanup_mounts(self.rootdir) + # remove rootdir + shutil.rmtree(self.rootdir, ignore_errors=True) + except: + pass diff --git a/scripts/lib/mic/chroot.py b/scripts/lib/mic/chroot.py new file mode 100644 index 0000000000..99fb9a2c17 --- /dev/null +++ b/scripts/lib/mic/chroot.py @@ -0,0 +1,343 @@ +#!/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") + abs_chrootdir = os.path.abspath(chrootdir) + mounts = open('/proc/mounts').readlines() + for line in reversed(mounts): + if abs_chrootdir not in line: + continue + + point = line.split()[1] + + # '/' to avoid common name prefix + if abs_chrootdir == point or point.startswith(abs_chrootdir + '/'): + args = [ umountcmd, "-l", point ] + ret = runner.quiet(args) + if ret != 0: + msger.warning("failed to unmount %s" % point) + + return 0 + +def setup_chrootenv(chrootdir, bindmounts = None, mountparent = True): + global chroot_lockfd, chroot_lock + + def get_bind_mounts(chrootdir, bindmounts, mountparent = True): + 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 some bindmount is not existed, but it's created inside + # chroot, this is not expected + if not os.path.exists(srcdst[0]): + os.makedirs(srcdst[0]) + + if not os.path.isdir(srcdst[0]): + continue + + if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/': + msger.verbose("%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: + if not os.path.exists(pt): + continue + chrootmounts.append(fs_related.BindChrootMount(pt, + chrootdir, + None)) + + if mountparent: + 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.verbose("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, mountparent) + 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): + for b in reversed(chrootmounts): + msger.verbose("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[0] == "/": + continue + + 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 os.path.exists(tmpdir) and 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/scripts/lib/mic/conf.py b/scripts/lib/mic/conf.py new file mode 100644 index 0000000000..b850d80520 --- /dev/null +++ b/scripts/lib/mic/conf.py @@ -0,0 +1,197 @@ +#!/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 + +from mic import msger +from mic import kickstart +from mic.utils import misc, runner, proxy, errors + + +def get_siteconf(): + mic_path = os.path.dirname(__file__) + eos = mic_path.find('scripts') + len('scripts') + scripts_path = mic_path[:eos] + + return scripts_path + "/lib/image/config/wic.conf" + +class ConfigMgr(object): + prefer_backends = ["zypp", "yum"] + + DEFAULTS = {'common': { + "distro_name": "Default Distribution", + "plugin_dir": "/usr/lib/wic/plugins", # TODO use prefix also? + }, + 'create': { + "tmpdir": '/var/tmp/wic', + "cachedir": '/var/tmp/wic/cache', + "outdir": './wic-output', + + "arch": None, # None means auto-detect + "pkgmgr": "auto", + "name": "output", + "ksfile": None, + "ks": None, + "repomd": None, + "local_pkgs_path": None, + "release": None, + "logfile": None, + "record_pkgs": [], + "pack_to": None, + "name_prefix": None, + "name_suffix": None, + "proxy": None, + "no_proxy": None, + "copy_kernel": False, + "install_pkgs": None, + "repourl": {}, + "localrepos": [], # save localrepos + "runtime": "bootstrap", + }, + 'chroot': { + "saveto": None, + }, + 'convert': { + "shell": False, + }, + 'bootstrap': { + "rootdir": '/var/tmp/wic-bootstrap', + "packages": [], + }, + } + + # 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": + 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']) + + # bootstrap option handling + self.set_runtime(self.create['runtime']) + if isinstance(self.bootstrap['packages'], basestring): + packages = self.bootstrap['packages'].replace('\n', ' ') + if packages.find(',') != -1: + packages = packages.split(',') + else: + packages = packages.split() + self.bootstrap['packages'] = packages + + def _parse_kickstart(self, ksconf=None): + if not ksconf: + return + + ksconf = misc.normalize_ksfile(ksconf, + self.create['release'], + self.create['arch']) + + ks = kickstart.read_kickstart(ksconf) + + self.create['ks'] = ks + self.create['name'] = os.path.splitext(os.path.basename(ksconf))[0] + + self.create['name'] = misc.build_name(ksconf, + self.create['release'], + self.create['name_prefix'], + self.create['name_suffix']) + + def set_runtime(self, runtime): + if runtime not in ("bootstrap", "native"): + msger.error("Invalid runtime mode: %s" % runtime) + + if misc.get_distro()[0] in ("tizen", "Tizen"): + runtime = "native" + self.create['runtime'] = runtime + +configmgr = ConfigMgr() diff --git a/scripts/lib/mic/creator.py b/scripts/lib/mic/creator.py new file mode 100644 index 0000000000..267928f877 --- /dev/null +++ b/scripts/lib/mic/creator.py @@ -0,0 +1,351 @@ +#!/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 +from mic.conf import configmgr +from mic.plugin import pluginmgr + + +class Creator(cmdln.Cmdln): + """${name}: create an image + + Usage: + ${name} SUBCOMMAND [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", "vcs"') + 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, native') + # --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.') + optparser.add_option('', '--install-pkgs', type='string', action='store', + dest='install_pkgs', default=None, + help='Specify what type of packages to be installed,' + ' valid: source, debuginfo, debugsource') + optparser.add_option('', '--tmpfs', action='store_true', dest='enabletmpfs', + help='Setup tmpdir as tmpfs to accelerate, experimental' + ' feature, use it if you have more than 4G memory') + optparser.add_option('', '--repourl', action='append', + dest='repourl', default=[], + help=SUPPRESS_HELP) + 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: + logfile_abs_path = abspath(self.options.logfile) + if os.path.isdir(logfile_abs_path): + raise errors.Usage("logfile's path %s should be file" + % self.options.logfile) + if not os.path.exists(os.path.dirname(logfile_abs_path)): + os.makedirs(os.path.dirname(logfile_abs_path)) + msger.set_interactive(False) + msger.set_logfile(logfile_abs_path) + 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'] + + for cdir in ('outdir', 'cachedir'): + if os.path.exists(configmgr.create[cdir]) \ + and not os.path.isdir(configmgr.create[cdir]): + msger.error('Invalid directory specified: %s' \ + % configmgr.create[cdir]) + + 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.rstrip('/') + + if self.options.record_pkgs: + configmgr.create['record_pkgs'] = [] + for infotype in self.options.record_pkgs.split(','): + if infotype not in ('name', 'content', 'license', 'vcs'): + raise errors.Usage('Invalid pkg recording: %s, valid ones:' + ' "name", "content", "license", "vcs"' \ + % 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.set_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 + + if self.options.install_pkgs: + configmgr.create['install_pkgs'] = [] + for pkgtype in self.options.install_pkgs.split(','): + if pkgtype not in ('source', 'debuginfo', 'debugsource'): + raise errors.Usage('Invalid parameter specified: "%s", ' + 'valid values: source, debuginfo, ' + 'debusource' % pkgtype) + + configmgr.create['install_pkgs'].append(pkgtype) + + if self.options.enabletmpfs: + configmgr.create['enabletmpfs'] = self.options.enabletmpfs + + if self.options.repourl: + for item in self.options.repourl: + try: + key, val = item.split('=') + except: + continue + configmgr.create['repourl'][key] = val + + 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() + + return self.cmd(args) + + def precmd(self, argv): # check help before cmd + + if '-h' in argv or '?' in argv or '--help' in argv or 'help' in argv: + return argv + + if len(argv) == 1: + return ['help', argv[0]] + + return argv + + def do_auto(self, subcmd, opts, *args): + """${cmd_name}: auto detect image type from magic header + + Usage: + ${name} ${cmd_name} + + ${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\w+)', inline_argv) + elif ptype == 'mic2': + m2 = re.search('(-f|--format(=)?)\s*(?P\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 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/scripts/lib/mic/imager/__init__.py b/scripts/lib/mic/imager/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib/mic/imager/baseimager.py b/scripts/lib/mic/imager/baseimager.py new file mode 100644 index 0000000000..b7212493b4 --- /dev/null +++ b/scripts/lib/mic/imager/baseimager.py @@ -0,0 +1,1263 @@ +#!/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 + +from mic import kickstart +from mic import msger +from mic.utils.errors import CreatorError, Abort +from mic.utils import misc, 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/wic" + self.cachedir = "/var/tmp/wic/cache" + self.workdir = "/var/tmp/wic/build" + + self.destdir = "." + self.installerfw_prefix = "INSTALLERFW_" + self.target_arch = "noarch" + self._local_pkgs_path = None + self.pack_to = None + self.repourl = {} + + # If the kernel is save to the destdir when copy_kernel cmd is called. + self._need_copy_kernel = False + # setup tmpfs tmpdir when enabletmpfs is True + self.enabletmpfs = 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'] + '_' + self.name + + 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"] + + # 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) + + content = None + if 'vcs' in self._recording_pkgs: + vcslst = ["%s %s" % (k, v) for (k, v) in self._pkgs_vcsinfo.items()] + content = '\n'.join(sorted(vcslst)) + elif 'name' in self._recording_pkgs: + content = '\n'.join(pkgs) + if content: + namefile = os.path.join(destdir, self.name + '.packages') + f = open(namefile, "w") + 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] + content += ' ' + content += '\n '.join(pkgcont) + 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 _set_part_env(self, pnum, prop, value): + """ This is a helper function which generates an environment variable + for a property "prop" with value "value" of a partition number "pnum". + + The naming convention is: + * Variables start with INSTALLERFW_PART + * Then goes the partition number, the order is the same as + specified in the KS file + * Then goes the property name + """ + + if value == None: + value = "" + else: + value = str(value) + + name = self.installerfw_prefix + ("PART%d_" % pnum) + prop + return { name : value } + + 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. + + in_chroot -- whether this %post script is to be executed chroot()ed + into _instroot. + """ + + env = {} + pnum = 0 + + for p in kickstart.get_partitions(self.ks): + env.update(self._set_part_env(pnum, "SIZE", p.size)) + env.update(self._set_part_env(pnum, "MOUNTPOINT", p.mountpoint)) + env.update(self._set_part_env(pnum, "FSTYPE", p.fstype)) + env.update(self._set_part_env(pnum, "LABEL", p.label)) + env.update(self._set_part_env(pnum, "FSOPTS", p.fsopts)) + env.update(self._set_part_env(pnum, "BOOTFLAG", p.active)) + env.update(self._set_part_env(pnum, "ALIGN", p.align)) + env.update(self._set_part_env(pnum, "TYPE_ID", p.part_type)) + env.update(self._set_part_env(pnum, "DEVNODE", + "/dev/%s%d" % (p.disk, pnum + 1))) + pnum += 1 + + # Count of paritions + env[self.installerfw_prefix + "PART_COUNT"] = str(pnum) + + # Partition table format + ptable_format = self.ks.handler.bootloader.ptable + env[self.installerfw_prefix + "PTABLE_FORMAT"] = ptable_format + + # The kerned boot parameters + kernel_opts = self.ks.handler.bootloader.appendLine + env[self.installerfw_prefix + "KERNEL_OPTS"] = kernel_opts + + # Name of the distribution + env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name + + # Name of the image creation tool + env[self.installerfw_prefix + "INSTALLER_NAME"] = "wic" + + # The real current location of the mounted file-systems + if in_chroot: + mount_prefix = "/" + else: + mount_prefix = self._instroot + env[self.installerfw_prefix + "MOUNT_PREFIX"] = mount_prefix + + # These are historical variables which lack the common name prefix + if not in_chroot: + env["INSTALL_ROOT"] = self._instroot + env["IMG_NAME"] = self._name + + return env + + 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.workdir = os.path.join(self.tmpdir, "build") + if not os.path.exists(self.workdir): + os.makedirs(self.workdir) + self.__builddir = tempfile.mkdtemp(dir = self.workdir, + 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 + "/wic-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_contents = self._get_fstab() + if fstab_contents: + fstab = open(self._instroot + "/etc/fstab", "w") + fstab.write(fstab_contents) + 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 __setup_tmpdir(self): + if not self.enabletmpfs: + return + + runner.show('mount -t tmpfs -o size=4G tmpfs %s' % self.workdir) + + def __clean_tmpdir(self): + if not self.enabletmpfs: + return + + runner.show('umount -l %s' % self.workdir) + + 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.__setup_tmpdir() + self.__ensure_builddir() + + self._mount_instroot(base_on) + + 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. + + """ + self._unmount_instroot() + + + 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 + + self.__clean_tmpdir() + + 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 = grabber.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=None): + """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() + + if hasattr(self, 'install_pkgs') and self.install_pkgs: + if 'debuginfo' in self.install_pkgs: + pkg_manager.install_debuginfo = True + + 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, nocache, + cost, priority) = repo + + yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy, + proxy_username, proxy_password, inc, exc, ssl_verify, + nocache, 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: + 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 + except KeyboardInterrupt: + raise + else: + self._pkgs_content = pkg_manager.getAllContent() + self._pkgs_license = pkg_manager.getPkgsLicense() + self._pkgs_vcsinfo = pkg_manager.getVcsInfo() + self.__attachment_packages(pkg_manager) + finally: + 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: + 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, self.repourl) + 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 MD5SUMS + with open(_rpath("MD5SUMS"), "w") as wf: + for f in os.listdir(destdir): + if f == "MD5SUMS": + 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 MD5SUMS can be used by users + wf.write("%s *%s\n" % (md5sum, f)) + + outimages.append("%s/MD5SUMS" % 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/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py new file mode 100644 index 0000000000..fef9d0ed32 --- /dev/null +++ b/scripts/lib/mic/imager/direct.py @@ -0,0 +1,384 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'direct' image creator class for 'wic', based +# loosely on the raw image creator from 'mic' +# +# AUTHORS +# Tom Zanussi +# + +import os +import stat +import shutil + +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 mic.imager.baseimager import BaseImageCreator +from mic.utils.oe.misc import * +from mic.plugin import pluginmgr + +disk_methods = { + "do_install_disk":None, +} + +class DirectImageCreator(BaseImageCreator): + """ + Installs a system into a file containing a partitioned disk image. + + DirectImageCreator is an advanced ImageCreator subclass; an image + file is formatted with a partition table, each partition created + from a rootfs or other OpenEmbedded build artifact and dd'ed into + the virtual disk. The disk image can subsequently be dd'ed onto + media and used on actual hardware. + """ + + def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, + kernel_dir, native_sysroot, hdddir, staging_data_dir, + creatoropts=None, pkgmgr=None, compress_image=None, + generate_bmap=None, fstab_entry="uuid"): + """ + Initialize a DirectImageCreator instance. + + This method takes the same arguments as ImageCreator.__init__() + """ + BaseImageCreator.__init__(self, creatoropts, pkgmgr) + + self.__instimage = None + self.__imgdir = None + self.__disks = {} + self.__disk_format = "direct" + self._disk_names = [] + self._ptable_format = self.ks.handler.bootloader.ptable + self.use_uuid = fstab_entry == "uuid" + self.compress_image = compress_image + self.bmap_needed = generate_bmap + + self.oe_builddir = oe_builddir + if image_output_dir: + self.tmpdir = image_output_dir + self.cachedir = "%s/cache" % image_output_dir + self.rootfs_dir = rootfs_dir + self.bootimg_dir = bootimg_dir + self.kernel_dir = kernel_dir + self.native_sysroot = native_sysroot + self.hdddir = hdddir + self.staging_data_dir = staging_data_dir + + def __write_fstab(self, image_rootfs): + """overriden to generate fstab (temporarily) in rootfs. This + is called from mount_instroot, make sure it doesn't get called + from BaseImage.mount()""" + if image_rootfs is None: + return None + + fstab = image_rootfs + "/etc/fstab" + if not os.path.isfile(fstab): + return None + + parts = self._get_parts() + + self._save_fstab(fstab) + fstab_lines = self._get_fstab(fstab, parts) + self._update_fstab(fstab_lines, parts) + self._write_fstab(fstab, fstab_lines) + + return fstab + + def _update_fstab(self, fstab_lines, parts): + """Assume partition order same as in wks""" + for num, p in enumerate(parts, 1): + if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot": + continue + if self._ptable_format == 'msdos' and num > 3: + device_name = "/dev/" + p.disk + str(num + 1) + else: + device_name = "/dev/" + p.disk + str(num) + fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n" + fstab_lines.append(fstab_entry) + + def _write_fstab(self, fstab, fstab_lines): + fstab = open(fstab, "w") + for line in fstab_lines: + fstab.write(line) + fstab.close() + + def _save_fstab(self, fstab): + """Save the current fstab in rootfs""" + shutil.copyfile(fstab, fstab + ".orig") + + def _restore_fstab(self, fstab): + """Restore the saved fstab in rootfs""" + if fstab is None: + return + shutil.move(fstab + ".orig", fstab) + + def _get_fstab(self, fstab, parts): + """Return the desired contents of /etc/fstab.""" + f = open(fstab, "r") + fstab_contents = f.readlines() + f.close() + + return fstab_contents + + def set_bootimg_dir(self, bootimg_dir): + """ + Accessor for bootimg_dir, the actual location used for the source + of the bootimg. Should be set by source plugins (only if they + change the default bootimg source) so the correct info gets + displayed for print_outimage_info(). + """ + self.bootimg_dir = bootimg_dir + + 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_disk_names(self): + """ Returns a list of physical target disk names (e.g., 'sdb') which + will be created. """ + + if self._disk_names: + return self._disk_names + + #get partition info from ks handler + parts = self._get_parts() + + for i in range(len(parts)): + if parts[i].disk: + disk_name = parts[i].disk + else: + raise CreatorError("Failed to create disks, no --ondisk " + "specified in partition line of ks file") + + if parts[i].mountpoint and not parts[i].fstype: + raise CreatorError("Failed to create disks, no --fstype " + "specified for partition with mountpoint " + "'%s' in the ks file") + + self._disk_names.append(disk_name) + + return self._disk_names + + def _full_name(self, name, extention): + """ Construct full file name for a file we generate. """ + return "%s-%s.%s" % (self.name, name, extention) + + def _full_path(self, path, name, extention): + """ Construct full file path to a file we generate. """ + return os.path.join(path, self._full_name(name, extention)) + + def get_default_source_plugin(self): + """ + The default source plugin i.e. the plugin that's consulted for + overall image generation tasks outside of any particular + partition. For convenience, we just hang it off the + bootloader handler since it's the one non-partition object in + any setup. By default the default plugin is set to the same + plugin as the /boot partition; since we hang it off the + bootloader object, the default can be explicitly set using the + --source bootloader param. + """ + return self.ks.handler.bootloader.source + + # + # Actual implemention + # + def _mount_instroot(self, base_on = None): + """ + For 'wic', we already have our build artifacts and don't want + to loop mount anything to install into, we just create + filesystems from the artifacts directly and combine them into + a partitioned image. + + We still want to reuse as much of the basic mic machinery + though; despite the fact that we don't actually do loop or any + other kind of mounting we still want to do many of the same + things to prepare images, so we basically just adapt to the + basic framework and reinterpret what 'mounting' means in our + context. + + _instroot would normally be something like + /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for + installing packages, etc. We don't currently need to do that, + so we simplify life by just using /var/tmp/wic/build as our + workdir. + """ + parts = self._get_parts() + + self.__instimage = PartitionedMount(self._instroot) + + for p in parts: + # as a convenience, set source to the boot partition source + # instead of forcing it to be set via bootloader --source + if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": + self.ks.handler.bootloader.source = p.source + + for p in parts: + # need to create the filesystems in order to get their + # sizes before we can add them and do the layout. + # PartitionedMount.mount() actually calls __format_disks() + # to create the disk images and carve out the partitions, + # then self.install() calls PartitionedMount.install() + # which calls __install_partitition() for each partition + # to dd the fs into the partitions. It would be nice to + # be able to use e.g. ExtDiskMount etc to create the + # filesystems, since that's where existing e.g. mkfs code + # is, but those are only created after __format_disks() + # which needs the partition sizes so needs them created + # before its called. Well, the existing setup is geared + # to installing packages into mounted filesystems - maybe + # when/if we need to actually do package selection we + # should modify things to use those objects, but for now + # we can avoid that. + + p.install_pkgs(self, self.workdir, self.oe_builddir, self.rootfs_dir, + self.bootimg_dir, self.kernel_dir, self.native_sysroot) + + p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, + self.bootimg_dir, self.kernel_dir, self.native_sysroot) + + fstab = self.__write_fstab(p.get_rootfs()) + self._restore_fstab(fstab) + + self.__instimage.add_partition(int(p.size), + p.disk, + p.mountpoint, + p.source_file, + p.fstype, + p.label, + fsopts = p.fsopts, + boot = p.active, + align = p.align, + part_type = p.part_type) + self.__instimage.layout_partitions(self._ptable_format) + + self.__imgdir = self.workdir + for disk_name, disk in self.__instimage.disks.items(): + full_path = self._full_path(self.__imgdir, disk_name, "direct") + msger.debug("Adding disk %s as %s with size %s bytes" \ + % (disk_name, full_path, disk['min_size'])) + disk_obj = fs_related.DiskImage(full_path, disk['min_size']) + self.__disks[disk_name] = disk_obj + self.__instimage.add_disk(disk_name, disk_obj) + + self.__instimage.mount() + + def install(self, repo_urls=None): + """ + Install fs images into partitions + """ + for disk_name, disk in self.__instimage.disks.items(): + full_path = self._full_path(self.__imgdir, disk_name, "direct") + msger.debug("Installing disk %s as %s with size %s bytes" \ + % (disk_name, full_path, disk['min_size'])) + self.__instimage.install(full_path) + + def configure(self, repodata = None): + """ + Configure the system image according to kickstart. + + For now, it just prepares the image to be bootable by e.g. + creating and installing a bootloader configuration. + """ + source_plugin = self.get_default_source_plugin() + if source_plugin: + self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods) + for disk_name, disk in self.__instimage.disks.items(): + self._source_methods["do_install_disk"](disk, disk_name, self, + self.workdir, + self.oe_builddir, + self.bootimg_dir, + self.kernel_dir, + self.native_sysroot) + + def print_outimage_info(self): + """ + Print the image(s) and artifacts used, for the user. + """ + msg = "The new image(s) can be found here:\n" + + parts = self._get_parts() + + for disk_name, disk in self.__instimage.disks.items(): + full_path = self._full_path(self.__imgdir, disk_name, "direct") + msg += ' %s\n\n' % full_path + + msg += 'The following build artifacts were used to create the image(s):\n' + for p in parts: + if p.get_rootfs() is None: + continue + if p.mountpoint == '/': + str = ':' + else: + str = '["%s"]:' % p.label + msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs()) + + msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir + msg += ' KERNEL_DIR: %s\n' % self.kernel_dir + msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot + + msger.info(msg) + + def _get_boot_config(self): + """ + Return the rootdev/root_part_uuid (if specified by + --part-type) + + Assume partition order same as in wks + """ + rootdev = None + root_part_uuid = None + parts = self._get_parts() + for num, p in enumerate(parts, 1): + if p.mountpoint == "/": + part = '' + if p.disk.startswith('mmcblk'): + part = 'p' + + if self._ptable_format == 'msdos' and num > 3: + rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1) + else: + rootdev = "/dev/%s%s%-d" % (p.disk, part, num) + root_part_uuid = p.part_type + + return (rootdev, root_part_uuid) + + def _unmount_instroot(self): + if not self.__instimage is None: + try: + self.__instimage.cleanup() + except MountError, err: + msger.warning("%s" % err) + diff --git a/scripts/lib/mic/imager/fs.py b/scripts/lib/mic/imager/fs.py new file mode 100644 index 0000000000..d53b29cb47 --- /dev/null +++ b/scripts/lib/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 + +from mic import msger +from mic.utils import runner, misc +from mic.utils.errors import CreatorError +from mic.utils.fs_related import find_binary_path +from mic.imager.baseimager import BaseImageCreator + +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/scripts/lib/mic/imager/livecd.py b/scripts/lib/mic/imager/livecd.py new file mode 100644 index 0000000000..e36f4a76c6 --- /dev/null +++ b/scripts/lib/mic/imager/livecd.py @@ -0,0 +1,750 @@ +#!/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, runner, misc +from mic.utils.errors import CreatorError +from mic.imager.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 rd.live.image" + else: + r = kickstart.get_kernel_args(self.ks) + + 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/bin/checkisomd5") and os.path.exists("/usr/bin/implantisomd5"): + return True + + return False + + def __restore_file(self,path): + try: + os.unlink(path) + except: + pass + if os.path.exists(path + '.rpmnew'): + os.rename(path + '.rpmnew', path) + + def _mount_instroot(self, base_on = None): + LoopImageCreator._mount_instroot(self, base_on) + self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd") + self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf") + + def _unmount_instroot(self): + self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd") + self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf") + 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_dracut_conf(self, path): + if not os.path.exists(os.path.dirname(path)): + fs_related.makedirs(os.path.dirname(path)) + f = open(path, "a") + f.write('add_dracutmodules+=" dmsquash-live pollcdrom "') + f.close() + + 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" + 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/share/branding/default/syslinux/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" + isDracut = False + + if self._alt_initrd_name: + src_initrd_path = os.path.join(bootdir, self._alt_initrd_name) + else: + if os.path.exists(bootdir + "/initramfs-" + version + ".img"): + src_initrd_path = os.path.join(bootdir, "initramfs-" +version+ ".img") + isDracut = True + else: + src_initrd_path = os.path.join(bootdir, "initrd-" +version+ ".img") + + try: + msger.debug("copy %s to %s" % (bootdir + "/vmlinuz-" + version, isodir + "/isolinux/vmlinuz" + index)) + shutil.copyfile(bootdir + "/vmlinuz-" + version, + isodir + "/isolinux/vmlinuz" + index) + + msger.debug("copy %s to %s" % (src_initrd_path, isodir + "/isolinux/initrd" + index + ".img")) + 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,isDracut) + + 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 #ff000000 #ffffffff +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 +menu hidden +menu clear +""" % args + + def __get_image_stanza(self, is_xen, isDracut, **args): + if isDracut: + args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args + else: + args["rootlabel"] = "CDLABEL=%(fslabel)s" % args + if not is_xen: + template = """label %(short)s + menu label %(long)s + kernel vmlinuz%(index)s + append initrd=initrd%(index)s.img root=%(rootlabel)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=%(rootlabel)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, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index) + if index == "0": + self._isDracut = isDracut + + 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) + # tell dracut not to ask for LUKS passwords or activate mdraid sets + if isDracut: + kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0" + else: + kern_opts = kernel_options + + cfg += self.__get_image_stanza(is_xen, isDracut, + fslabel = self.fslabel, + liveargs = kern_opts, + 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, isDracut, + 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, isDracut, + 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 = "i386" +if arch in ("i386", "x86_64"): + LiveCDImageCreator = x86LiveImageCreator +elif arch.startswith("arm"): + LiveCDImageCreator = LiveImageCreatorBase +else: + raise CreatorError("Architecture not supported!") diff --git a/scripts/lib/mic/imager/liveusb.py b/scripts/lib/mic/imager/liveusb.py new file mode 100644 index 0000000000..a909928a4c --- /dev/null +++ b/scripts/lib/mic/imager/liveusb.py @@ -0,0 +1,308 @@ +#!/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 mic.imager.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(usbmnt) + usbloop.add_disk('/dev/sdb', disk) + + 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("rd.live.image", "rd.live.image " + 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("rd.live.image", "rd.live.image rd.live.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/scripts/lib/mic/imager/loop.py b/scripts/lib/mic/imager/loop.py new file mode 100644 index 0000000000..4d05ef271d --- /dev/null +++ b/scripts/lib/mic/imager/loop.py @@ -0,0 +1,418 @@ +#!/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 mic.imager.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', + 'extopts': part.extopts or None, + '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 get_image_names(self): + if not self._instloops: + return None + + return [lo['name'] for lo in self._instloops] + + 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", + "extopts": None, + "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']) + + if fstype in ("ext2", "ext3", "ext4"): + loop['loop'].extopts = loop['extopts'] + + 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): + try: + item['loop'].cleanup() + except: + pass + + 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/scripts/lib/mic/imager/raw.py b/scripts/lib/mic/imager/raw.py new file mode 100644 index 0000000000..838191a6f1 --- /dev/null +++ b/scripts/lib/mic/imager/raw.py @@ -0,0 +1,501 @@ +#!/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 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 mic.imager.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, generate_bmap=None, fstab_entry="uuid"): + """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._disk_names = [] + self._ptable_format = self.ks.handler.bootloader.ptable + self.vmem = 512 + self.vcpu = 1 + self.checksum = False + self.use_uuid = fstab_entry == "uuid" + self.appliance_version = None + self.appliance_release = None + self.compress_image = compress_image + self.bmap_needed = generate_bmap + self._need_extlinux = not kickstart.use_installerfw(self.ks, "extlinux") + #self.getsource = False + #self.listpkg = False + + self._dep_checks.extend(["sync", "kpartx", "parted"]) + if self._need_extlinux: + self._dep_checks.extend(["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): + if kickstart.use_installerfw(self.ks, "fstab"): + # The fstab file will be generated by installer framework scripts + # instead. + return None + + s = "" + for mp in self.__instloop.mountOrder: + p = None + for p1 in self.__instloop.partitions: + if p1['mountpoint'] == mp: + p = p1 + break + + if self.use_uuid and p['uuid']: + device = "UUID=%s" % p['uuid'] + else: + device = "/dev/%s%-d" % (p['disk_name'], p['num']) + + s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { + 'device': device, + '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_name'], 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_disk_names(self): + """ Returns a list of physical target disk names (e.g., 'sdb') which + will be created. """ + + if self._disk_names: + return self._disk_names + + #get partition info from ks handler + parts = self._get_parts() + + for i in range(len(parts)): + if parts[i].disk: + disk_name = parts[i].disk + else: + raise CreatorError("Failed to create disks, no --ondisk " + "specified in partition line of ks file") + + if parts[i].mountpoint and not parts[i].fstype: + raise CreatorError("Failed to create disks, no --fstype " + "specified for partition with mountpoint " + "'%s' in the ks file") + + self._disk_names.append(disk_name) + + return self._disk_names + + def _full_name(self, name, extention): + """ Construct full file name for a file we generate. """ + return "%s-%s.%s" % (self.name, name, extention) + + def _full_path(self, path, name, extention): + """ Construct full file path to a file we generate. """ + return os.path.join(path, self._full_name(name, extention)) + + # + # Actual implemention + # + def _mount_instroot(self, base_on = None): + parts = self._get_parts() + self.__instloop = PartitionedMount(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, + part_type = p.part_type) + + self.__instloop.layout_partitions(self._ptable_format) + + # Create the disks + self.__imgdir = self._mkdtemp() + for disk_name, disk in self.__instloop.disks.items(): + full_path = self._full_path(self.__imgdir, disk_name, "raw") + msger.debug("Adding disk %s as %s with size %s bytes" \ + % (disk_name, full_path, disk['min_size'])) + + disk_obj = fs_related.SparseLoopbackDisk(full_path, + disk['min_size']) + self.__disks[disk_name] = disk_obj + self.__instloop.add_disk(disk_name, disk_obj) + + self.__instloop.mount() + self._create_mkinitrd_config() + + def _get_required_packages(self): + required_packages = BaseImageCreator._get_required_packages(self) + if self._need_extlinux: + 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): + rootdev = None + root_part_uuid = None + for p in self.__instloop.partitions: + if p['mountpoint'] == "/": + rootdev = "/dev/%s%-d" % (p['disk_name'], p['num']) + root_part_uuid = p['partuuid'] + + return (rootdev, root_part_uuid) + + def _create_syslinux_config(self): + + splash = os.path.join(self._instroot, "boot/extlinux") + if os.path.exists(splash): + splashline = "menu background splash.jpg" + else: + splashline = "" + + (rootdev, root_part_uuid) = 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" + if self._ptable_format == 'msdos': + rootstr = rootdev + else: + if not root_part_uuid: + raise MountError("Cannot find the root GPT partition UUID") + rootstr = "PARTUUID=%s" % root_part_uuid + syslinux_conf += "\tappend ro root=%s %s\n" % (rootstr, 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: + 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): + for name in self.__disks.keys(): + loopdev = self.__disks[name].device + + # Set MBR + mbrfile = "%s/usr/share/syslinux/" % self._instroot + if self._ptable_format == 'gpt': + mbrfile += "gptmbr.bin" + else: + mbrfile += "mbr.bin" + + msger.debug("Installing syslinux bootloader '%s' to %s" % \ + (mbrfile, loopdev)) + + mbrsize = os.stat(mbrfile)[stat.ST_SIZE] + rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=' + loopdev]) + if rc != 0: + raise MountError("Unable to set MBR to %s" % loopdev) + + + # 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 %s" \ + % loopdev) + + def _create_bootconfig(self): + #If syslinux is available do the required configurations. + if self._need_extlinux \ + and 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: + try: + self.__instloop.cleanup() + except MountError, err: + msger.warning("%s" % err) + + def _resparse(self, size = None): + return self.__instloop.resparse(size) + + def _get_post_scripts_env(self, in_chroot): + env = BaseImageCreator._get_post_scripts_env(self, in_chroot) + + # Export the file-system UUIDs and partition UUIDs (AKA PARTUUIDs) + for p in self.__instloop.partitions: + env.update(self._set_part_env(p['ks_pnum'], "UUID", p['uuid'])) + env.update(self._set_part_env(p['ks_pnum'], "PARTUUID", p['partuuid'])) + + return env + + 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 = "\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 += " %s\n" % (name_attributes, self.name) + xml += " \n" + # XXX don't hardcode - determine based on the kernel we installed for + # grub baremetal vs xen + xml += " \n" + xml += " \n" + xml += " %s\n" % imgarch + xml += " \n" + xml += " \n" + xml += " \n" + xml += " \n" + + i = 0 + for name in self.__disks.keys(): + full_name = self._full_name(name, self.__disk_format) + xml += " \n" \ + % (full_name, chr(ord('a') + i)) + i = i + 1 + + xml += " \n" + xml += " \n" + xml += " %s\n" % self.vcpu + xml += " %d\n" %(self.vmem * 1024) + for network in self.ks.handler.network.network: + xml += " \n" + xml += " \n" + xml += " \n" + xml += " \n" + xml += " \n" + + if self.checksum is True: + for name in self.__disks.keys(): + diskpath = self._full_path(self._outdir, name, \ + self.__disk_format) + full_name = self._full_name(name, self.__disk_format) + + msger.debug("Generating disk signature for %s" % full_name) + + xml += " \n" \ + % (full_name, self.__disk_format) + + hashes = misc.calc_hashes(diskpath, ('sha1', 'sha256')) + + xml += " %s\n" \ + % hashes[0] + xml += " %s\n" \ + % hashes[1] + xml += " \n" + else: + for name in self.__disks.keys(): + full_name = self._full_name(name, self.__disk_format) + xml += " \n" \ + % (full_name, self.__disk_format) + + xml += " \n" + xml += "\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() + + def generate_bmap(self): + """ Generate block map file for the image. The idea is that while disk + images we generate may be large (e.g., 4GiB), they may actually contain + only little real data, e.g., 512MiB. This data are files, directories, + file-system meta-data, partition table, etc. In other words, when + flashing the image to the target device, you do not have to copy all the + 4GiB of data, you can copy only 512MiB of it, which is 4 times faster. + + This function generates the block map file for an arbitrary image that + mic has generated. The block map file is basically an XML file which + contains a list of blocks which have to be copied to the target device. + The other blocks are not used and there is no need to copy them. """ + + if self.bmap_needed is None: + return + + from mic.utils import BmapCreate + msger.info("Generating the map file(s)") + + for name in self.__disks.keys(): + image = self._full_path(self.__imgdir, name, self.__disk_format) + bmap_file = self._full_path(self._outdir, name, "bmap") + + msger.debug("Generating block map file '%s'" % bmap_file) + + try: + creator = BmapCreate.BmapCreate(image, bmap_file) + creator.generate() + del creator + except BmapCreate.Error as err: + raise CreatorError("Failed to create bmap file: %s" % str(err)) diff --git a/scripts/lib/mic/kickstart/__init__.py b/scripts/lib/mic/kickstart/__init__.py new file mode 100644 index 0000000000..72f3ca6849 --- /dev/null +++ b/scripts/lib/mic/kickstart/__init__.py @@ -0,0 +1,892 @@ +#!/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 string + +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 + +from mic import msger +from mic.utils import errors, misc, runner, fs_related as fs +from custom_commands import desktop, micrepo, wicboot, partition, installerfw + + +AUTH_URL_PTN = r"(?P.*)://(?P.*)(:?P.*)?@(?P.*)" + + +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.Mic_Desktop + commandMap[using_version]["repo"] = micrepo.Mic_Repo + commandMap[using_version]["bootloader"] = wicboot.Wic_Bootloader + commandMap[using_version]["part"] = partition.Wic_Partition + commandMap[using_version]["partition"] = partition.Wic_Partition + commandMap[using_version]["installerfw"] = installerfw.Mic_installerfw + dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData + dataMap[using_version]["PartData"] = partition.Wic_PartData + superclass = ksversion.returnClassForVersion(version=using_version) + + class KSHandlers(superclass): + def __init__(self): + 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 + +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 += [ "-m"] + args += [ "-d", "/home/%s" % userconfig.name ] + args.append(userconfig.name) + try: + dev_null = os.open("/dev/null", os.O_WRONLY) + msger.debug('adding user with %s' % args) + 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: + self.addUser(userconfig) + +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: + auth_url = re.compile(AUTH_URL_PTN) + m = auth_url.match(baseurl) + if m: + baseurl = "%s://%s" % (m.group('scheme'), m.group('url')) + 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, repourl): + for repo in ksrepo.repoList: + if repo.name in repourl: + repo.baseurl = repourl[repo.name] + 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 use_installerfw(ks, feature): + """ Check if the installer framework has to be used for a feature + "feature". """ + + features = ks.handler.installerfw.features + if features: + if feature in features or "all" in features: + return True + return False + +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 rd.live.image"): + 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=None): + 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_urls and 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" + nocache = False + if hasattr(repo, "nocache"): + nocache = repo.nocache + 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, nocache, + 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 + list(required) + +def get_pre_packages(ks, required=()): + return ks.handler.prepackages.packageList + list(required) + +def get_packages(ks, required=()): + return ks.handler.packages.packageList + list(required) + +def get_groups(ks, required=()): + return ks.handler.packages.groupList + list(required) + +def get_excluded(ks, required=()): + return ks.handler.packages.excludedList + list(required) + +def get_partitions(ks): + 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/scripts/lib/mic/kickstart/custom_commands/__init__.py b/scripts/lib/mic/kickstart/custom_commands/__init__.py new file mode 100644 index 0000000000..6aed0ff6fa --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/__init__.py @@ -0,0 +1,17 @@ +from desktop import Mic_Desktop +from micrepo import Mic_Repo, Mic_RepoData +from micpartition import Mic_Partition +from micpartition import Mic_PartData +from installerfw import Mic_installerfw +from partition import Wic_Partition + +__all__ = ( + "Mic_Desktop", + "Mic_Repo", + "Mic_RepoData", + "Mic_Partition", + "Mic_PartData", + "Mic_installerfw", + "Wic_Partition", + "Wic_PartData", +) diff --git a/scripts/lib/mic/kickstart/custom_commands/desktop.py b/scripts/lib/mic/kickstart/custom_commands/desktop.py new file mode 100644 index 0000000000..c8bd647ae3 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/desktop.py @@ -0,0 +1,95 @@ +#!/usr/bin/python -tt +# +# Copyright (c) 2008, 2009, 2010 Intel, Inc. +# +# Yi Yang +# +# 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 Mic_Desktop(KickstartCommand): + def __init__(self, writePriority=0, + defaultdesktop=None, + defaultdm=None, + autologinuser=None, + 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/scripts/lib/mic/kickstart/custom_commands/installerfw.py b/scripts/lib/mic/kickstart/custom_commands/installerfw.py new file mode 100644 index 0000000000..2466f1dc07 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/installerfw.py @@ -0,0 +1,63 @@ +#!/usr/bin/python -tt +# +# Copyright (c) 2013 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 pykickstart.base import * +from pykickstart.options import * + +class Mic_installerfw(KickstartCommand): + """ This class implements the "installerfw" KS option. The argument + of the option is a comman-separated list of MIC features which have to be + disabled and instead, will be done in the installer. For example, + "installerfw=extlinux" disables all the MIC code which installs extlinux to + the target images, and instead, the extlinux or whatever boot-loader will + be installed by the installer instead. + + The installer is a tool which is external to MIC, it comes from the + installation repositories and can be executed by MIC in order to perform + various configuration actions. The main point here is to make sure MIC has + no hard-wired knoledge about the target OS configuration. """ + + removedKeywords = KickstartCommand.removedKeywords + removedAttrs = KickstartCommand.removedAttrs + + def __init__(self, *args, **kwargs): + KickstartCommand.__init__(self, *args, **kwargs) + self.op = self._getParser() + self.features = kwargs.get("installerfw", None) + + def __str__(self): + retval = KickstartCommand.__str__(self) + + if self.features: + retval += "# Enable installer framework features\ninstallerfw\n" + + return retval + + def _getParser(self): + op = KSOptionParser() + return op + + def parse(self, args): + (_, extra) = self.op.parse_args(args=args, lineno=self.lineno) + + if len(extra) != 1: + msg = "Kickstart command \"installerfw\" requires one " \ + "argumet - a list of legacy features to disable" + raise KickstartValueError, formatErrorMsg(self.lineno, msg = msg) + + self.features = extra[0].split(",") + return self diff --git a/scripts/lib/mic/kickstart/custom_commands/micboot.py b/scripts/lib/mic/kickstart/custom_commands/micboot.py new file mode 100644 index 0000000000..66d1678aa7 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micboot.py @@ -0,0 +1,49 @@ +#!/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 Mic_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 = "" + self.ptable = "msdos" + + def _getArgsAsStr(self): + ret = F8_Bootloader._getArgsAsStr(self) + + if self.menus == "": + ret += " --menus=%s" %(self.menus,) + if self.ptable: + ret += " --ptable=\"%s\"" %(self.ptable,) + return ret + + def _getParser(self): + op = F8_Bootloader._getParser(self) + op.add_option("--menus", dest="menus") + op.add_option("--ptable", dest="ptable", type="string") + return op + diff --git a/scripts/lib/mic/kickstart/custom_commands/micpartition.py b/scripts/lib/mic/kickstart/custom_commands/micpartition.py new file mode 100644 index 0000000000..59a87fb486 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micpartition.py @@ -0,0 +1,57 @@ +#!/usr/bin/python -tt +# +# Marko Saukko +# +# 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 Mic_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) + self.extopts = kwargs.get("extopts", None) + self.part_type = kwargs.get("part_type", None) + + def _getArgsAsStr(self): + retval = FC4_PartData._getArgsAsStr(self) + + if self.align: + retval += " --align" + if self.extopts: + retval += " --extoptions=%s" % self.extopts + if self.part_type: + retval += " --part-type=%s" % self.part_type + + return retval + +class Mic_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) + op.add_option("--extoptions", type="string", action="store", dest="extopts", + default=None) + op.add_option("--part-type", type="string", action="store", dest="part_type", + default=None) + return op diff --git a/scripts/lib/mic/kickstart/custom_commands/micrepo.py b/scripts/lib/mic/kickstart/custom_commands/micrepo.py new file mode 100644 index 0000000000..b31576e400 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micrepo.py @@ -0,0 +1,127 @@ +#!/usr/bin/python -tt +# +# Copyright (c) 2008, 2009, 2010 Intel, Inc. +# +# Yi Yang +# +# 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 Mic_RepoData(F8_RepoData): + + def __init__(self, baseurl="", mirrorlist=None, 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", + nocache=False): + kw = {} + # F8_RepoData keywords + if includepkgs: + kw['includepkgs'] = includepkgs + if excludepkgs: + kw['excludepkgs'] = excludepkgs + + #FC6_RepoData keywords + if baseurl: + kw['baseurl'] = baseurl + if mirrorlist: + kw['mirrorlist'] = mirrorlist + if name: + kw['name'] = name + + F8_RepoData.__init__(self, **kw) + 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 + self.nocache = nocache + + 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 + if self.nocache: + retval += " --nocache" + + return retval + +class Mic_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) + op.add_option("--nocache", action="store_true", dest="nocache", + default=False) + return op diff --git a/scripts/lib/mic/kickstart/custom_commands/partition.py b/scripts/lib/mic/kickstart/custom_commands/partition.py new file mode 100644 index 0000000000..450d2d492d --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/partition.py @@ -0,0 +1,482 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module provides the OpenEmbedded partition object definitions. +# +# AUTHORS +# Tom Zanussi +# + +import shutil + +from pykickstart.commands.partition import * +from mic.utils.oe.misc import * +from mic.kickstart.custom_commands import * +from mic.plugin import pluginmgr + +import os +from mic.utils.oe.package_manager import * + +partition_methods = { + "do_install_pkgs":None, + "do_stage_partition":None, + "do_prepare_partition":None, + "do_configure_partition":None, +} + +class Wic_PartData(Mic_PartData): + removedKeywords = Mic_PartData.removedKeywords + removedAttrs = Mic_PartData.removedAttrs + + def __init__(self, *args, **kwargs): + Mic_PartData.__init__(self, *args, **kwargs) + self.deleteRemovedAttrs() + self.source = kwargs.get("source", None) + self.rootfs = kwargs.get("rootfs-dir", None) + self.source_file = "" + self.size = 0 + + def _getArgsAsStr(self): + retval = Mic_PartData._getArgsAsStr(self) + + if self.source: + retval += " --source=%s" % self.source + if self.rootfs: + retval += " --rootfs-dir=%s" % self.rootfs + + return retval + + def get_rootfs(self): + """ + Acessor for rootfs dir + """ + return self.rootfs + + def set_rootfs(self, rootfs): + """ + Acessor for actual rootfs dir, which must be set by source + plugins. + """ + self.rootfs = rootfs + + def get_size(self): + """ + Accessor for partition size, 0 or --size before set_size(). + """ + return self.size + + def set_size(self, size): + """ + Accessor for actual partition size, which must be set by source + plugins. + """ + self.size = size + + def set_source_file(self, source_file): + """ + Accessor for source_file, the location of the generated partition + image, which must be set by source plugins. + """ + self.source_file = source_file + + def get_extra_block_count(self, current_blocks): + """ + The --size param is reflected in self.size (in MB), and we already + have current_blocks (1k) blocks, calculate and return the + number of (1k) blocks we need to add to get to --size, 0 if + we're already there or beyond. + """ + msger.debug("Requested partition size for %s: %d" % \ + (self.mountpoint, self.size)) + + if not self.size: + return 0 + + requested_blocks = self.size * 1024 + + msger.debug("Requested blocks %d, current_blocks %d" % \ + (requested_blocks, current_blocks)) + + if requested_blocks > current_blocks: + return requested_blocks - current_blocks + else: + return 0 + + def install_pkgs(self, creator, cr_workdir, oe_builddir, rootfs_dir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Prepare content for individual partitions, installing packages. + """ + + if not self.source: + return + + self._source_methods = pluginmgr.get_source_plugin_methods(self.source, partition_methods) + self._source_methods["do_install_pkgs"](self, creator, + cr_workdir, + oe_builddir, + rootfs_dir, + bootimg_dir, + kernel_dir, + native_sysroot) + + def install_pkgs_ipk(self, cr_workdir, oe_builddir, rootfs_dir, + native_sysroot, packages, repourl): + """ + Install packages specified into wks file using opkg package manager. + This method is dependend on bb module. + """ + + gVar = {} + gVar["DEPLOY_DIR_IPK"] = os.path.join(oe_builddir, "tmp/deploy/ipk") + + # Run postinstall scripts even in offline mode + # Use the arch priority package rather than higher version one if more than one candidate is found. + #d.setVar("OPKG_ARGS", "--force_postinstall --prefer-arch-to-version") + gVar["OPKG_ARGS"] = "--force_postinstall" + + # OPKG path relative to /output_path + gVar["OPKGLIBDIR"] = "var/lib" + + source_url = repourl.split() + + # Generate feed uri's names, it doesn't seem to matter what name they have + feed_uris = "" + cnt = 0 + archs = "" + for url in source_url: + feed_uris += "cl_def_feed%d##%s\n" % (cnt, url) + cnt += 1 + head, tail = os.path.split(url) + archs += " " + tail + + # IPK_FEED_URIS with special formating defines the URI's used as source for packages + gVar['IPK_FEED_URIS'] = feed_uris + + gVar['BUILD_IMAGES_FROM_FEEDS'] = "1" + + # We need to provide sysroot for utilities + gVar['STAGING_DIR_NATIVE'] = native_sysroot + + # Set WORKDIR for output + gVar['WORKDIR'] = cr_workdir + + # Set TMPDIR for output + gVar['TMPDIR'] = os.path.join(cr_workdir, "tmp") + + if 'ROOTFS_DIR' in rootfs_dir: + target_dir = rootfs_dir['ROOTFS_DIR'] + elif os.path.isdir(rootfs_dir): + target_dir = rootfs_dir + else: + msg = "Couldn't find --rootfs-dir=%s connection" + msg += " or it is not a valid path, exiting" + msger.error(msg % rootfs_dir) + + # Need native sysroot /usr/bin/ for opkg-cl + # chnage PATH var to avoid issues with host tools + defpath = os.environ['PATH'] + os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" + + pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot + pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % target_dir + pseudo += "export PSEUDO_PASSWD=%s;" % target_dir + pseudo += "export PSEUDO_NOSYMLINKEXP=1;" + pseudo += "%s/usr/bin/pseudo " % native_sysroot + + pm = WicOpkgPM(gVar, + target_dir, + 'opkg.conf', + archs, + pseudo, + native_sysroot) + + pm.update() + + pm.install(packages) + + os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" + + + def prepare(self, cr, cr_workdir, oe_builddir, rootfs_dir, bootimg_dir, + kernel_dir, native_sysroot): + """ + Prepare content for individual partitions, depending on + partition command parameters. + """ + if not self.source: + if self.fstype and self.fstype == "swap": + self.prepare_swap_partition(cr_workdir, oe_builddir, + native_sysroot) + elif self.fstype: + self.prepare_empty_partition(cr_workdir, oe_builddir, + native_sysroot) + return + + self._source_methods = pluginmgr.get_source_plugin_methods(self.source, partition_methods) + self._source_methods["do_configure_partition"](self, cr, cr_workdir, + oe_builddir, + bootimg_dir, + kernel_dir, + native_sysroot) + self._source_methods["do_stage_partition"](self, cr, cr_workdir, + oe_builddir, + bootimg_dir, kernel_dir, + native_sysroot) + self._source_methods["do_prepare_partition"](self, cr, cr_workdir, + oe_builddir, + bootimg_dir, kernel_dir, rootfs_dir, + native_sysroot) + + def prepare_rootfs_from_fs_image(self, cr_workdir, oe_builddir, + rootfs_dir): + """ + Handle an already-created partition e.g. xxx.ext3 + """ + rootfs = oe_builddir + du_cmd = "du -Lbms %s" % rootfs + rc, out = exec_cmd(du_cmd) + rootfs_size = out.split()[0] + + self.size = rootfs_size + self.source_file = rootfs + + def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir, + native_sysroot): + """ + Prepare content for a rootfs partition i.e. create a partition + and fill it from a /rootfs dir. + + Currently handles ext2/3/4 and btrfs. + """ + pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot + pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir + pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir + pseudo += "export PSEUDO_NOSYMLINKEXP=1;" + pseudo += "%s/usr/bin/pseudo " % native_sysroot + + if self.fstype.startswith("ext"): + return self.prepare_rootfs_ext(cr_workdir, oe_builddir, + rootfs_dir, native_sysroot, + pseudo) + elif self.fstype.startswith("btrfs"): + return self.prepare_rootfs_btrfs(cr_workdir, oe_builddir, + rootfs_dir, native_sysroot, + pseudo) + + def prepare_rootfs_ext(self, cr_workdir, oe_builddir, rootfs_dir, + native_sysroot, pseudo): + """ + Prepare content for an ext2/3/4 rootfs partition. + """ + + image_rootfs = rootfs_dir + rootfs = "%s/rootfs_%s.%s" % (cr_workdir, self.label ,self.fstype) + + du_cmd = "du -ks %s" % image_rootfs + rc, out = exec_cmd(du_cmd) + actual_rootfs_size = int(out.split()[0]) + + extra_blocks = self.get_extra_block_count(actual_rootfs_size) + + if extra_blocks < IMAGE_EXTRA_SPACE: + extra_blocks = IMAGE_EXTRA_SPACE + + rootfs_size = actual_rootfs_size + extra_blocks + + msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ + (extra_blocks, self.mountpoint, rootfs_size)) + + dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \ + (rootfs, rootfs_size) + rc, out = exec_cmd(dd_cmd) + + extra_imagecmd = "-i 8192" + + mkfs_cmd = "mkfs.%s -F %s %s -d %s" % \ + (self.fstype, extra_imagecmd, rootfs, image_rootfs) + rc, out = exec_native_cmd(pseudo + mkfs_cmd, native_sysroot) + + + # get the rootfs size in the right units for kickstart (Mb) + du_cmd = "du -Lbms %s" % rootfs + rc, out = exec_cmd(du_cmd) + rootfs_size = out.split()[0] + + self.size = rootfs_size + self.source_file = rootfs + + return 0 + + def prepare_for_uboot(self, arch, cr_workdir, oe_builddir, rootfs_dir, + native_sysroot): + """ + Generates u-boot image from source_file( ext2/3/4 ) + + """ + pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot + pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir + pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir + pseudo += "export PSEUDO_NOSYMLINKEXP=1;" + pseudo += "%s/usr/bin/pseudo " % native_sysroot + + # 1) compress image + rootfs = self.source_file + rootfs_gzip = "%s.gz" % rootfs + gzip_cmd = "gzip -f -9 -c %s > %s" % (rootfs, rootfs_gzip) + rc, out = exec_native_cmd(pseudo + gzip_cmd, native_sysroot) + + # 2) image for U-Boot + rootfs_uboot = "%s.u-boot" % rootfs_gzip + mkimage_cmd = "mkimage -A %s -O linux -T ramdisk -C gzip -n %s -d %s %s" % \ + (arch, self.label, rootfs_gzip, rootfs_uboot) + rc, out = exec_native_cmd(pseudo + mkimage_cmd, native_sysroot) + + msger.info("\n\n\tThe new U-Boot ramdisk image can be found here:\n\t\t%s\n\n" % rootfs_uboot) + + return 0 + + def prepare_rootfs_btrfs(self, cr_workdir, oe_builddir, rootfs_dir, + native_sysroot, pseudo): + """ + Prepare content for a btrfs rootfs partition. + + Currently handles ext2/3/4 and btrfs. + """ + image_rootfs = rootfs_dir + rootfs = "%s/rootfs_%s.%s" % (cr_workdir, self.label, self.fstype) + + du_cmd = "du -ks %s" % image_rootfs + rc, out = exec_cmd(du_cmd) + actual_rootfs_size = int(out.split()[0]) + + extra_blocks = self.get_extra_block_count(actual_rootfs_size) + + if extra_blocks < IMAGE_EXTRA_SPACE: + extra_blocks = IMAGE_EXTRA_SPACE + + rootfs_size = actual_rootfs_size + extra_blocks + + msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ + (extra_blocks, self.mountpoint, rootfs_size)) + + dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \ + (rootfs, rootfs_size) + rc, out = exec_cmd(dd_cmd) + + mkfs_cmd = "mkfs.%s -b %d -r %s %s" % \ + (self.fstype, rootfs_size * 1024, image_rootfs, rootfs) + rc, out = exec_native_cmd(pseudo + mkfs_cmd, native_sysroot) + + # get the rootfs size in the right units for kickstart (Mb) + du_cmd = "du -Lbms %s" % rootfs + rc, out = exec_cmd(du_cmd) + rootfs_size = out.split()[0] + + self.size = rootfs_size + self.source_file = rootfs + + def prepare_empty_partition(self, cr_workdir, oe_builddir, native_sysroot): + """ + Prepare an empty partition. + """ + if self.fstype.startswith("ext"): + return self.prepare_empty_partition_ext(cr_workdir, oe_builddir, + native_sysroot) + elif self.fstype.startswith("btrfs"): + return self.prepare_empty_partition_btrfs(cr_workdir, oe_builddir, + native_sysroot) + + def prepare_empty_partition_ext(self, cr_workdir, oe_builddir, + native_sysroot): + """ + Prepare an empty ext2/3/4 partition. + """ + fs = "%s/fs.%s" % (cr_workdir, self.fstype) + + dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ + (fs, self.size) + rc, out = exec_cmd(dd_cmd) + + extra_imagecmd = "-i 8192" + + mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs) + rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) + + self.source_file = fs + + return 0 + + def prepare_empty_partition_btrfs(self, cr_workdir, oe_builddir, + native_sysroot): + """ + Prepare an empty btrfs partition. + """ + fs = "%s/fs.%s" % (cr_workdir, self.fstype) + + dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ + (fs, self.size) + rc, out = exec_cmd(dd_cmd) + + mkfs_cmd = "mkfs.%s -b %d %s" % (self.fstype, self.size * 1024, rootfs) + rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) + + mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs) + rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) + + self.source_file = fs + + return 0 + + def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot): + """ + Prepare a swap partition. + """ + fs = "%s/fs.%s" % (cr_workdir, self.fstype) + + dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ + (fs, self.size) + rc, out = exec_cmd(dd_cmd) + + import uuid + label_str = "" + if self.label: + label_str = "-L %s" % self.label + mkswap_cmd = "mkswap %s -U %s %s" % (label_str, str(uuid.uuid1()), fs) + rc, out = exec_native_cmd(mkswap_cmd, native_sysroot) + + self.source_file = fs + + return 0 + +class Wic_Partition(Mic_Partition): + removedKeywords = Mic_Partition.removedKeywords + removedAttrs = Mic_Partition.removedAttrs + + def _getParser(self): + op = Mic_Partition._getParser(self) + # use specified source file to fill the partition + # and calculate partition size + op.add_option("--source", type="string", action="store", + dest="source", default=None) + # use specified rootfs path to fill the partition + op.add_option("--rootfs-dir", type="string", action="store", + dest="rootfs", default=None) + return op diff --git a/scripts/lib/mic/kickstart/custom_commands/wicboot.py b/scripts/lib/mic/kickstart/custom_commands/wicboot.py new file mode 100644 index 0000000000..ab8871de4e --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/wicboot.py @@ -0,0 +1,57 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module provides the OpenEmbedded bootloader object definitions. +# +# AUTHORS +# Tom Zanussi +# + +from pykickstart.base import * +from pykickstart.errors import * +from pykickstart.options import * +from pykickstart.commands.bootloader import * + +from mic.kickstart.custom_commands.micboot import * + +class Wic_Bootloader(Mic_Bootloader): + def __init__(self, writePriority=10, appendLine="", driveorder=None, + forceLBA=False, location="", md5pass="", password="", + upgrade=False, menus=""): + Mic_Bootloader.__init__(self, writePriority, appendLine, driveorder, + forceLBA, location, md5pass, password, upgrade) + + self.source = "" + + def _getArgsAsStr(self): + retval = Mic_Bootloader._getArgsAsStr(self) + + if self.source: + retval += " --source=%s" % self.source + + return retval + + def _getParser(self): + op = Mic_Bootloader._getParser(self) + # use specified source plugin to implement bootloader-specific methods + op.add_option("--source", type="string", action="store", + dest="source", default=None) + return op + diff --git a/scripts/lib/mic/msger.py b/scripts/lib/mic/msger.py new file mode 100644 index 0000000000..9afc85be93 --- /dev/null +++ b/scripts/lib/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 + +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: + timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime()) + 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 log(msg=''): + # log msg to LOG_CONTENT then save to logfile + global LOG_CONTENT + if msg: + LOG_CONTENT += msg + +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(): + # + 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 to continue ...' + raw_input(msg) + +def set_logfile(fpath): + global LOG_FILE_FP + + def _savelogf(): + if 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 = 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/scripts/lib/mic/plugin.py b/scripts/lib/mic/plugin.py new file mode 100644 index 0000000000..df03c15081 --- /dev/null +++ b/scripts/lib/mic/plugin.py @@ -0,0 +1,121 @@ +#!/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 mic import msger +from mic import pluginbase +from mic.utils import errors + + +__ALL__ = ['PluginMgr', 'pluginmgr'] + +PLUGIN_TYPES = ["imager", "source"] # 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): + mic_path = os.path.dirname(__file__) + eos = mic_path.find('scripts') + len('scripts') + scripts_path = mic_path[:eos] + + self.plugin_dir = scripts_path + "/lib/mic/plugins" + + 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, err: + msg = 'Failed to load plugin %s/%s: %s' \ + % (os.path.basename(pdir), mod, err) + msger.warning(msg) + + 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) + + def get_source_plugin_methods(self, source_name, methods): + """ + The methods param is a dict with the method names to find. On + return, the dict values will be filled in with pointers to the + corresponding methods. If one or more methods are not found, + None is returned. + """ + return_methods = None + for _source_name, klass in self.get_plugins('source').iteritems(): + if _source_name == source_name: + for _method_name in methods.keys(): + if not hasattr(klass, _method_name): + msger.warning("Unimplemented %s source interface for: %s"\ + % (_method_name, _source_name)) + return None + func = getattr(klass, _method_name) + methods[_method_name] = func + return_methods = methods + return return_methods + +pluginmgr = PluginMgr() diff --git a/scripts/lib/mic/pluginbase.py b/scripts/lib/mic/pluginbase.py new file mode 100644 index 0000000000..881d9969c6 --- /dev/null +++ b/scripts/lib/mic/pluginbase.py @@ -0,0 +1,158 @@ +#!/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 SourcePlugin(_Plugin): + mic_plugin_type = "source" + """ + The methods that can be implemented by --source plugins. + + Any methods not implemented in a subclass inherit these. + """ + + @classmethod + def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, rootfs_dir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called before partitions have been prepared and assembled into a + disk image. Install packages into rootfs + """ + msger.debug("SourcePlugin: do_install_pkgs: part %s" % part) + + @classmethod + def do_install_disk(self, disk, disk_name, cr, workdir, oe_builddir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called after all partitions have been prepared and assembled into a + disk image. This provides a hook to allow finalization of a + disk image e.g. to write an MBR to it. + """ + msger.debug("SourcePlugin: do_install_disk: disk: %s" % disk_name) + + @classmethod + def do_stage_partition(self, part, cr, workdir, oe_builddir, bootimg_dir, + kernel_dir, native_sysroot): + """ + Special content staging hook called before do_prepare_partition(), + normally empty. + + Typically, a partition will just use the passed-in parame e.g + straight bootimg_dir, etc, but in some cases, things need to + be more tailored e.g. to use a deploy dir + /boot, etc. This + hook allows those files to be staged in a customized fashion. + Not that get_bitbake_var() allows you to acces non-standard + variables that you might want to use for this. + """ + msger.debug("SourcePlugin: do_stage_partition: part: %s" % part) + + @classmethod + def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called before do_prepare_partition(), typically used to create + custom configuration files for a partition, for example + syslinux or grub config files. + """ + msger.debug("SourcePlugin: do_configure_partition: part: %s" % part) + + @classmethod + def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, + kernel_dir, rootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + """ + msger.debug("SourcePlugin: do_prepare_partition: part: %s" % part) + +class BackendPlugin(_Plugin): + mic_plugin_type="backend" + + 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', 'SourcePlugin', 'get_plugins'] diff --git a/scripts/lib/mic/plugins/backend/yumpkgmgr.py b/scripts/lib/mic/plugins/backend/yumpkgmgr.py new file mode 100644 index 0000000000..955f813109 --- /dev/null +++ b/scripts/lib/mic/plugins/backend/yumpkgmgr.py @@ -0,0 +1,490 @@ +#!/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.grabber import TextProgress +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: + try: + import M2Crypto + m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck + M2Crypto.SSL.Connection.clientPostConnectionCheck = None + except ImportError, err: + raise CreatorError("%s, please try to install python-m2crypto" % str(err)) + + proxy = None + if url: + proxy = get_proxy_for(url) + else: + proxy = get_proxy_for(self.urls[0]) + + if proxy: + self.proxy = str(proxy) + + 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 = {} + self.__pkgs_vcsinfo = {} + + self.install_debuginfo = False + + 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, nocache=False, + 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.cache = not nocache + + 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, + 'version': pkg.version, + 'release': pkg.release + } + 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 cached ...") + for po in dlpkgs: + local = po.localPkg() + repo = filter(lambda r: r.id == po.repoid, self.repos.listEnabled())[0] + if not repo.cache and os.path.exists(local): + os.unlink(local) + 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("Packages: %d Total, %d Cached, %d Missed" \ + % (total_count, cached_count, total_count - cached_count)) + + try: + repos = self.repos.listEnabled() + for repo in repos: + repo.setCallback(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 getVcsInfo(self): + return self.__pkgs_vcsinfo + + 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/scripts/lib/mic/plugins/backend/zypppkgmgr.py b/scripts/lib/mic/plugins/backend/zypppkgmgr.py new file mode 100755 index 0000000000..c760859832 --- /dev/null +++ b/scripts/lib/mic/plugins/backend/zypppkgmgr.py @@ -0,0 +1,973 @@ +#!/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 download.tizen.org/tools") + +from mic import msger +from mic.kickstart import ksparser +from mic.utils import misc, rpmmisc, runner, fs_related +from mic.utils.grabber import myurlgrab, TextProgress +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.nocache = False + + 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.__pkgs_vcsinfo = {} + 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 + self.install_debuginfo = False + + 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 _cleanupZyppJunk(self, installroot): + try: + shutil.rmtree(os.path.join(installroot, '.zypp')) + except: + pass + + 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 _zyppQueryPackage(self, pkg): + query = zypp.PoolQuery() + query.addKind(zypp.ResKind.package) + query.addAttribute(zypp.SolvAttr.name,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(p1, p2): + # compare criterion: arch compatibility first, then repo + # priority, and version last + a1 = p1.arch() + a2 = p2.arch() + if str(a1) != str(a2): + if a1.compatible_with(a2): + return -1 + else: + return 1 + # Priority of a repository is an integer value between 0 (the + # highest priority) and 99 (the lowest priority) + pr1 = int(p1.repoInfo().priority()) + pr2 = int(p2.repoInfo().priority()) + if pr1 > pr2: + return -1 + elif pr1 < pr2: + return 1 + + ed1 = p1.edition() + ed2 = p2.edition() + (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 pitem in sorted( + q.queryResults(self.Z.pool()), + cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)), + reverse=True): + item = zypp.asKindPackage(pitem) + 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, pitem) + 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 pitem in sorted( + q.queryResults(self.Z.pool()), + cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)), + reverse=True): + item = zypp.asKindPackage(pitem) + 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, pitem) + break + + if found: + return None + else: + raise CreatorError("Unable to find package: %s" % (pkg,)) + + def inDeselectPackages(self, pitem): + """check if specified pacakges are in the list of inDeselectPackages + """ + item = zypp.asKindPackage(pitem) + 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 pitem in q.queryResults(self.Z.pool()): + item = zypp.asKindPattern(pitem) + summary = "%s" % item.summary() + name = "%s" % item.name() + if name == grp or summary == grp: + found = True + pitem.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, + nocache = False, + 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.nocache = nocache + 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 is not None: + # priority 0 has issue in RepoInfo.setPriority + repo.priority = priority + 1 + + 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 is not None: + repo_info.setPriority(repo.priority) + + # this hack is used to change zypp credential file location + # the default one is $HOME/.zypp, which cause conflicts when + # installing some basic packages, and the location doesn't + # have any interface actually, so use a tricky way anyway + homedir = None + if 'HOME' in os.environ: + homedir = os.environ['HOME'] + os.environ['HOME'] = '/' + else: + os.environ['HOME'] = '/' + + self.repo_manager.addRepository(repo_info) + + # save back the $HOME env + if homedir: + os.environ['HOME'] = homedir + else: + del os.environ['HOME'] + + 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 pitem in installed_pkgs: + if not zypp.isKindPattern(pitem) and \ + not self.inDeselectPackages(pitem): + item = zypp.asKindPackage(pitem) + dlpkgs.append(item) + + if not self.install_debuginfo or str(item.arch()) == "noarch": + continue + + dipkg = self._zyppQueryPackage("%s-debuginfo" % item.name()) + if dipkg: + ditem = zypp.asKindPackage(dipkg) + dlpkgs.append(ditem) + else: + msger.warning("No debuginfo rpm found for: %s" \ + % item.name()) + + # 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'], + 'version': hdr['version'], + 'release': hdr['release'] + } + license = hdr['license'] + + else: + pkg_long_name = misc.RPM_FMT % { + 'name': pkg.name(), + 'arch': pkg.arch(), + 'version': pkg.edition().version(), + 'release': pkg.edition().release() + } + + 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.downloadSize()), dlpkgs)) + localpkgs = self.localpkgs.keys() + + msger.info("Checking packages cached ...") + for po in dlpkgs: + # Check if it is cached locally + if po.name() in localpkgs: + cached_count += 1 + else: + local = self.getLocalPkgPath(po) + name = str(po.repoInfo().name()) + try: + repo = filter(lambda r: r.name == name, self.repos)[0] + except IndexError: + repo = None + nocache = repo.nocache if repo else False + + if os.path.exists(local): + if nocache or 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("Packages: %d Total, %d Cached, %d Missed" \ + % (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 getVcsInfo(self): + if self.__pkgs_vcsinfo: + return + + if not self.ts: + self.__initialize_transaction() + + mi = self.ts.dbMatch() + for hdr in mi: + lname = misc.RPM_FMT % { + 'name': hdr['name'], + 'arch': hdr['arch'], + 'version': hdr['version'], + 'release': hdr['release'] + } + self.__pkgs_vcsinfo[lname] = hdr['VCS'] + + return self.__pkgs_vcsinfo + + def getAllContent(self): + if self.__pkgs_content: + return self.__pkgs_content + + if not self.ts: + self.__initialize_transaction() + + mi = self.ts.dbMatch() + for hdr in mi: + lname = misc.RPM_FMT % { + 'name': hdr['name'], + 'arch': hdr['arch'], + 'version': hdr['version'], + 'release': hdr['release'] + } + self.__pkgs_content[lname] = hdr['FILENAMES'] + + 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= 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 = 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 = 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() + + # clean rpm lock + self._cleanupRpmdbLocks(self.instroot) + self._cleanupZyppJunk(self.instroot) + # 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 + rpmuri = self.get_url(po) + if rpmuri.startswith("file:/"): + rpmpath = rpmuri[5:] + + 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 = 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(p1, p2): + ed1 = p1.edition() + ed2 = p2.edition() + (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(zypp.asKindPackage(x), zypp.asKindPackage(y)), + reverse=True) + + if items: + item = zypp.asKindPackage(items[0]) + url = self.get_url(item) + proxies = self.get_proxies(item) + return (url, proxies) + + return (None, None) diff --git a/scripts/lib/mic/plugins/hook/.py b/scripts/lib/mic/plugins/hook/.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib/mic/plugins/hook/empty_hook.py b/scripts/lib/mic/plugins/hook/empty_hook.py new file mode 100644 index 0000000000..397585d8c1 --- /dev/null +++ b/scripts/lib/mic/plugins/hook/empty_hook.py @@ -0,0 +1,3 @@ +#!/usr/bin/python + +# TODO: plugin base for hooks diff --git a/scripts/lib/mic/plugins/imager/direct_plugin.py b/scripts/lib/mic/plugins/imager/direct_plugin.py new file mode 100644 index 0000000000..fc7c10c3df --- /dev/null +++ b/scripts/lib/mic/plugins/imager/direct_plugin.py @@ -0,0 +1,107 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'direct' imager plugin class for 'wic', based +# loosely on the raw imager plugin from 'mic' +# +# AUTHORS +# Tom Zanussi +# + +import os +import shutil +import re +import tempfile + +from mic import chroot, msger +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.direct as direct +from mic.pluginbase import ImagerPlugin + +class DirectPlugin(ImagerPlugin): + name = 'direct' + + @classmethod + def __rootfs_dir_to_dict(self, rootfs_dirs): + """ + Gets a string that contain 'connection=dir' splitted by + space and return a dict + """ + krootfs_dir = {} + for rootfs_dir in rootfs_dirs.split(' '): + k, v = rootfs_dir.split('=') + krootfs_dir[k] = v + + return krootfs_dir + + @classmethod + def do_create(self, subcmd, opts, *args): + """ + Create direct image, called from creator as 'direct' cmd + """ + if len(args) != 9: + raise errors.Usage("Extra arguments given") + + staging_data_dir = args[0] + hdddir = args[1] + native_sysroot = args[2] + kernel_dir = args[3] + bootimg_dir = args[4] + rootfs_dir = args[5] + + creatoropts = configmgr.create + ksconf = args[6] + + image_output_dir = args[7] + oe_builddir = args[8] + + krootfs_dir = self.__rootfs_dir_to_dict(rootfs_dir) + + configmgr._ksconf = ksconf + + creator = direct.DirectImageCreator(oe_builddir, + image_output_dir, + krootfs_dir, + bootimg_dir, + kernel_dir, + native_sysroot, + hdddir, + staging_data_dir, + creatoropts, + None, + None, + None) + + try: + creator.mount(None, creatoropts["cachedir"]) + creator.install() + creator.configure(creatoropts["repomd"]) + creator.print_outimage_info() + + except errors.CreatorError: + raise + finally: + creator.cleanup() + + return 0 diff --git a/scripts/lib/mic/plugins/imager/fs_plugin.py b/scripts/lib/mic/plugins/imager/fs_plugin.py new file mode 100644 index 0000000000..6bcaf00729 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/fs_plugin.py @@ -0,0 +1,143 @@ +#!/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 +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} [OPTS] + + ${cmd_option_list} + """ + + if len(args) != 1: + raise errors.Usage("Extra arguments given") + + creatoropts = configmgr.create + ksconf = args[0] + + if creatoropts['runtime'] == 'bootstrap': + configmgr._ksconf = ksconf + rt_util.bootstrap_mic() + + 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') + if 'vcs' not in recording_pkgs: + recording_pkgs.append('vcs') + + 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 + backends = pluginmgr.get_plugins('backend') + if 'auto' == creatoropts['pkgmgr']: + for key in configmgr.prefer_backends: + if key in backends: + pkgmgr = backends[key] + break + else: + for key in backends.keys(): + if key == creatoropts['pkgmgr']: + pkgmgr = backends[key] + break + + if not pkgmgr: + raise errors.CreatorError("Can't find backend: %s, " + "available choices: %s" % + (creatoropts['pkgmgr'], + ','.join(backends.keys()))) + + 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, cmd=[]):#chroot.py parse opts&args + try: + if len(cmd) != 0: + cmdline = ' '.join(cmd) + else: + cmdline = "/bin/bash" + envcmd = fs_related.find_binary_inchroot("env", target) + if envcmd: + cmdline = "%s HOME=/root %s" % (envcmd, cmdline) + chroot.chroot(target, None, cmdline) + finally: + chroot.cleanup_after_chroot("dir", None, None, None) + return 1 diff --git a/scripts/lib/mic/plugins/imager/livecd_plugin.py b/scripts/lib/mic/plugins/imager/livecd_plugin.py new file mode 100644 index 0000000000..82cb1af7dc --- /dev/null +++ b/scripts/lib/mic/plugins/imager/livecd_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 +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} [OPTS] + + ${cmd_option_list} + """ + + if len(args) != 1: + raise errors.Usage("Extra arguments given") + + creatoropts = configmgr.create + ksconf = args[0] + + if creatoropts['runtime'] == 'bootstrap': + configmgr._ksconf = ksconf + rt_util.bootstrap_mic() + + 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') + if 'vcs' not in recording_pkgs: + recording_pkgs.append('vcs') + + 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 + backends = pluginmgr.get_plugins('backend') + if 'auto' == creatoropts['pkgmgr']: + for key in configmgr.prefer_backends: + if key in backends: + pkgmgr = backends[key] + break + else: + for key in backends.keys(): + if key == creatoropts['pkgmgr']: + pkgmgr = backends[key] + break + + if not pkgmgr: + raise errors.CreatorError("Can't find backend: %s, " + "available choices: %s" % + (creatoropts['pkgmgr'], + ','.join(backends.keys()))) + + 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, cmd=[]): + 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: + if len(cmd) != 0: + cmdline = ' '.join(cmd) + else: + cmdline = "/bin/bash" + envcmd = fs_related.find_binary_inchroot("env", extmnt) + if envcmd: + cmdline = "%s HOME=/root %s" % (envcmd, cmdline) + 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/scripts/lib/mic/plugins/imager/liveusb_plugin.py b/scripts/lib/mic/plugins/imager/liveusb_plugin.py new file mode 100644 index 0000000000..3d53c84410 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/liveusb_plugin.py @@ -0,0 +1,260 @@ +#!/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 +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} [OPTS] + + ${cmd_option_list} + """ + + if len(args) != 1: + raise errors.Usage("Extra arguments given") + + creatoropts = configmgr.create + ksconf = args[0] + + if creatoropts['runtime'] == "bootstrap": + configmgr._ksconf = ksconf + rt_util.bootstrap_mic() + + 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') + if 'vcs' not in recording_pkgs: + recording_pkgs.append('vcs') + + 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 + backends = pluginmgr.get_plugins('backend') + if 'auto' == creatoropts['pkgmgr']: + for key in configmgr.prefer_backends: + if key in backends: + pkgmgr = backends[key] + break + else: + for key in backends.keys(): + if key == creatoropts['pkgmgr']: + pkgmgr = backends[key] + break + + if not pkgmgr: + raise errors.CreatorError("Can't find backend: %s, " + "available choices: %s" % + (creatoropts['pkgmgr'], + ','.join(backends.keys()))) + + 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, cmd=[]): + 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: + if len(cmd) != 0: + cmdline = ' '.join(cmd) + else: + cmdline = "/bin/bash" + envcmd = fs_related.find_binary_inchroot("env", extmnt) + if envcmd: + cmdline = "%s HOME=/root %s" % (envcmd, cmdline) + 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(imgmnt, skipformat = True) + imgloop.add_disk('/dev/sdb', disk) + 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/scripts/lib/mic/plugins/imager/loop_plugin.py b/scripts/lib/mic/plugins/imager/loop_plugin.py new file mode 100644 index 0000000000..2a05b3c238 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/loop_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 +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} [OPTS] + + ${cmd_option_list} + """ + + if len(args) != 1: + raise errors.Usage("Extra arguments given") + + creatoropts = configmgr.create + ksconf = args[0] + + if creatoropts['runtime'] == "bootstrap": + configmgr._ksconf = ksconf + rt_util.bootstrap_mic() + + 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') + if 'vcs' not in recording_pkgs: + recording_pkgs.append('vcs') + + 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 + backends = pluginmgr.get_plugins('backend') + if 'auto' == creatoropts['pkgmgr']: + for key in configmgr.prefer_backends: + if key in backends: + pkgmgr = backends[key] + break + else: + for key in backends.keys(): + if key == creatoropts['pkgmgr']: + pkgmgr = backends[key] + break + + if not pkgmgr: + raise errors.CreatorError("Can't find backend: %s, " + "available choices: %s" % + (creatoropts['pkgmgr'], + ','.join(backends.keys()))) + + creator = LoopImageCreator(creatoropts, + pkgmgr, + opts.compress_image, + opts.shrink) + + if len(recording_pkgs) > 0: + creator._recording_pkgs = recording_pkgs + + image_names = [creator.name + ".img"] + image_names.extend(creator.get_image_names()) + self.check_image_exists(creator.destdir, + creator.pack_to, + image_names, + 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, cmd=[]): + 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: + if len(cmd) != 0: + cmdline = "/usr/bin/env HOME=/root " + ' '.join(cmd) + else: + cmdline = "/usr/bin/env HOME=/root /bin/bash" + chroot.chroot(mntdir, None, cmdline) + 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, cmd=[]): + if target.endswith('.tar'): + import tarfile + if tarfile.is_tarfile(target): + LoopPlugin._do_chroot_tar(target, cmd) + 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: + if len(cmd) != 0: + cmdline = ' '.join(cmd) + else: + cmdline = "/bin/bash" + envcmd = fs_related.find_binary_inchroot("env", extmnt) + if envcmd: + cmdline = "%s HOME=/root %s" % (envcmd, cmdline) + 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/scripts/lib/mic/plugins/imager/raw_plugin.py b/scripts/lib/mic/plugins/imager/raw_plugin.py new file mode 100644 index 0000000000..f9625b87e8 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/raw_plugin.py @@ -0,0 +1,275 @@ +#!/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 +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") + @cmdln.option("--generate-bmap", action="store_true", default = None, + help="also generate the block map file") + @cmdln.option("--fstab-entry", dest="fstab_entry", type='choice', + choices=("name", "uuid"), default="uuid", + help="Set fstab entry, 'name' means using device names, " + "'uuid' means using filesystem uuid") + def do_create(self, subcmd, opts, *args): + """${cmd_name}: create raw image + + Usage: + ${name} ${cmd_name} [OPTS] + + ${cmd_option_list} + """ + + if len(args) != 1: + raise errors.Usage("Extra arguments given") + + creatoropts = configmgr.create + ksconf = args[0] + + if creatoropts['runtime'] == "bootstrap": + configmgr._ksconf = ksconf + rt_util.bootstrap_mic() + + 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') + if 'vcs' not in recording_pkgs: + recording_pkgs.append('vcs') + + 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 + backends = pluginmgr.get_plugins('backend') + if 'auto' == creatoropts['pkgmgr']: + for key in configmgr.prefer_backends: + if key in backends: + pkgmgr = backends[key] + break + else: + for key in backends.keys(): + if key == creatoropts['pkgmgr']: + pkgmgr = backends[key] + break + + if not pkgmgr: + raise errors.CreatorError("Can't find backend: %s, " + "available choices: %s" % + (creatoropts['pkgmgr'], + ','.join(backends.keys()))) + + creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image, + opts.generate_bmap, opts.fstab_entry) + + if len(recording_pkgs) > 0: + creator._recording_pkgs = recording_pkgs + + images = ["%s-%s.raw" % (creator.name, disk_name) + for disk_name in creator.get_disk_names()] + 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.generate_bmap() + 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, cmd=[]): + 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(imgmnt, skipformat = True) + imgloop.add_disk('/dev/sdb', disk) + 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: + if len(cmd) != 0: + cmdline = ' '.join(cmd) + else: + cmdline = "/bin/bash" + envcmd = fs_related.find_binary_inchroot("env", imgmnt) + if envcmd: + cmdline = "%s HOME=/root %s" % (envcmd, cmdline) + 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(srcmnt, skipformat = True) + + srcloop.add_disk('/dev/sdb', disk) + 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/scripts/lib/mic/plugins/source/bootimg-efi.py b/scripts/lib/mic/plugins/source/bootimg-efi.py new file mode 100644 index 0000000000..2cc179a337 --- /dev/null +++ b/scripts/lib/mic/plugins/source/bootimg-efi.py @@ -0,0 +1,169 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'bootimg-efi' source plugin class for 'wic' +# +# AUTHORS +# Tom Zanussi +# + +import os +import shutil +import re +import tempfile + +from mic import kickstart, chroot, msger +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.direct as direct +from mic.pluginbase import SourcePlugin +from mic.utils.oe.misc import * +from mic.imager.direct import DirectImageCreator + +class BootimgEFIPlugin(SourcePlugin): + name = 'bootimg-efi' + + @classmethod + def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called before do_prepare_partition(), creates grubefi config + """ + hdddir = "%s/hdd/boot" % cr_workdir + rm_cmd = "rm -rf %s" % cr_workdir + exec_cmd(rm_cmd) + + install_cmd = "install -d %s/EFI/BOOT" % hdddir + tmp = exec_cmd(install_cmd) + + splash = os.path.join(cr_workdir, "/EFI/boot/splash.jpg") + if os.path.exists(splash): + splashline = "menu background splash.jpg" + else: + splashline = "" + + (rootdev, root_part_uuid) = cr._get_boot_config() + options = cr.ks.handler.bootloader.appendLine + + grubefi_conf = "" + grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" + grubefi_conf += "default=boot\n" + timeout = kickstart.get_timeout(cr.ks) + if not timeout: + timeout = 0 + grubefi_conf += "timeout=%s\n" % timeout + grubefi_conf += "menuentry 'boot'{\n" + + kernel = "/vmlinuz" + + if cr._ptable_format == 'msdos': + rootstr = rootdev + else: + if not root_part_uuid: + raise MountError("Cannot find the root GPT partition UUID") + rootstr = "PARTUUID=%s" % root_part_uuid + + grubefi_conf += "linux %s root=%s rootwait %s\n" \ + % (kernel, rootstr, options) + grubefi_conf += "}\n" + if splashline: + syslinux_conf += "%s\n" % splashline + + msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \ + % cr_workdir) + cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w") + cfg.write(grubefi_conf) + cfg.close() + + @classmethod + def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, + kernel_dir, rootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + In this case, prepare content for an EFI (grub) boot partition. + """ + if not bootimg_dir: + bootimg_dir = get_bitbake_var("HDDDIR") + if not bootimg_dir: + msger.error("Couldn't find HDDDIR, exiting\n") + # just so the result notes display it + cr.set_bootimg_dir(bootimg_dir) + + staging_kernel_dir = kernel_dir + staging_data_dir = bootimg_dir + + hdddir = "%s/hdd" % cr_workdir + + install_cmd = "install -m 0644 %s/bzImage %s/bzImage" % \ + (staging_kernel_dir, hdddir) + tmp = exec_cmd(install_cmd) + + shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, + "%s/grub.cfg" % cr_workdir) + + cp_cmd = "cp %s/EFI/BOOT/* %s/EFI/BOOT" % (staging_data_dir, hdddir) + exec_cmd(cp_cmd, True) + + shutil.move("%s/grub.cfg" % cr_workdir, + "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir) + + du_cmd = "du -bks %s" % hdddir + rc, out = exec_cmd(du_cmd) + blocks = int(out.split()[0]) + + extra_blocks = part.get_extra_block_count(blocks) + + if extra_blocks < BOOTDD_EXTRA_SPACE: + extra_blocks = BOOTDD_EXTRA_SPACE + + blocks += extra_blocks + + msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ + (extra_blocks, part.mountpoint, blocks)) + + # Ensure total sectors is an integral number of sectors per + # track or mcopy will complain. Sectors are 512 bytes, and we + # generate images with 32 sectors per track. This calculation is + # done in blocks, thus the mod by 16 instead of 32. + blocks += (16 - (blocks % 16)) + + # dosfs image, created by mkdosfs + bootimg = "%s/boot.img" % cr_workdir + + dosfs_cmd = "mkdosfs -n efi -C %s %d" % (bootimg, blocks) + exec_native_cmd(dosfs_cmd, native_sysroot) + + mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) + exec_native_cmd(mcopy_cmd, native_sysroot) + + chmod_cmd = "chmod 644 %s" % bootimg + exec_cmd(chmod_cmd) + + du_cmd = "du -Lbms %s" % bootimg + rc, out = exec_cmd(du_cmd) + bootimg_size = out.split()[0] + + part.set_size(bootimg_size) + part.set_source_file(bootimg) + + diff --git a/scripts/lib/mic/plugins/source/bootimg-pcbios.py b/scripts/lib/mic/plugins/source/bootimg-pcbios.py new file mode 100644 index 0000000000..1211e5c93b --- /dev/null +++ b/scripts/lib/mic/plugins/source/bootimg-pcbios.py @@ -0,0 +1,195 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'bootimg-pcbios' source plugin class for 'wic' +# +# AUTHORS +# Tom Zanussi +# + +import os +import shutil +import re +import tempfile + +from mic import kickstart, chroot, msger +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.direct as direct +from mic.pluginbase import SourcePlugin +from mic.utils.oe.misc import * +from mic.imager.direct import DirectImageCreator + +class BootimgPcbiosPlugin(SourcePlugin): + name = 'bootimg-pcbios' + + @classmethod + def do_install_disk(self, disk, disk_name, cr, workdir, oe_builddir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called after all partitions have been prepared and assembled into a + disk image. In this case, we install the MBR. + """ + mbrfile = "%s/syslinux/" % bootimg_dir + if cr._ptable_format == 'gpt': + mbrfile += "gptmbr.bin" + else: + mbrfile += "mbr.bin" + + if not os.path.exists(mbrfile): + msger.error("Couldn't find %s. If using the -e option, do you have the right MACHINE set in local.conf? If not, is the bootimg_dir path correct?" % mbrfile) + + full_path = cr._full_path(workdir, disk_name, "direct") + msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ + % (disk_name, full_path, disk['min_size'])) + + rc = runner.show(['dd', 'if=%s' % mbrfile, + 'of=%s' % full_path, 'conv=notrunc']) + if rc != 0: + raise MountError("Unable to set MBR to %s" % full_path) + + @classmethod + def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called before do_prepare_partition(), creates syslinux config + """ + hdddir = "%s/hdd/boot" % cr_workdir + rm_cmd = "rm -rf " + cr_workdir + exec_cmd(rm_cmd) + + install_cmd = "install -d %s" % hdddir + tmp = exec_cmd(install_cmd) + + splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg") + if os.path.exists(splash): + splashline = "menu background splash.jpg" + else: + splashline = "" + + (rootdev, root_part_uuid) = cr._get_boot_config() + options = cr.ks.handler.bootloader.appendLine + + syslinux_conf = "" + syslinux_conf += "PROMPT 0\n" + timeout = kickstart.get_timeout(cr.ks) + if not timeout: + timeout = 0 + syslinux_conf += "TIMEOUT " + str(timeout) + "\n" + syslinux_conf += "\n" + syslinux_conf += "ALLOWOPTIONS 1\n" + syslinux_conf += "SERIAL 0 115200\n" + syslinux_conf += "\n" + if splashline: + syslinux_conf += "%s\n" % splashline + syslinux_conf += "DEFAULT boot\n" + syslinux_conf += "LABEL boot\n" + + kernel = "/vmlinuz" + syslinux_conf += "KERNEL " + kernel + "\n" + + if cr._ptable_format == 'msdos': + rootstr = rootdev + else: + if not root_part_uuid: + raise MountError("Cannot find the root GPT partition UUID") + rootstr = "PARTUUID=%s" % root_part_uuid + + syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options) + + msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \ + % cr_workdir) + cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w") + cfg.write(syslinux_conf) + cfg.close() + + @classmethod + def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, + kernel_dir, rootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + In this case, prepare content for legacy bios boot partition. + """ + if not bootimg_dir: + bootimg_dir = get_bitbake_var("STAGING_DATADIR") + if not bootimg_dir: + msger.error("Couldn't find STAGING_DATADIR, exiting\n") + # just so the result notes display it + cr.set_bootimg_dir(bootimg_dir) + + staging_kernel_dir = kernel_dir + staging_data_dir = bootimg_dir + + hdddir = "%s/hdd/boot" % cr_workdir + + install_cmd = "install -m 0644 %s/bzImage %s/vmlinuz" \ + % (staging_kernel_dir, hdddir) + tmp = exec_cmd(install_cmd) + + install_cmd = "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" \ + % (staging_data_dir, hdddir) + tmp = exec_cmd(install_cmd) + + du_cmd = "du -bks %s" % hdddir + rc, out = exec_cmd(du_cmd) + blocks = int(out.split()[0]) + + extra_blocks = part.get_extra_block_count(blocks) + + if extra_blocks < BOOTDD_EXTRA_SPACE: + extra_blocks = BOOTDD_EXTRA_SPACE + + blocks += extra_blocks + + msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ + (extra_blocks, part.mountpoint, blocks)) + + # Ensure total sectors is an integral number of sectors per + # track or mcopy will complain. Sectors are 512 bytes, and we + # generate images with 32 sectors per track. This calculation is + # done in blocks, thus the mod by 16 instead of 32. + blocks += (16 - (blocks % 16)) + + # dosfs image, created by mkdosfs + bootimg = "%s/boot.img" % cr_workdir + + dosfs_cmd = "mkdosfs -n boot -S 512 -C %s %d" % (bootimg, blocks) + exec_native_cmd(dosfs_cmd, native_sysroot) + + mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) + exec_native_cmd(mcopy_cmd, native_sysroot) + + syslinux_cmd = "syslinux %s" % bootimg + exec_native_cmd(syslinux_cmd, native_sysroot) + + chmod_cmd = "chmod 644 %s" % bootimg + exec_cmd(chmod_cmd) + + du_cmd = "du -Lbms %s" % bootimg + rc, out = exec_cmd(du_cmd) + bootimg_size = out.split()[0] + + part.set_size(bootimg_size) + part.set_source_file(bootimg) + + diff --git a/scripts/lib/mic/plugins/source/rootfs.py b/scripts/lib/mic/plugins/source/rootfs.py new file mode 100644 index 0000000000..75999e03d2 --- /dev/null +++ b/scripts/lib/mic/plugins/source/rootfs.py @@ -0,0 +1,71 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'rootfs' source plugin class for 'wic' +# +# AUTHORS +# Tom Zanussi +# Joao Henrique Ferreira de Freitas +# + +import os +import shutil +import re +import tempfile + +from mic import kickstart, chroot, msger +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.direct as direct +from mic.pluginbase import SourcePlugin +from mic.utils.oe.misc import * +from mic.imager.direct import DirectImageCreator + +class RootfsPlugin(SourcePlugin): + name = 'rootfs' + + @classmethod + def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, + kernel_dir, krootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + In this case, prepare content for legacy bios boot partition. + """ + if part.rootfs is None: + if not 'ROOTFS_DIR' in krootfs_dir: + msg = "Couldn't find --rootfs-dir, exiting" + msger.error(msg) + rootfs_dir = krootfs_dir['ROOTFS_DIR'] + else: + if part.rootfs in krootfs_dir: + rootfs_dir = krootfs_dir[part.rootfs] + elif os.path.isdir(part.rootfs): + rootfs_dir = part.rootfs + else: + msg = "Couldn't find --rootfs-dir=%s connection" + msg += " or it is not a valid path, exiting" + msger.error(msg % part.rootfs) + + part.set_rootfs(rootfs_dir) + part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, native_sysroot) + diff --git a/scripts/lib/mic/plugins/source/uboot.py b/scripts/lib/mic/plugins/source/uboot.py new file mode 100644 index 0000000000..57cb3cf8fe --- /dev/null +++ b/scripts/lib/mic/plugins/source/uboot.py @@ -0,0 +1,173 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Enea AB. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the 'uboot' source plugin class for 'wic' +# +# AUTHORS +# Adrian Calianu +# + +import os +import shutil +import re +import tempfile + +from mic import kickstart, chroot, msger +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.direct as direct +from mic.pluginbase import SourcePlugin +from mic.utils.oe.misc import * +from mic.imager.direct import DirectImageCreator + +def create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot): + # In order to have a full control over rootfs we will make a local copy under workdir + # and change rootfs_dir to new location. + # In this way we can install more than one ROOTFS_DIRs and/or use + # an empty rootfs to install packages, so a rootfs could be generated only from pkgs + # TBD: create workdir/rootfs ; copy rootfs-> workdir/rootfs; set rootfs=workdir/rootfs + + cr_workdir = os.path.abspath(cr_workdir) + new_rootfs_dir = "%s/rootfs_%s" % (cr_workdir, creator.name) + + rootfs_exists = 1 + if part.rootfs is None: + if not 'ROOTFS_DIR' in krootfs_dir: + msg = "Couldn't find --rootfs-dir, exiting, " + msger.info(msg) + rootfs_exists = 0 + rootfs_dir = krootfs_dir['ROOTFS_DIR'] + creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir + else: + if part.rootfs in krootfs_dir: + rootfs_dir = krootfs_dir[part.rootfs] + creator.rootfs_dir[part.rootfs] = new_rootfs_dir + elif os.path.isdir(part.rootfs): + rootfs_dir = part.rootfs + part.rootfs = new_rootfs_dir + else: + msg = "Couldn't find --rootfs-dir=%s connection" + msg += " or it is not a valid path, exiting" + msger.info(msg % part.rootfs) + rootfs_exists = 0 + creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir + + pseudox = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot + pseudox += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % new_rootfs_dir + pseudox += "export PSEUDO_PASSWD=%s;" % new_rootfs_dir + pseudox += "export PSEUDO_NOSYMLINKEXP=1;" + pseudox += "%s/usr/bin/pseudo " % native_sysroot + + mkdir_cmd = "mkdir %s" % (new_rootfs_dir) + # rc, out = exec_native_cmd(pseudox + mkdir_cmd, native_sysroot) + rc, out = exec_cmd(mkdir_cmd, True) + + if rootfs_exists == 1 and os.path.isdir(rootfs_dir): + defpath = os.environ['PATH'] + os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" + + rootfs_dir = os.path.abspath(rootfs_dir) + + pseudoc = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot + pseudoc += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir + pseudoc += "export PSEUDO_PASSWD=%s;" % rootfs_dir + pseudoc += "export PSEUDO_NOSYMLINKEXP=1;" + pseudoc += "%s/usr/bin/pseudo " % native_sysroot + + tarc_cmd = "tar cvpf %s/rootfs.tar -C %s ." % (cr_workdir, rootfs_dir) + rc, out = exec_native_cmd(pseudoc + tarc_cmd, native_sysroot) + + tarx_cmd = "tar xpvf %s/rootfs.tar -C %s" % (cr_workdir, new_rootfs_dir) + rc, out = exec_native_cmd(pseudox + tarx_cmd, native_sysroot) + + rm_cmd = "rm %s/rootfs.tar" % cr_workdir + rc, out = exec_cmd(rm_cmd, True) + + os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" + + return new_rootfs_dir + +class UBootPlugin(SourcePlugin): + name = 'uboot' + + @classmethod + def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, krootfs_dir, + bootimg_dir, kernel_dir, native_sysroot): + """ + Called before all partitions have been prepared and assembled into a + disk image. Intall packages based on wic configuration. + """ + + # set new rootfs_dir + rootfs_dir = create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot) + + # wks file parsing + packages = kickstart.get_packages(creator.ks) + + # wic.conf file parsing = found under 'creator' + local_pkgs_path = creator._local_pkgs_path + repourl = creator.repourl + pkgmgr = creator.pkgmgr_name + + # install packages + if packages and pkgmgr in ["opkg"]: + if len(repourl) > 0 : + part.install_pkgs_ipk(cr_workdir, oe_builddir, rootfs_dir, native_sysroot, + packages, repourl) + else: + msger.error("No packages repository provided in wic.conf") + + @classmethod + def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, + kernel_dir, krootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + In this case, prepare content for legacy bios boot partition. + """ + if part.rootfs is None: + if not 'ROOTFS_DIR' in krootfs_dir: + msg = "Couldn't find --rootfs-dir, exiting" + msger.error(msg) + rootfs_dir = krootfs_dir['ROOTFS_DIR'] + else: + if part.rootfs in krootfs_dir: + rootfs_dir = krootfs_dir[part.rootfs] + elif os.path.isdir(part.rootfs): + rootfs_dir = part.rootfs + else: + msg = "Couldn't find --rootfs-dir=%s connection" + msg += " or it is not a valid path, exiting" + msger.error(msg % part.rootfs) + + part.set_rootfs(rootfs_dir) + + # change partition label wich will reflect into the final rootfs image name + part.label = "%s_%s" % (part.label, cr.name) + + defpath = os.environ['PATH'] + os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" + + part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, native_sysroot) + part.prepare_for_uboot(cr.target_arch,cr_workdir, oe_builddir, rootfs_dir, native_sysroot) + + os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" diff --git a/scripts/lib/mic/rt_util.py b/scripts/lib/mic/rt_util.py new file mode 100644 index 0000000000..2a31f4a218 --- /dev/null +++ b/scripts/lib/mic/rt_util.py @@ -0,0 +1,223 @@ +#!/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 sys +import glob +import re +import shutil +import subprocess + +from mic import bootstrap, msger +from mic.conf import configmgr +from mic.utils import errors, proxy +from mic.utils.fs_related import find_binary_path, makedirs +from mic.chroot import setup_chrootenv, cleanup_chrootenv + +expath = lambda p: os.path.abspath(os.path.expanduser(p)) + +def bootstrap_mic(argv=None): + + + def mychroot(): + os.chroot(rootdir) + os.chdir(cwd) + + # by default, sys.argv is used to run mic in bootstrap + if not argv: + argv = sys.argv + if argv[0] not in ('/usr/bin/mic', 'mic'): + argv[0] = '/usr/bin/mic' + + cropts = configmgr.create + bsopts = configmgr.bootstrap + distro = bsopts['distro_name'].lower() + + rootdir = bsopts['rootdir'] + pkglist = bsopts['packages'] + cwd = os.getcwd() + + # create bootstrap and run mic in bootstrap + bsenv = bootstrap.Bootstrap(rootdir, distro, cropts['arch']) + bsenv.logfile = cropts['logfile'] + # rootdir is regenerated as a temp dir + rootdir = bsenv.rootdir + + if 'optional' in bsopts: + optlist = bsopts['optional'] + else: + optlist = [] + + try: + msger.info("Creating %s bootstrap ..." % distro) + bsenv.create(cropts['repomd'], pkglist, optlist) + + # bootstrap is relocated under "bootstrap" + if os.path.exists(os.path.join(rootdir, "bootstrap")): + rootdir = os.path.join(rootdir, "bootstrap") + + bsenv.dirsetup(rootdir) + sync_mic(rootdir) + + #FIXME: sync the ks file to bootstrap + if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)): + safecopy(configmgr._ksconf, rootdir) + + msger.info("Start mic in bootstrap: %s\n" % rootdir) + bindmounts = get_bindmounts(cropts) + ret = bsenv.run(argv, cwd, rootdir, bindmounts) + + except errors.BootstrapError, err: + msger.warning('\n%s' % err) + if msger.ask("Switch to native mode and continue?"): + return + raise + except RuntimeError, err: + #change exception type but keep the trace back + value, tb = sys.exc_info()[1:] + raise errors.BootstrapError, value, tb + else: + sys.exit(ret) + finally: + bsenv.cleanup() + +def get_bindmounts(cropts): + binddirs = [ + os.getcwd(), + cropts['tmpdir'], + cropts['cachedir'], + cropts['outdir'], + cropts['local_pkgs_path'], + ] + bindfiles = [ + cropts['logfile'], + configmgr._ksconf, + ] + + for lrepo in cropts['localrepos']: + binddirs.append(lrepo) + + bindlist = map(expath, filter(None, binddirs)) + bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles))) + bindlist = sorted(set(bindlist)) + bindmounts = ';'.join(bindlist) + return bindmounts + + +def get_mic_binpath(): + fp = None + try: + import pkg_resources # depends on 'setuptools' + except ImportError: + pass + else: + dist = pkg_resources.get_distribution('mic') + # the real script is under EGG_INFO/scripts + if dist.has_metadata('scripts/mic'): + fp = os.path.join(dist.egg_info, "scripts/mic") + + if fp: + return fp + + # not found script if 'flat' egg installed + try: + return find_binary_path('mic') + except errors.CreatorError: + raise errors.BootstrapError("Can't find mic binary in host OS") + + +def get_mic_modpath(): + try: + import mic + except ImportError: + raise errors.BootstrapError("Can't find mic module in host OS") + path = os.path.abspath(mic.__file__) + return os.path.dirname(path) + +def get_mic_libpath(): + # TBD: so far mic lib path is hard coded + return "/usr/lib/mic" + +# the hard code path is prepared for bootstrap +def sync_mic(bootstrap, binpth = '/usr/bin/mic', + libpth='/usr/lib', + pylib = '/usr/lib/python2.7/site-packages', + conf = '/etc/mic/mic.conf'): + _path = lambda p: os.path.join(bootstrap, p.lstrip('/')) + + micpaths = { + 'binpth': get_mic_binpath(), + 'libpth': get_mic_libpath(), + 'pylib': get_mic_modpath(), + 'conf': '/etc/mic/mic.conf', + } + + if not os.path.exists(_path(pylib)): + pyptn = '/usr/lib/python?.?/site-packages' + pylibs = glob.glob(_path(pyptn)) + if pylibs: + pylib = pylibs[0].replace(bootstrap, '') + else: + raise errors.BootstrapError("Can't find python site dir in: %s" % + bootstrap) + + for key, value in micpaths.items(): + try: + safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"]) + except (OSError, IOError), err: + raise errors.BootstrapError(err) + + # auto select backend + conf_str = file(_path(conf)).read() + conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str) + with open(_path(conf), 'w') as wf: + wf.write(conf_str) + + # chmod +x /usr/bin/mic + os.chmod(_path(binpth), 0777) + + # correct python interpreter + mic_cont = file(_path(binpth)).read() + mic_cont = "#!/usr/bin/python\n" + mic_cont + with open(_path(binpth), 'w') as wf: + wf.write(mic_cont) + + +def safecopy(src, dst, symlinks=False, ignore_ptns=()): + if os.path.isdir(src): + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + if os.path.exists(dst): + shutil.rmtree(dst, ignore_errors=True) + + src = src.rstrip('/') + # check common prefix to ignore copying itself + if dst.startswith(src + '/'): + ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ] + + ignores = shutil.ignore_patterns(*ignore_ptns) + try: + shutil.copytree(src, dst, symlinks, ignores) + except (OSError, IOError): + shutil.rmtree(dst, ignore_errors=True) + raise + else: + if not os.path.isdir(dst): + makedirs(os.path.dirname(dst)) + + shutil.copy2(src, dst) diff --git a/scripts/lib/mic/test b/scripts/lib/mic/test new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/scripts/lib/mic/test @@ -0,0 +1 @@ +test diff --git a/scripts/lib/mic/utils/BmapCreate.py b/scripts/lib/mic/utils/BmapCreate.py new file mode 100644 index 0000000000..65b19a5f46 --- /dev/null +++ b/scripts/lib/mic/utils/BmapCreate.py @@ -0,0 +1,298 @@ +""" This module implements the block map (bmap) creation functionality and +provides the corresponding API in form of the 'BmapCreate' class. + +The idea is that while images files may generally be very large (e.g., 4GiB), +they may nevertheless contain only little real data, e.g., 512MiB. This data +are files, directories, file-system meta-data, partition table, etc. When +copying the image to the target device, you do not have to copy all the 4GiB of +data, you can copy only 512MiB of it, which is 4 times less, so copying should +presumably be 4 times faster. + +The block map file is an XML file which contains a list of blocks which have to +be copied to the target device. The other blocks are not used and there is no +need to copy them. The XML file also contains some additional information like +block size, image size, count of mapped blocks, etc. There are also many +commentaries, so it is human-readable. + +The image has to be a sparse file. Generally, this means that when you generate +this image file, you should start with a huge sparse file which contains a +single hole spanning the entire file. Then you should partition it, write all +the data (probably by means of loop-back mounting the image or parts of it), +etc. The end result should be a sparse file where mapped areas represent useful +parts of the image and holes represent useless parts of the image, which do not +have to be copied when copying the image to the target device. + +This module uses the FIBMAP ioctl to detect holes. """ + +# Disable the following pylint recommendations: +# * Too many instance attributes - R0902 +# * Too few public methods - R0903 +# pylint: disable=R0902,R0903 + +import hashlib +from mic.utils.misc import human_size +from mic.utils import Fiemap + +# The bmap format version we generate +SUPPORTED_BMAP_VERSION = "1.3" + +_BMAP_START_TEMPLATE = \ +""" + + + + + %u + + + %u + + + %u + +""" + +class Error(Exception): + """ A class for exceptions generated by this module. We currently support + only one type of exceptions, and we basically throw human-readable problem + description in case of errors. """ + pass + +class BmapCreate: + """ This class implements the bmap creation functionality. To generate a + bmap for an image (which is supposedly a sparse file), you should first + create an instance of 'BmapCreate' and provide: + + * full path or a file-like object of the image to create bmap for + * full path or a file object to use for writing the results to + + Then you should invoke the 'generate()' method of this class. It will use + the FIEMAP ioctl to generate the bmap. """ + + def _open_image_file(self): + """ Open the image file. """ + + try: + self._f_image = open(self._image_path, 'rb') + except IOError as err: + raise Error("cannot open image file '%s': %s" \ + % (self._image_path, err)) + + self._f_image_needs_close = True + + def _open_bmap_file(self): + """ Open the bmap file. """ + + try: + self._f_bmap = open(self._bmap_path, 'w+') + except IOError as err: + raise Error("cannot open bmap file '%s': %s" \ + % (self._bmap_path, err)) + + self._f_bmap_needs_close = True + + def __init__(self, image, bmap): + """ Initialize a class instance: + * image - full path or a file-like object of the image to create bmap + for + * bmap - full path or a file object to use for writing the resulting + bmap to """ + + self.image_size = None + self.image_size_human = None + self.block_size = None + self.blocks_cnt = None + self.mapped_cnt = None + self.mapped_size = None + self.mapped_size_human = None + self.mapped_percent = None + + self._mapped_count_pos1 = None + self._mapped_count_pos2 = None + self._sha1_pos = None + + self._f_image_needs_close = False + self._f_bmap_needs_close = False + + if hasattr(image, "read"): + self._f_image = image + self._image_path = image.name + else: + self._image_path = image + self._open_image_file() + + if hasattr(bmap, "read"): + self._f_bmap = bmap + self._bmap_path = bmap.name + else: + self._bmap_path = bmap + self._open_bmap_file() + + self.fiemap = Fiemap.Fiemap(self._f_image) + + self.image_size = self.fiemap.image_size + self.image_size_human = human_size(self.image_size) + if self.image_size == 0: + raise Error("cannot generate bmap for zero-sized image file '%s'" \ + % self._image_path) + + self.block_size = self.fiemap.block_size + self.blocks_cnt = self.fiemap.blocks_cnt + + def _bmap_file_start(self): + """ A helper function which generates the starting contents of the + block map file: the header comment, image size, block size, etc. """ + + # We do not know the amount of mapped blocks at the moment, so just put + # whitespaces instead of real numbers. Assume the longest possible + # numbers. + mapped_count = ' ' * len(str(self.image_size)) + mapped_size_human = ' ' * len(self.image_size_human) + + xml = _BMAP_START_TEMPLATE \ + % (SUPPORTED_BMAP_VERSION, self.image_size_human, + self.image_size, self.block_size, self.blocks_cnt) + xml += " \n" % (mapped_size_human, 100.0) + xml += " " + + self._f_bmap.write(xml) + self._mapped_count_pos2 = self._f_bmap.tell() + + xml = "%s \n\n" % mapped_count + + # pylint: disable=C0301 + xml += " \n" + xml += " " + + self._f_bmap.write(xml) + self._sha1_pos = self._f_bmap.tell() + + xml = "0" * 40 + " \n\n" + xml += " \n" + xml += " \n" + # pylint: enable=C0301 + + self._f_bmap.write(xml) + + def _bmap_file_end(self): + """ A helper function which generates the final parts of the block map + file: the ending tags and the information about the amount of mapped + blocks. """ + + xml = " \n" + xml += "\n" + + self._f_bmap.write(xml) + + self._f_bmap.seek(self._mapped_count_pos1) + self._f_bmap.write("%s or %.1f%%" % \ + (self.mapped_size_human, self.mapped_percent)) + + self._f_bmap.seek(self._mapped_count_pos2) + self._f_bmap.write("%u" % self.mapped_cnt) + + self._f_bmap.seek(0) + sha1 = hashlib.sha1(self._f_bmap.read()).hexdigest() + self._f_bmap.seek(self._sha1_pos) + self._f_bmap.write("%s" % sha1) + + def _calculate_sha1(self, first, last): + """ A helper function which calculates SHA1 checksum for the range of + blocks of the image file: from block 'first' to block 'last'. """ + + start = first * self.block_size + end = (last + 1) * self.block_size + + self._f_image.seek(start) + hash_obj = hashlib.new("sha1") + + chunk_size = 1024*1024 + to_read = end - start + read = 0 + + while read < to_read: + if read + chunk_size > to_read: + chunk_size = to_read - read + chunk = self._f_image.read(chunk_size) + hash_obj.update(chunk) + read += chunk_size + + return hash_obj.hexdigest() + + def generate(self, include_checksums = True): + """ Generate bmap for the image file. If 'include_checksums' is 'True', + also generate SHA1 checksums for block ranges. """ + + # Save image file position in order to restore it at the end + image_pos = self._f_image.tell() + + self._bmap_file_start() + + # Generate the block map and write it to the XML block map + # file as we go. + self.mapped_cnt = 0 + for first, last in self.fiemap.get_mapped_ranges(0, self.blocks_cnt): + self.mapped_cnt += last - first + 1 + if include_checksums: + sha1 = self._calculate_sha1(first, last) + sha1 = " sha1=\"%s\"" % sha1 + else: + sha1 = "" + + if first != last: + self._f_bmap.write(" %s-%s \n" \ + % (sha1, first, last)) + else: + self._f_bmap.write(" %s \n" \ + % (sha1, first)) + + self.mapped_size = self.mapped_cnt * self.block_size + self.mapped_size_human = human_size(self.mapped_size) + self.mapped_percent = (self.mapped_cnt * 100.0) / self.blocks_cnt + + self._bmap_file_end() + + try: + self._f_bmap.flush() + except IOError as err: + raise Error("cannot flush the bmap file '%s': %s" \ + % (self._bmap_path, err)) + + self._f_image.seek(image_pos) + + def __del__(self): + """ The class destructor which closes the opened files. """ + + if self._f_image_needs_close: + self._f_image.close() + if self._f_bmap_needs_close: + self._f_bmap.close() diff --git a/scripts/lib/mic/utils/Fiemap.py b/scripts/lib/mic/utils/Fiemap.py new file mode 100644 index 0000000000..f2db6ff0b8 --- /dev/null +++ b/scripts/lib/mic/utils/Fiemap.py @@ -0,0 +1,252 @@ +""" This module implements python API for the FIEMAP ioctl. The FIEMAP ioctl +allows to find holes and mapped areas in a file. """ + +# Note, a lot of code in this module is not very readable, because it deals +# with the rather complex FIEMAP ioctl. To understand the code, you need to +# know the FIEMAP interface, which is documented in the +# Documentation/filesystems/fiemap.txt file in the Linux kernel sources. + +# Disable the following pylint recommendations: +# * Too many instance attributes (R0902) +# pylint: disable=R0902 + +import os +import struct +import array +import fcntl +from mic.utils.misc import get_block_size + +# Format string for 'struct fiemap' +_FIEMAP_FORMAT = "=QQLLLL" +# sizeof(struct fiemap) +_FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT) +# Format string for 'struct fiemap_extent' +_FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL" +# sizeof(struct fiemap_extent) +_FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT) +# The FIEMAP ioctl number +_FIEMAP_IOCTL = 0xC020660B + +# Minimum buffer which is required for 'class Fiemap' to operate +MIN_BUFFER_SIZE = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE +# The default buffer size for 'class Fiemap' +DEFAULT_BUFFER_SIZE = 256 * 1024 + +class Error(Exception): + """ A class for exceptions generated by this module. We currently support + only one type of exceptions, and we basically throw human-readable problem + description in case of errors. """ + pass + +class Fiemap: + """ This class provides API to the FIEMAP ioctl. Namely, it allows to + iterate over all mapped blocks and over all holes. """ + + def _open_image_file(self): + """ Open the image file. """ + + try: + self._f_image = open(self._image_path, 'rb') + except IOError as err: + raise Error("cannot open image file '%s': %s" \ + % (self._image_path, err)) + + self._f_image_needs_close = True + + def __init__(self, image, buf_size = DEFAULT_BUFFER_SIZE): + """ Initialize a class instance. The 'image' argument is full path to + the file to operate on, or a file object to operate on. + + The 'buf_size' argument is the size of the buffer for 'struct + fiemap_extent' elements which will be used when invoking the FIEMAP + ioctl. The larger is the buffer, the less times the FIEMAP ioctl will + be invoked. """ + + self._f_image_needs_close = False + + if hasattr(image, "fileno"): + self._f_image = image + self._image_path = image.name + else: + self._image_path = image + self._open_image_file() + + # Validate 'buf_size' + if buf_size < MIN_BUFFER_SIZE: + raise Error("too small buffer (%d bytes), minimum is %d bytes" \ + % (buf_size, MIN_BUFFER_SIZE)) + + # How many 'struct fiemap_extent' elements fit the buffer + buf_size -= _FIEMAP_SIZE + self._fiemap_extent_cnt = buf_size / _FIEMAP_EXTENT_SIZE + self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE + self._buf_size += _FIEMAP_SIZE + + # Allocate a mutable buffer for the FIEMAP ioctl + self._buf = array.array('B', [0] * self._buf_size) + + self.image_size = os.fstat(self._f_image.fileno()).st_size + + try: + self.block_size = get_block_size(self._f_image) + except IOError as err: + raise Error("cannot get block size for '%s': %s" \ + % (self._image_path, err)) + + self.blocks_cnt = self.image_size + self.block_size - 1 + self.blocks_cnt /= self.block_size + + # Synchronize the image file to make sure FIEMAP returns correct values + try: + self._f_image.flush() + except IOError as err: + raise Error("cannot flush image file '%s': %s" \ + % (self._image_path, err)) + try: + os.fsync(self._f_image.fileno()), + except OSError as err: + raise Error("cannot synchronize image file '%s': %s " \ + % (self._image_path, err.strerror)) + + # Check if the FIEMAP ioctl is supported + self.block_is_mapped(0) + + def __del__(self): + """ The class destructor which closes the opened files. """ + + if self._f_image_needs_close: + self._f_image.close() + + def _invoke_fiemap(self, block, count): + """ Invoke the FIEMAP ioctl for 'count' blocks of the file starting from + block number 'block'. + + The full result of the operation is stored in 'self._buf' on exit. + Returns the unpacked 'struct fiemap' data structure in form of a python + list (just like 'struct.upack()'). """ + + if block < 0 or block >= self.blocks_cnt: + raise Error("bad block number %d, should be within [0, %d]" \ + % (block, self.blocks_cnt)) + + # Initialize the 'struct fiemap' part of the buffer + struct.pack_into(_FIEMAP_FORMAT, self._buf, 0, block * self.block_size, + count * self.block_size, 0, 0, + self._fiemap_extent_cnt, 0) + + try: + fcntl.ioctl(self._f_image, _FIEMAP_IOCTL, self._buf, 1) + except IOError as err: + error_msg = "the FIEMAP ioctl failed for '%s': %s" \ + % (self._image_path, err) + if err.errno == os.errno.EPERM or err.errno == os.errno.EACCES: + # The FIEMAP ioctl was added in kernel version 2.6.28 in 2008 + error_msg += " (looks like your kernel does not support FIEMAP)" + + raise Error(error_msg) + + return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE]) + + def block_is_mapped(self, block): + """ This function returns 'True' if block number 'block' of the image + file is mapped and 'False' otherwise. """ + + struct_fiemap = self._invoke_fiemap(block, 1) + + # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field. + # If it contains zero, the block is not mapped, otherwise it is + # mapped. + return bool(struct_fiemap[3]) + + def block_is_unmapped(self, block): + """ This function returns 'True' if block number 'block' of the image + file is not mapped (hole) and 'False' otherwise. """ + + return not self.block_is_mapped(block) + + def _unpack_fiemap_extent(self, index): + """ Unpack a 'struct fiemap_extent' structure object number 'index' + from the internal 'self._buf' buffer. """ + + offset = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE * index + return struct.unpack(_FIEMAP_EXTENT_FORMAT, + self._buf[offset : offset + _FIEMAP_EXTENT_SIZE]) + + def _do_get_mapped_ranges(self, start, count): + """ Implements most the functionality for the 'get_mapped_ranges()' + generator: invokes the FIEMAP ioctl, walks through the mapped + extents and yields mapped block ranges. However, the ranges may be + consecutive (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()' + simply merges them. """ + + block = start + while block < start + count: + struct_fiemap = self._invoke_fiemap(block, count) + + mapped_extents = struct_fiemap[3] + if mapped_extents == 0: + # No more mapped blocks + return + + extent = 0 + while extent < mapped_extents: + fiemap_extent = self._unpack_fiemap_extent(extent) + + # Start of the extent + extent_start = fiemap_extent[0] + # Starting block number of the extent + extent_block = extent_start / self.block_size + # Length of the extent + extent_len = fiemap_extent[2] + # Count of blocks in the extent + extent_count = extent_len / self.block_size + + # Extent length and offset have to be block-aligned + assert extent_start % self.block_size == 0 + assert extent_len % self.block_size == 0 + + if extent_block > start + count - 1: + return + + first = max(extent_block, block) + last = min(extent_block + extent_count, start + count) - 1 + yield (first, last) + + extent += 1 + + block = extent_block + extent_count + + def get_mapped_ranges(self, start, count): + """ A generator which yields ranges of mapped blocks in the file. The + ranges are tuples of 2 elements: [first, last], where 'first' is the + first mapped block and 'last' is the last mapped block. + + The ranges are yielded for the area of the file of size 'count' blocks, + starting from block 'start'. """ + + iterator = self._do_get_mapped_ranges(start, count) + + first_prev, last_prev = iterator.next() + + for first, last in iterator: + if last_prev == first - 1: + last_prev = last + else: + yield (first_prev, last_prev) + first_prev, last_prev = first, last + + yield (first_prev, last_prev) + + def get_unmapped_ranges(self, start, count): + """ Just like 'get_mapped_ranges()', but yields unmapped block ranges + instead (holes). """ + + hole_first = start + for first, last in self._do_get_mapped_ranges(start, count): + if first > hole_first: + yield (hole_first, first - 1) + + hole_first = last + 1 + + if hole_first < start + count: + yield (hole_first, start + count - 1) diff --git a/scripts/lib/mic/utils/__init__.py b/scripts/lib/mic/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/lib/mic/utils/cmdln.py b/scripts/lib/mic/utils/cmdln.py new file mode 100644 index 0000000000..b099473ee4 --- /dev/null +++ b/scripts/lib/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 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 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 + 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 = + if opts.verbose: + print "lots of debugging output..." + # args = + 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: + (, ) + "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 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) -> + (, )""" + # 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/scripts/lib/mic/utils/errors.py b/scripts/lib/mic/utils/errors.py new file mode 100644 index 0000000000..8d720f9080 --- /dev/null +++ b/scripts/lib/mic/utils/errors.py @@ -0,0 +1,71 @@ +#!/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 = '' + + def __init__(self, msg): + self.msg = msg + + def __str__(self): + if isinstance(self.msg, unicode): + self.msg = self.msg.encode('utf-8', 'ignore') + else: + self.msg = str(self.msg) + return self.keyword + self.msg + +class Usage(CreatorError): + keyword = '' + + def __str__(self): + if isinstance(self.msg, unicode): + self.msg = self.msg.encode('utf-8', 'ignore') + else: + self.msg = str(self.msg) + return self.keyword + self.msg + ', please use "--help" for more info' + +class Abort(CreatorError): + keyword = '' + +class ConfigError(CreatorError): + keyword = '' + +class KsError(CreatorError): + keyword = '' + +class RepoError(CreatorError): + keyword = '' + +class RpmError(CreatorError): + keyword = '' + +class MountError(CreatorError): + keyword = '' + +class SnapshotError(CreatorError): + keyword = '' + +class SquashfsError(CreatorError): + keyword = '' + +class BootstrapError(CreatorError): + keyword = '' + +class RuntimeError(CreatorError): + keyword = '' diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py new file mode 100644 index 0000000000..dd420e88dc --- /dev/null +++ b/scripts/lib/mic/utils/fs_related.py @@ -0,0 +1,1060 @@ +#!/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 +import uuid + +from mic import msger +from mic.utils import runner +from mic.utils.errors import * +from mic.utils.oe.misc import * + +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 + + print "External command '%s' not found, exiting." % binary + print " (Please install '%s' on your host system)" % binary + sys.exit(1) + +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.root = os.path.abspath(os.path.expanduser(chroot)) + self.option = option + + self.orig_src = self.src = src + if os.path.islink(src): + self.src = os.readlink(src) + if not self.src.startswith('/'): + self.src = os.path.abspath(os.path.join(os.path.dirname(src), + self.src)) + + if not dest: + dest = self.src + self.dest = os.path.join(self.root, dest.lstrip('/')) + + 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 + if os.path.islink(self.orig_src): + dest = os.path.join(self.root, self.orig_src.lstrip('/')) + if not os.path.exists(dest): + os.symlink(self.src, dest) + + 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 DiskImage(Disk): + """ + A Disk backed by a file. + """ + def __init__(self, image_file, size): + Disk.__init__(self, size) + self.image_file = image_file + + def exists(self): + return os.path.exists(self.image_file) + + def create(self): + if self.device is not None: + return + + blocks = self.size / 1024 + if self.size - blocks * 1024: + blocks += 1 + + # create disk image + dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=1" % \ + (self.image_file, blocks) + rc, out = exec_cmd(dd_cmd) + + self.device = self.image_file + + +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) + + if size <= 0: + size = 1 + try: + os.ftruncate(fd, size) + except: + # may be limited by 2G in 32bit env + os.ftruncate(fd, 2**31L) + + 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 = str(uuid.uuid4()) + self.skipformat = skipformat + self.fsopts = fsopts + self.extopts = None + 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)) + cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", + str(self.blocksize), "-U", self.uuid] + if self.extopts: + cmdlist.extend(self.extopts.split()) + cmdlist.extend([self.disk.device]) + + rc, errout = runner.runtool(cmdlist, catch=2) + if rc != 0: + raise MountError("Error creating %s filesystem on disk %s:\n%s" % + (self.fstype, self.disk.device, errout)) + + if not self.extopts: + 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("/", "") + rand1 = random.randint(0, 2**16 - 1) + rand2 = random.randint(0, 2**16 - 1) + self.uuid = "%04X-%04X" % (rand1, rand2) + 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.replace("-", ""), 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") + + def register(self, device): + self.device = device + self.loopid = None + self.created = True + + def reg_atexit(self): + import atexit + atexit.register(self.close) + + def _genloopid(self): + import glob + if not glob.glob("/dev/loop[0-9]*"): + return 10 + + 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 + + mknod = find_binary_path('mknod') + rc = runner.show([mknod, '-m664', self.device, 'b', '7', str(self.loopid)]) + if rc != 0: + 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) + elif self.loopid: + os.unlink(self.device) + +DEVICE_PIDFILE_DIR = "/var/tmp/mic/device" +DEVICE_LOCKFILE = "/var/lock/__mic_loopdev.lock" + +def get_loop_device(losetupcmd, lofile): + global DEVICE_PIDFILE_DIR + global DEVICE_LOCKFILE + + import fcntl + makedirs(os.path.dirname(DEVICE_LOCKFILE)) + fp = open(DEVICE_LOCKFILE, 'w') + fcntl.flock(fp, fcntl.LOCK_EX) + try: + loopdev = None + devinst = LoopDevice() + + # clean up left loop device first + clean_loop_devices() + + # provide an avaible loop device + rc, out = runner.runtool([losetupcmd, "--find"]) + if rc == 0: + loopdev = out.split()[0] + devinst.register(loopdev) + if not loopdev or not os.path.exists(loopdev): + devinst.create() + loopdev = devinst.device + + # setup a loop device for image file + rc = runner.show([losetupcmd, loopdev, lofile]) + if rc != 0: + raise MountError("Failed to setup loop device for '%s'" % lofile) + + devinst.reg_atexit() + + # try to save device and pid + makedirs(DEVICE_PIDFILE_DIR) + pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev)) + if os.path.exists(pidfile): + os.unlink(pidfile) + with open(pidfile, 'w') as wf: + wf.write(str(os.getpid())) + + except MountError, err: + raise CreatorError("%s" % str(err)) + except: + raise + finally: + try: + fcntl.flock(fp, fcntl.LOCK_UN) + fp.close() + os.unlink(DEVICE_LOCKFILE) + except: + pass + + return loopdev + +def clean_loop_devices(piddir=DEVICE_PIDFILE_DIR): + if not os.path.exists(piddir) or not os.path.isdir(piddir): + return + + for loopdev in os.listdir(piddir): + pidfile = os.path.join(piddir, loopdev) + try: + with open(pidfile, 'r') as rf: + devpid = int(rf.read()) + except: + devpid = None + + # if the process using this device is alive, skip it + if not devpid or os.path.exists(os.path.join('/proc', str(devpid))): + continue + + # try to clean it up + try: + devinst = LoopDevice() + devinst.register(os.path.join('/dev', loopdev)) + devinst.cleanup() + os.unlink(pidfile) + except: + pass + diff --git a/scripts/lib/mic/utils/gpt_parser.py b/scripts/lib/mic/utils/gpt_parser.py new file mode 100644 index 0000000000..5d43b70778 --- /dev/null +++ b/scripts/lib/mic/utils/gpt_parser.py @@ -0,0 +1,331 @@ +#!/usr/bin/python -tt +# +# Copyright (c) 2013 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. + +""" This module implements a simple GPT partitions parser which can read the +GPT header and the GPT partition table. """ + +import struct +import uuid +import binascii +from mic.utils.errors import MountError + +_GPT_HEADER_FORMAT = "<8s4sIIIQQQQ16sQIII" +_GPT_HEADER_SIZE = struct.calcsize(_GPT_HEADER_FORMAT) +_GPT_ENTRY_FORMAT = "<16s16sQQQ72s" +_GPT_ENTRY_SIZE = struct.calcsize(_GPT_ENTRY_FORMAT) +_SUPPORTED_GPT_REVISION = '\x00\x00\x01\x00' + +def _stringify_uuid(binary_uuid): + """ A small helper function to transform a binary UUID into a string + format. """ + + uuid_str = str(uuid.UUID(bytes_le = binary_uuid)) + + return uuid_str.upper() + +def _calc_header_crc(raw_hdr): + """ Calculate GPT header CRC32 checksum. The 'raw_hdr' parameter has to + be a list or a tuple containing all the elements of the GPT header in a + "raw" form, meaning that it should simply contain "unpacked" disk data. + """ + + raw_hdr = list(raw_hdr) + raw_hdr[3] = 0 + raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) + + return binascii.crc32(raw_hdr) & 0xFFFFFFFF + +def _validate_header(raw_hdr): + """ Validate the GPT header. The 'raw_hdr' parameter has to be a list or a + tuple containing all the elements of the GPT header in a "raw" form, + meaning that it should simply contain "unpacked" disk data. """ + + # Validate the signature + if raw_hdr[0] != 'EFI PART': + raise MountError("GPT partition table not found") + + # Validate the revision + if raw_hdr[1] != _SUPPORTED_GPT_REVISION: + raise MountError("Unsupported GPT revision '%s', supported revision " \ + "is '%s'" % \ + (binascii.hexlify(raw_hdr[1]), + binascii.hexlify(_SUPPORTED_GPT_REVISION))) + + # Validate header size + if raw_hdr[2] != _GPT_HEADER_SIZE: + raise MountError("Bad GPT header size: %d bytes, expected %d" % \ + (raw_hdr[2], _GPT_HEADER_SIZE)) + + crc = _calc_header_crc(raw_hdr) + if raw_hdr[3] != crc: + raise MountError("GPT header crc mismatch: %#x, should be %#x" % \ + (crc, raw_hdr[3])) + +class GptParser: + """ GPT partition table parser. Allows reading the GPT header and the + partition table, as well as modifying the partition table records. """ + + def __init__(self, disk_path, sector_size = 512): + """ The class constructor which accepts the following parameters: + * disk_path - full path to the disk image or device node + * sector_size - size of a disk sector in bytes """ + + self.sector_size = sector_size + self.disk_path = disk_path + + try: + self._disk_obj = open(disk_path, 'r+b') + except IOError as err: + raise MountError("Cannot open file '%s' for reading GPT " \ + "partitions: %s" % (disk_path, err)) + + def __del__(self): + """ The class destructor. """ + + self._disk_obj.close() + + def _read_disk(self, offset, size): + """ A helper function which reads 'size' bytes from offset 'offset' of + the disk and checks all the error conditions. """ + + self._disk_obj.seek(offset) + try: + data = self._disk_obj.read(size) + except IOError as err: + raise MountError("cannot read from '%s': %s" % \ + (self.disk_path, err)) + + if len(data) != size: + raise MountError("cannot read %d bytes from offset '%d' of '%s', " \ + "read only %d bytes" % \ + (size, offset, self.disk_path, len(data))) + + return data + + def _write_disk(self, offset, buf): + """ A helper function which writes buffer 'buf' to offset 'offset' of + the disk. This function takes care of unaligned writes and checks all + the error conditions. """ + + # Since we may be dealing with a block device, we only can write in + # 'self.sector_size' chunks. Find the aligned starting and ending + # disk offsets to read. + start = (offset / self.sector_size) * self.sector_size + end = ((start + len(buf)) / self.sector_size + 1) * self.sector_size + + data = self._read_disk(start, end - start) + off = offset - start + data = data[:off] + buf + data[off + len(buf):] + + self._disk_obj.seek(start) + try: + self._disk_obj.write(data) + except IOError as err: + raise MountError("cannot write to '%s': %s" % (self.disk_path, err)) + + def read_header(self, primary = True): + """ Read and verify the GPT header and return a dictionary containing + the following elements: + + 'signature' : header signature + 'revision' : header revision + 'hdr_size' : header size in bytes + 'hdr_crc' : header CRC32 + 'hdr_lba' : LBA of this header + 'hdr_offs' : byte disk offset of this header + 'backup_lba' : backup header LBA + 'backup_offs' : byte disk offset of backup header + 'first_lba' : first usable LBA for partitions + 'first_offs' : first usable byte disk offset for partitions + 'last_lba' : last usable LBA for partitions + 'last_offs' : last usable byte disk offset for partitions + 'disk_uuid' : UUID of the disk + 'ptable_lba' : starting LBA of array of partition entries + 'ptable_offs' : disk byte offset of the start of the partition table + 'ptable_size' : partition table size in bytes + 'entries_cnt' : number of available partition table entries + 'entry_size' : size of a single partition entry + 'ptable_crc' : CRC32 of the partition table + 'primary' : a boolean, if 'True', this is the primary GPT header, + if 'False' - the secondary + 'primary_str' : contains string "primary" if this is the primary GPT + header, and "backup" otherwise + + This dictionary corresponds to the GPT header format. Please, see the + UEFI standard for the description of these fields. + + If the 'primary' parameter is 'True', the primary GPT header is read, + otherwise the backup GPT header is read instead. """ + + # Read and validate the primary GPT header + raw_hdr = self._read_disk(self.sector_size, _GPT_HEADER_SIZE) + raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) + _validate_header(raw_hdr) + primary_str = "primary" + + if not primary: + # Read and validate the backup GPT header + raw_hdr = self._read_disk(raw_hdr[6] * self.sector_size, _GPT_HEADER_SIZE) + raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) + _validate_header(raw_hdr) + primary_str = "backup" + + return { 'signature' : raw_hdr[0], + 'revision' : raw_hdr[1], + 'hdr_size' : raw_hdr[2], + 'hdr_crc' : raw_hdr[3], + 'hdr_lba' : raw_hdr[5], + 'hdr_offs' : raw_hdr[5] * self.sector_size, + 'backup_lba' : raw_hdr[6], + 'backup_offs' : raw_hdr[6] * self.sector_size, + 'first_lba' : raw_hdr[7], + 'first_offs' : raw_hdr[7] * self.sector_size, + 'last_lba' : raw_hdr[8], + 'last_offs' : raw_hdr[8] * self.sector_size, + 'disk_uuid' :_stringify_uuid(raw_hdr[9]), + 'ptable_lba' : raw_hdr[10], + 'ptable_offs' : raw_hdr[10] * self.sector_size, + 'ptable_size' : raw_hdr[11] * raw_hdr[12], + 'entries_cnt' : raw_hdr[11], + 'entry_size' : raw_hdr[12], + 'ptable_crc' : raw_hdr[13], + 'primary' : primary, + 'primary_str' : primary_str } + + def _read_raw_ptable(self, header): + """ Read and validate primary or backup partition table. The 'header' + argument is the GPT header. If it is the primary GPT header, then the + primary partition table is read and validated, otherwise - the backup + one. The 'header' argument is a dictionary which is returned by the + 'read_header()' method. """ + + raw_ptable = self._read_disk(header['ptable_offs'], + header['ptable_size']) + + crc = binascii.crc32(raw_ptable) & 0xFFFFFFFF + if crc != header['ptable_crc']: + raise MountError("Partition table at LBA %d (%s) is corrupted" % \ + (header['ptable_lba'], header['primary_str'])) + + return raw_ptable + + def get_partitions(self, primary = True): + """ This is a generator which parses the GPT partition table and + generates the following dictionary for each partition: + + 'index' : the index of the partition table endry + 'offs' : byte disk offset of the partition table entry + 'type_uuid' : partition type UUID + 'part_uuid' : partition UUID + 'first_lba' : the first LBA + 'last_lba' : the last LBA + 'flags' : attribute flags + 'name' : partition name + 'primary' : a boolean, if 'True', this is the primary partition + table, if 'False' - the secondary + 'primary_str' : contains string "primary" if this is the primary GPT + header, and "backup" otherwise + + This dictionary corresponds to the GPT header format. Please, see the + UEFI standard for the description of these fields. + + If the 'primary' parameter is 'True', partitions from the primary GPT + partition table are generated, otherwise partitions from the backup GPT + partition table are generated. """ + + if primary: + primary_str = "primary" + else: + primary_str = "backup" + + header = self.read_header(primary) + raw_ptable = self._read_raw_ptable(header) + + for index in xrange(0, header['entries_cnt']): + start = header['entry_size'] * index + end = start + header['entry_size'] + raw_entry = struct.unpack(_GPT_ENTRY_FORMAT, raw_ptable[start:end]) + + if raw_entry[2] == 0 or raw_entry[3] == 0: + continue + + part_name = str(raw_entry[5].decode('UTF-16').split('\0', 1)[0]) + + yield { 'index' : index, + 'offs' : header['ptable_offs'] + start, + 'type_uuid' : _stringify_uuid(raw_entry[0]), + 'part_uuid' : _stringify_uuid(raw_entry[1]), + 'first_lba' : raw_entry[2], + 'last_lba' : raw_entry[3], + 'flags' : raw_entry[4], + 'name' : part_name, + 'primary' : primary, + 'primary_str' : primary_str } + + def _change_partition(self, header, entry): + """ A helper function for 'change_partitions()' which changes a + a paricular instance of the partition table (primary or backup). """ + + if entry['index'] >= header['entries_cnt']: + raise MountError("Partition table at LBA %d has only %d " \ + "records cannot change record number %d" % \ + (header['entries_cnt'], entry['index'])) + # Read raw GPT header + raw_hdr = self._read_disk(header['hdr_offs'], _GPT_HEADER_SIZE) + raw_hdr = list(struct.unpack(_GPT_HEADER_FORMAT, raw_hdr)) + _validate_header(raw_hdr) + + # Prepare the new partition table entry + raw_entry = struct.pack(_GPT_ENTRY_FORMAT, + uuid.UUID(entry['type_uuid']).bytes_le, + uuid.UUID(entry['part_uuid']).bytes_le, + entry['first_lba'], + entry['last_lba'], + entry['flags'], + entry['name'].encode('UTF-16')) + + # Write the updated entry to the disk + entry_offs = header['ptable_offs'] + \ + header['entry_size'] * entry['index'] + self._write_disk(entry_offs, raw_entry) + + # Calculate and update partition table CRC32 + raw_ptable = self._read_disk(header['ptable_offs'], + header['ptable_size']) + raw_hdr[13] = binascii.crc32(raw_ptable) & 0xFFFFFFFF + + # Calculate and update the GPT header CRC + raw_hdr[3] = _calc_header_crc(raw_hdr) + + # Write the updated header to the disk + raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) + self._write_disk(header['hdr_offs'], raw_hdr) + + def change_partition(self, entry): + """ Change a GPT partition. The 'entry' argument has the same format as + 'get_partitions()' returns. This function simply changes the partition + table record corresponding to 'entry' in both, the primary and the + backup GPT partition tables. The parition table CRC is re-calculated + and the GPT headers are modified accordingly. """ + + # Change the primary partition table + header = self.read_header(True) + self._change_partition(header, entry) + + # Change the backup partition table + header = self.read_header(False) + self._change_partition(header, entry) diff --git a/scripts/lib/mic/utils/grabber.py b/scripts/lib/mic/utils/grabber.py new file mode 100644 index 0000000000..45e30b4fb0 --- /dev/null +++ b/scripts/lib/mic/utils/grabber.py @@ -0,0 +1,97 @@ +#!/usr/bin/python + +import os +import sys +import rpm +import fcntl +import struct +import termios + +from mic import msger +from mic.utils import runner +from mic.utils.errors import CreatorError + +from urlgrabber import grabber +from urlgrabber import __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:/"): + filepath = "/%s" % url.replace("file:", "").lstrip('/') + if not os.path.exists(filepath): + raise CreatorError("URLGrabber error: can't find file %s" % url) + if url.endswith('.rpm'): + return filepath + else: + # untouch repometadata in source path + runner.show(['cp', '-f', filepath, filename]) + + else: + try: + filename = g.urlgrab(url=str(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, err: + msg = str(err) + if msg.find(url) < 0: + msg += ' on %s' % url + raise CreatorError(msg) + + 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 + diff --git a/scripts/lib/mic/utils/misc.py b/scripts/lib/mic/utils/misc.py new file mode 100644 index 0000000000..95241d7f15 --- /dev/null +++ b/scripts/lib/mic/utils/misc.py @@ -0,0 +1,1065 @@ +#!/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 sys +import time +import tempfile +import re +import shutil +import glob +import hashlib +import subprocess +import platform +import traceback + + +try: + import sqlite3 as sqlite +except ImportError: + import sqlite + +try: + from xml.etree import cElementTree +except ImportError: + import cElementTree +xmlparse = cElementTree.parse + +from mic import msger +from mic.utils.errors import CreatorError, SquashfsError +from mic.utils.fs_related import find_binary_path, makedirs +from mic.utils.proxy import get_proxy_for +from mic.utils import runner + + +RPM_RE = re.compile("(.*)\.(.*) (.*)-(.*)") +RPM_FMT = "%(name)s.%(arch)s %(version)s-%(release)s" +SRPM_RE = re.compile("(.*)-(\d+.*)-(\d+\.\d+).src.rpm") + + +def build_name(kscfg, release=None, prefix = None, suffix = 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. + + kscfg -- a path to a kickstart file + release -- a replacement to suffix for image release + 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 + + 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 release is not None: + suffix = "" + 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):] + + prefix = "%s-" % prefix if prefix else "" + suffix = "-%s" % suffix if suffix else "" + + ret = prefix + name + suffix + return ret + +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: + distro_str = ' '.join(map(str.strip, (dist, ver, id))) + return distro_str.strip() + +_LOOP_RULE_PTH = None + +def hide_loopdev_presentation(): + udev_rules = "80-prevent-loop-present.rules" + udev_rules_dir = [ + '/usr/lib/udev/rules.d/', + '/lib/udev/rules.d/', + '/etc/udev/rules.d/' + ] + + global _LOOP_RULE_PTH + + for rdir in udev_rules_dir: + if os.path.exists(rdir): + _LOOP_RULE_PTH = os.path.join(rdir, udev_rules) + + if not _LOOP_RULE_PTH: + return + + 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(): + global _LOOP_RULE_PTH + + if not _LOOP_RULE_PTH: + return + + 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 get_block_size(file_obj): + """ Returns block size for file object 'file_obj'. Errors are indicated by + the 'IOError' exception. """ + + from fcntl import ioctl + import struct + + # Get the block size of the host file-system for the image file by calling + # the FIGETBSZ ioctl (number 2). + binary_data = ioctl(file_obj, 2, struct.pack('I', 0)) + return struct.unpack('I', binary_data)[0] + +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 calc_hashes(file_path, hash_names, start = 0, end = None): + """ Calculate hashes for a file. The 'file_path' argument is the file + to calculate hash functions for, 'start' and 'end' are the starting and + ending file offset to calculate the has functions for. The 'hash_names' + argument is a list of hash names to calculate. Returns the the list + of calculated hash values in the hexadecimal form in the same order + as 'hash_names'. + """ + if end == None: + end = os.path.getsize(file_path) + + chunk_size = 65536 + to_read = end - start + read = 0 + + hashes = [] + for hash_name in hash_names: + hashes.append(hashlib.new(hash_name)) + + with open(file_path, "rb") as f: + f.seek(start) + + while read < to_read: + if read + chunk_size > to_read: + chunk_size = to_read - read + chunk = f.read(chunk_size) + for hash_obj in hashes: + hash_obj.update(chunk) + read += chunk_size + + result = [] + for hash_obj in hashes: + result.append(hash_obj.hexdigest()) + + return result + +def get_md5sum(fpath): + return calc_hashes(fpath, ('md5', ))[0] + + +def normalize_ksfile(ksconf, release, arch): + ''' + Return the name of a normalized ks file in which macro variables + @BUILD_ID@ and @ARCH@ are replace with real values. + + The original ks file is returned if no special macro is used, otherwise + a temp file is created and returned, which will be deleted when program + exits normally. + ''' + + 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@" not in ksc and "@BUILD_ID@" not in ksc: + return ksconf + + 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)) + os.write(fd, ksc) + os.close(fd) + + msger.debug('normalized ks file:%s' % ksconf) + + def remove_temp_ks(): + try: + os.unlink(ksconf) + except OSError, err: + msger.warning('Failed to remove temp ks file:%s:%s' % (ksconf, err)) + + import atexit + atexit.register(remove_temp_ks) + + return ksconf + + +def _check_mic_chroot(rootdir): + def _path(path): + return rootdir.rstrip('/') + path + + release_files = map(_path, [ "/etc/moblin-release", + "/etc/meego-release", + "/etc/tizen-release"]) + + if not any(map(os.path.exists, release_files)): + msger.warning("Dir %s is not a MeeGo/Tizen chroot env" % rootdir) + + if not glob.glob(rootdir + "/boot/vmlinuz-*"): + msger.warning("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_mic_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(filename): + """ Return size in MB unit """ + cmd = ['du', "-s", "-b", "-B", "1M", filename] + rc, duOutput = runner.runtool(cmd) + if rc != 0: + raise CreatorError("Failed to run: %s" % ' '.join(cmd)) + size1 = int(duOutput.split()[0]) + + cmd = ['du', "-s", "-B", "1M", filename] + rc, duOutput = runner.runtool(cmd) + if rc != 0: + raise CreatorError("Failed to run: %s" % ' '.join(cmd)) + + size2 = int(duOutput.split()[0]) + return max(size1, 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 = "wic-tmp-"): + """ FIXME: use the dir in wic.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): + try: + sumcmd = find_binary_path("%ssum" % sumtype) + except: + file_checksum = None + else: + file_checksum = runner.outs([sumcmd, filename]).split()[0] + + if file_checksum and 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 + if not arch: + arches = [] + elif arch not in rpmmisc.archPolicies: + arches = [arch] + else: + arches = rpmmisc.archPolicies[arch].split(':') + arches.append('noarch') + + 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 in arches: + 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 arch: + sql = 'select version, release, location_href from packages ' \ + 'where name = "%s" and arch IN ("%s")' % \ + (pkg, '","'.join(arches)) + for row in con.execute(sql): + tmpver = "%s-%s" % (row[0], row[1]) + if tmpver > ver: + ver = tmpver + pkgpath = "%s" % row[2] + target_repo = repo + break + else: + sql = 'select version, release, location_href from packages ' \ + 'where name = "%s"' % pkg + for row in con.execute(sql): + tmpver = "%s-%s" % (row[0], row[1]) + if tmpver > ver: + ver = tmpver + pkgpath = "%s" % row[2] + target_repo = repo + break + con.close() + if target_repo: + makedirs("%s/packages/%s" % (target_repo["cachedir"], target_repo["name"])) + url = os.path.join(target_repo["baseurl"], pkgpath) + filename = str("%s/packages/%s/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath))) + if os.path.exists(filename): + ret = rpmmisc.checkRpmIntegrity('rpm', filename) + if ret == 0: + return filename + + msger.warning("package %s is damaged: %s" % + (os.path.basename(filename), filename)) + os.unlink(filename) + + 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 + "/usr/bin/qemu-arm-static") + qemu_emulator = "/usr/bin/qemu-arm-static" + + # 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"]) + + # unregister it if it has been registered and is a dynamically-linked executable + node = "/proc/sys/fs/binfmt_misc/arm" + if 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/scripts/lib/mic/utils/oe/__init__.py b/scripts/lib/mic/utils/oe/__init__.py new file mode 100644 index 0000000000..d10e802116 --- /dev/null +++ b/scripts/lib/mic/utils/oe/__init__.py @@ -0,0 +1,22 @@ +# +# OpenEmbedded mic utils library +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# AUTHORS +# Tom Zanussi +# diff --git a/scripts/lib/mic/utils/oe/misc.py b/scripts/lib/mic/utils/oe/misc.py new file mode 100644 index 0000000000..7ad3aa9685 --- /dev/null +++ b/scripts/lib/mic/utils/oe/misc.py @@ -0,0 +1,144 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This module provides a place to collect various mic-related utils +# for the OpenEmbedded Image Tools. +# +# AUTHORS +# Tom Zanussi +# + +from mic import msger +from mic.utils import runner + +def exec_cmd(cmd_and_args, as_shell = False, catch = 3): + """ + Execute command, catching stderr, stdout + + Need to execute as_shell if the command uses wildcards + """ + msger.debug("exec_cmd: %s" % cmd_and_args) + args = cmd_and_args.split() + msger.debug(args) + + if (as_shell): + rc, out = runner.runtool(cmd_and_args, catch) + else: + rc, out = runner.runtool(args, catch) + out = out.strip() + msger.debug("exec_cmd: output for %s (rc = %d): %s" % \ + (cmd_and_args, rc, out)) + + if rc != 0: + # 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("WARNING: %s returned '%s' instead of 0" % (cmd_and_args, rc)) + + return (rc, out) + + +def exec_cmd_quiet(cmd_and_args, as_shell = False): + """ + Execute command, catching nothing in the output + + Need to execute as_shell if the command uses wildcards + """ + return exec_cmd(cmd_and_args, as_shell, 0) + + +def exec_native_cmd(cmd_and_args, native_sysroot, catch = 3): + """ + Execute native command, catching stderr, stdout + + Need to execute as_shell if the command uses wildcards + + Always need to execute native commands as_shell + """ + native_paths = \ + "export PATH=%s/sbin:%s/usr/sbin:%s/usr/bin:$PATH" % \ + (native_sysroot, native_sysroot, native_sysroot) + native_cmd_and_args = "%s;%s" % (native_paths, cmd_and_args) + msger.debug("exec_native_cmd: %s" % cmd_and_args) + + args = cmd_and_args.split() + msger.debug(args) + + rc, out = exec_cmd(native_cmd_and_args, True, catch) + + if rc == 127: # shell command-not-found + msger.error("A native (host) program required to build the image " + "was not found (see details above). Please make sure " + "it's installed and try again.") + + return (rc, out) + + +def exec_native_cmd_quiet(cmd_and_args, native_sysroot): + """ + Execute native command, catching nothing in the output + + Need to execute as_shell if the command uses wildcards + + Always need to execute native commands as_shell + """ + return exec_native_cmd(cmd_and_args, native_sysroot, 0) + + +# kickstart doesn't support variable substution in commands, so this +# is our current simplistic scheme for supporting that + +wks_vars = dict() + +def get_wks_var(key): + return wks_vars[key] + +def add_wks_var(key, val): + wks_vars[key] = val + +BOOTDD_EXTRA_SPACE = 16384 +IMAGE_EXTRA_SPACE = 10240 + +__bitbake_env_lines = "" + +def set_bitbake_env_lines(bitbake_env_lines): + global __bitbake_env_lines + __bitbake_env_lines = bitbake_env_lines + +def get_bitbake_env_lines(): + return __bitbake_env_lines + +def get_line_val(line, key): + """ + Extract the value from the VAR="val" string + """ + if line.startswith(key + "="): + stripped_line = line.split('=')[1] + stripped_line = stripped_line.replace('\"', '') + return stripped_line + return None + +def get_bitbake_var(key): + for line in __bitbake_env_lines.split('\n'): + if (get_line_val(line, key)): + val = get_line_val(line, key) + return val + return None diff --git a/scripts/lib/mic/utils/oe/package_manager.py b/scripts/lib/mic/utils/oe/package_manager.py new file mode 100644 index 0000000000..92ce98e2ce --- /dev/null +++ b/scripts/lib/mic/utils/oe/package_manager.py @@ -0,0 +1,810 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2014, Enea AB. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This implements the opkg package manager wrapper as a combination of +# meta/lib/oe/package_manager.py and bitbake/lib/bb/utils.py files and +# adaptation of those files to 'wic'. +# +# AUTHORS +# Adrian Calianu +# +# This file incorporates work covered by the following copyright and +# permission notice: +# +# meta/COPYING.GPLv2 (GPLv2) +# meta/COPYING.MIT (MIT) +# +# Copyright (C) 2004 Michael Lauer +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +from abc import ABCMeta, abstractmethod +import os +import glob +import subprocess +import shutil +import multiprocessing +import re +import errno +import fcntl + +from mic.utils.oe.misc import * +from mic import msger + +def mkdirhier(directory): + """Create a directory like 'mkdir -p', but does not complain if + directory already exists like os.makedirs + """ + + try: + os.makedirs(directory) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + +def remove(path, recurse=False): + """Equivalent to rm -f or rm -rf""" + if not path: + return + if recurse: + # shutil.rmtree(name) would be ideal but its too slow + subprocess.call(['rm', '-rf'] + glob.glob(path)) + return + for name in glob.glob(path): + try: + os.unlink(name) + except OSError as exc: + if exc.errno != errno.ENOENT: + raise + +def lockfile(name, shared=False, retry=True): + """ + Use the file fn as a lock file, return when the lock has been acquired. + Returns a variable to pass to unlockfile(). + """ + dirname = os.path.dirname(name) + mkdirhier(dirname) + + if not os.access(dirname, os.W_OK): + logger.error("Unable to acquire lock '%s', directory is not writable", + name) + sys.exit(1) + + op = fcntl.LOCK_EX + if shared: + op = fcntl.LOCK_SH + if not retry: + op = op | fcntl.LOCK_NB + + while True: + # If we leave the lockfiles lying around there is no problem + # but we should clean up after ourselves. This gives potential + # for races though. To work around this, when we acquire the lock + # we check the file we locked was still the lock file on disk. + # by comparing inode numbers. If they don't match or the lockfile + # no longer exists, we start again. + + # This implementation is unfair since the last person to request the + # lock is the most likely to win it. + + try: + lf = open(name, 'a+') + fileno = lf.fileno() + fcntl.flock(fileno, op) + statinfo = os.fstat(fileno) + if os.path.exists(lf.name): + statinfo2 = os.stat(lf.name) + if statinfo.st_ino == statinfo2.st_ino: + return lf + lf.close() + except Exception: + try: + lf.close() + except Exception: + pass + pass + if not retry: + return None + +def unlockfile(lf): + """ + Unlock a file locked using lockfile() + """ + try: + # If we had a shared lock, we need to promote to exclusive before + # removing the lockfile. Attempt this, ignore failures. + fcntl.flock(lf.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) + os.unlink(lf.name) + except (IOError, OSError): + pass + fcntl.flock(lf.fileno(), fcntl.LOCK_UN) + lf.close() + +def which(path, item, direction = 0, history = False): + """ + Locate a file in a PATH + """ + + hist = [] + paths = (path or "").split(':') + if direction != 0: + paths.reverse() + + for p in paths: + next = os.path.join(p, item) + hist.append(next) + if os.path.exists(next): + if not os.path.isabs(next): + next = os.path.abspath(next) + if history: + return next, hist + return next + + if history: + return "", hist + return "" + + + +# this can be used by all PM backends to create the index files in parallel +def wic_create_index(arg): + index_cmd = arg + + try: + msger.info("Executing '%s' ..." % index_cmd) + subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + return("Index creation command '%s' failed with return code %d:\n%s" % + (e.cmd, e.returncode, e.output)) + + return None + + +class WicIndexer(object): + __metaclass__ = ABCMeta + + def __init__(self, d, deploy_dir): + self.d = d + self.deploy_dir = deploy_dir + + @abstractmethod + def write_index(self): + pass + +class WicOpkgIndexer(WicIndexer): + def write_index(self): + arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS", + "SDK_PACKAGE_ARCHS", + "MULTILIB_ARCHS"] + + opkg_index_cmd = which(os.getenv('PATH'), "opkg-make-index") + + if not os.path.exists(os.path.join(self.deploy_dir, "Packages")): + open(os.path.join(self.deploy_dir, "Packages"), "w").close() + + index_cmds = [] + for arch_var in arch_vars: + if self.d.has_key(arch_var): + archs = self.d[arch_var] + else: + archs = None + + if archs is None: + continue + + for arch in archs.split(): + pkgs_dir = os.path.join(self.deploy_dir, arch) + pkgs_file = os.path.join(pkgs_dir, "Packages") + + if not os.path.isdir(pkgs_dir): + continue + + if not os.path.exists(pkgs_file): + open(pkgs_file, "w").close() + + index_cmds.append('%s -r %s -p %s -m %s' % + (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir)) + + if len(index_cmds) == 0: + msger.info("There are no packages in %s!" % self.deploy_dir) + return + + nproc = multiprocessing.cpu_count() + pool = multiprocessing.Pool(nproc) + results = list(pool.imap(wic_create_index, index_cmds)) + pool.close() + pool.join() + + for result in results: + if result is not None: + return(result) + +class WicPkgsList(object): + __metaclass__ = ABCMeta + + def __init__(self, d, rootfs_dir): + self.d = d + self.rootfs_dir = rootfs_dir + + @abstractmethod + def list(self, format=None): + pass + + +class WicOpkgPkgsList(WicPkgsList): + def __init__(self, d, rootfs_dir, config_file): + super(WicOpkgPkgsList, self).__init__(d, rootfs_dir) + + self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl") + self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir) + if self.d.has_key("OPKG_ARGS"): + self.opkg_args += self.d["OPKG_ARGS"] + + def list(self, format=None): + opkg_query_cmd = which(os.getenv('PATH'), "opkg-query-helper.py") + + if format == "arch": + cmd = "%s %s status | %s -a" % \ + (self.opkg_cmd, self.opkg_args, opkg_query_cmd) + elif format == "file": + cmd = "%s %s status | %s -f" % \ + (self.opkg_cmd, self.opkg_args, opkg_query_cmd) + elif format == "ver": + cmd = "%s %s status | %s -v" % \ + (self.opkg_cmd, self.opkg_args, opkg_query_cmd) + elif format == "deps": + cmd = "%s %s status | %s" % \ + (self.opkg_cmd, self.opkg_args, opkg_query_cmd) + else: + cmd = "%s %s list_installed | cut -d' ' -f1" % \ + (self.opkg_cmd, self.opkg_args) + + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip() + except subprocess.CalledProcessError as e: + msger.error("Cannot get the installed packages list. Command '%s' " + "returned %d:\n%s" % (cmd, e.returncode, e.output)) + + if output and format == "file": + tmp_output = "" + for line in output.split('\n'): + pkg, pkg_file, pkg_arch = line.split() + full_path = os.path.join(self.rootfs_dir, pkg_arch, pkg_file) + if os.path.exists(full_path): + tmp_output += "%s %s %s\n" % (pkg, full_path, pkg_arch) + else: + tmp_output += "%s %s %s\n" % (pkg, pkg_file, pkg_arch) + + output = tmp_output + + return output + + +class WicPackageManager(object): + """ + This is an abstract class. Do not instantiate this directly. + """ + __metaclass__ = ABCMeta + + def __init__(self, d, pseudo, native_sysroot): + self.d = d + self.deploy_dir = None + self.deploy_lock = None + if self.d.has_key('PACKAGE_FEED_URIS'): + self.feed_uris = self.d['PACKAGE_FEED_URIS'] + else: + self.feed_uris = "" + self.pseudo = pseudo + self.native_sysroot = native_sysroot + + """ + Update the package manager package database. + """ + @abstractmethod + def update(self): + pass + + """ + Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is + True, installation failures are ignored. + """ + @abstractmethod + def install(self, pkgs, attempt_only=False): + pass + + """ + Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies' + is False, the any dependencies are left in place. + """ + @abstractmethod + def remove(self, pkgs, with_dependencies=True): + pass + + """ + This function creates the index files + """ + @abstractmethod + def write_index(self): + pass + + @abstractmethod + def remove_packaging_data(self): + pass + + @abstractmethod + def list_installed(self, format=None): + pass + + @abstractmethod + def insert_feeds_uris(self): + pass + + """ + Install complementary packages based upon the list of currently installed + packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install + these packages, if they don't exist then no error will occur. Note: every + backend needs to call this function explicitly after the normal package + installation + """ + def install_complementary(self, globs=None): + # we need to write the list of installed packages to a file because the + # oe-pkgdata-util reads it from a file + if self.d.has_key('WORKDIR'): + installed_pkgs_file = os.path.join(self.d['WORKDIR'], + "installed_pkgs.txt") + else: + msger.error("No WORKDIR provided!") + + with open(installed_pkgs_file, "w+") as installed_pkgs: + installed_pkgs.write(self.list_installed("arch")) + + if globs is None: + if self.d.has_key('IMAGE_INSTALL_COMPLEMENTARY'): + globs = self.d['IMAGE_INSTALL_COMPLEMENTARY'] + split_linguas = set() + + if self.d.has_key('IMAGE_LINGUAS'): + for translation in self.d['IMAGE_LINGUAS'].split(): + split_linguas.add(translation) + split_linguas.add(translation.split('-')[0]) + + split_linguas = sorted(split_linguas) + + for lang in split_linguas: + globs += " *-locale-%s" % lang + + if globs is None: + return + + if not self.d.has_key('PKGDATA_DIR'): + msger.error("No PKGDATA_DIR provided!") + + cmd = [which(os.getenv('PATH'), "oe-pkgdata-util"), + "glob", self.d['PKGDATA_DIR'], installed_pkgs_file, + globs] + + rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) + if rc != 0: + msger.error("Could not compute complementary packages list. Command " + "'%s' returned %d" % + (' '.join(cmd), rc)) + + self.install(out.split(), attempt_only=True) + + + def deploy_dir_lock(self): + if self.deploy_dir is None: + raise RuntimeError("deploy_dir is not set!") + + lock_file_name = os.path.join(self.deploy_dir, "deploy.lock") + + self.deploy_lock = lockfile(lock_file_name) + + def deploy_dir_unlock(self): + if self.deploy_lock is None: + return + + unlockfile(self.deploy_lock) + + self.deploy_lock = None + + +class WicOpkgPM(WicPackageManager): + def __init__(self, d, target_rootfs, config_file, archs, pseudo, native_sysroot, task_name='target'): + super(WicOpkgPM, self).__init__(d, pseudo, native_sysroot) + + self.target_rootfs = target_rootfs + self.config_file = config_file + self.pkg_archs = archs + self.task_name = task_name + + if self.d.has_key("DEPLOY_DIR_IPK"): + self.deploy_dir = self.d["DEPLOY_DIR_IPK"] + + self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock") + self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl") + self.opkg_args = "-f %s -o %s " % (self.config_file, target_rootfs) + if self.d.has_key("OPKG_ARGS"): + self.opkg_args += self.d["OPKG_ARGS"] + + if self.d.has_key('OPKGLIBDIR'): + opkg_lib_dir = self.d['OPKGLIBDIR'] + else: + opkg_lib_dir = "" + + if opkg_lib_dir[0] == "/": + opkg_lib_dir = opkg_lib_dir[1:] + + self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg") + + mkdirhier(self.opkg_dir) + + if self.d.has_key("TMPDIR"): + tmp_dir = self.d["TMPDIR"] + else: + tmp_dir = "" + + self.saved_opkg_dir = '%s/saved/%s' % (tmp_dir, self.task_name) + if not os.path.exists('%s/saved' % tmp_dir): + mkdirhier('%s/saved' % tmp_dir) + + if self.d.has_key('BUILD_IMAGES_FROM_FEEDS') and self.d['BUILD_IMAGES_FROM_FEEDS'] != "1": + self._create_config() + else: + self._create_custom_config() + + self.indexer = WicOpkgIndexer(self.d, self.deploy_dir) + + """ + This function will change a package's status in /var/lib/opkg/status file. + If 'packages' is None then the new_status will be applied to all + packages + """ + def mark_packages(self, status_tag, packages=None): + status_file = os.path.join(self.opkg_dir, "status") + + with open(status_file, "r") as sf: + with open(status_file + ".tmp", "w+") as tmp_sf: + if packages is None: + tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)", + r"Package: \1\n\2Status: \3%s" % status_tag, + sf.read())) + else: + if type(packages).__name__ != "list": + raise TypeError("'packages' should be a list object") + + status = sf.read() + for pkg in packages: + status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg, + r"Package: %s\n\1Status: \2%s" % (pkg, status_tag), + status) + + tmp_sf.write(status) + + os.rename(status_file + ".tmp", status_file) + + def _create_custom_config(self): + msger.info("Building from feeds activated!") + + with open(self.config_file, "w+") as config_file: + priority = 1 + for arch in self.pkg_archs.split(): + config_file.write("arch %s %d\n" % (arch, priority)) + priority += 5 + + if self.d.has_key('IPK_FEED_URIS'): + ipk_feed_uris = self.d['IPK_FEED_URIS'] + else: + ipk_feed_uris = "" + + for line in ipk_feed_uris.split(): + feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line) + + if feed_match is not None: + feed_name = feed_match.group(1) + feed_uri = feed_match.group(2) + + msger.info("Add %s feed with URL %s" % (feed_name, feed_uri)) + + config_file.write("src/gz %s %s\n" % (feed_name, feed_uri)) + + """ + Allow to use package deploy directory contents as quick devel-testing + feed. This creates individual feed configs for each arch subdir of those + specified as compatible for the current machine. + NOTE: Development-helper feature, NOT a full-fledged feed. + """ + if self.d.has_key('FEED_DEPLOYDIR_BASE_URI'): + feed_deploydir_base_dir = self.d['FEED_DEPLOYDIR_BASE_URI'] + else: + feed_deploydir_base_dir = "" + + if feed_deploydir_base_dir != "": + for arch in self.pkg_archs.split(): + if self.d.has_key("sysconfdir"): + sysconfdir = self.d["sysconfdir"] + else: + sysconfdir = None + + cfg_file_name = os.path.join(self.target_rootfs, + sysconfdir, + "opkg", + "local-%s-feed.conf" % arch) + + with open(cfg_file_name, "w+") as cfg_file: + cfg_file.write("src/gz local-%s %s/%s" % + arch, + feed_deploydir_base_dir, + arch) + + def _create_config(self): + with open(self.config_file, "w+") as config_file: + priority = 1 + for arch in self.pkg_archs.split(): + config_file.write("arch %s %d\n" % (arch, priority)) + priority += 5 + + config_file.write("src oe file:%s\n" % self.deploy_dir) + + for arch in self.pkg_archs.split(): + pkgs_dir = os.path.join(self.deploy_dir, arch) + if os.path.isdir(pkgs_dir): + config_file.write("src oe-%s file:%s\n" % + (arch, pkgs_dir)) + + def insert_feeds_uris(self): + if self.feed_uris == "": + return + + rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf' + % self.target_rootfs) + + with open(rootfs_config, "w+") as config_file: + uri_iterator = 0 + for uri in self.feed_uris.split(): + config_file.write("src/gz url-%d %s/ipk\n" % + (uri_iterator, uri)) + + for arch in self.pkg_archs.split(): + if not os.path.exists(os.path.join(self.deploy_dir, arch)): + continue + msger.info('Note: adding opkg channel url-%s-%d (%s)' % + (arch, uri_iterator, uri)) + + config_file.write("src/gz uri-%s-%d %s/ipk/%s\n" % + (arch, uri_iterator, uri, arch)) + uri_iterator += 1 + + def update(self): + self.deploy_dir_lock() + + cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args) + + rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) + if rc != 0: + self.deploy_dir_unlock() + msger.error("Unable to update the package index files. Command '%s' " + "returned %d" % (cmd, rc)) + + self.deploy_dir_unlock() + + def install(self, pkgs, attempt_only=False): + if attempt_only and len(pkgs) == 0: + return + + cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) + + os.environ['D'] = self.target_rootfs + os.environ['OFFLINE_ROOT'] = self.target_rootfs + os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs + os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs + if self.d.has_key('WORKDIR'): + os.environ['INTERCEPT_DIR'] = os.path.join(self.d['WORKDIR'], + "intercept_scripts") + else: + os.environ['INTERCEPT_DIR'] = "." + msger.warning("No WORKDIR provided!") + + if self.d.has_key('STAGING_DIR_NATIVE'): + os.environ['NATIVE_ROOT'] = self.d['STAGING_DIR_NATIVE'] + else: + msger.error("No STAGING_DIR_NATIVE provided!") + + rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) + if rc != 0: + msger.error("Unable to install packages. " + "Command '%s' returned %d" % (cmd, rc)) + + + def remove(self, pkgs, with_dependencies=True): + if with_dependencies: + cmd = "%s %s --force-depends --force-remove --force-removal-of-dependent-packages remove %s" % \ + (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) + else: + cmd = "%s %s --force-depends remove %s" % \ + (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) + + rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) + if rc != 0: + msger.error("Unable to remove packages. Command '%s' " + "returned %d" % (cmd, rc)) + + + def write_index(self): + self.deploy_dir_lock() + + result = self.indexer.write_index() + + self.deploy_dir_unlock() + + if result is not None: + msger.error(result) + + def remove_packaging_data(self): + remove(self.opkg_dir, True) + # create the directory back, it's needed by PM lock + mkdirhier(self.opkg_dir) + + def list_installed(self, format=None): + return WicOpkgPkgsList(self.d, self.target_rootfs, self.config_file).list(format) + + def handle_bad_recommendations(self): + if self.d.has_key("BAD_RECOMMENDATIONS"): + bad_recommendations = self.d["BAD_RECOMMENDATIONS"] + else: + bad_recommendations = "" + + if bad_recommendations.strip() == "": + return + + status_file = os.path.join(self.opkg_dir, "status") + + # If status file existed, it means the bad recommendations has already + # been handled + if os.path.exists(status_file): + return + + cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args) + + with open(status_file, "w+") as status: + for pkg in bad_recommendations.split(): + pkg_info = cmd + pkg + + try: + output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip() + except subprocess.CalledProcessError as e: + msger.error("Cannot get package info. Command '%s' " + "returned %d:\n%s" % (pkg_info, e.returncode, e.output)) + + if output == "": + msger.info("Ignored bad recommendation: '%s' is " + "not a package" % pkg) + continue + + for line in output.split('\n'): + if line.startswith("Status:"): + status.write("Status: deinstall hold not-installed\n") + else: + status.write(line + "\n") + + ''' + The following function dummy installs pkgs and returns the log of output. + ''' + def dummy_install(self, pkgs): + if len(pkgs) == 0: + return + + # Create an temp dir as opkg root for dummy installation + if self.d.has_key("TMPDIR"): + tmp_dir = self.d["TMPDIR"] + else: + tmp_dir = "." + msger.warning("No TMPDIR provided!") + + temp_rootfs = '%s/opkg' % tmp_dir + temp_opkg_dir = os.path.join(temp_rootfs, 'var/lib/opkg') + mkdirhier(temp_opkg_dir) + + opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs) + if self.d.has_key("OPKG_ARGS"): + opkg_args += self.d["OPKG_ARGS"] + + cmd = "%s %s update" % (self.opkg_cmd, opkg_args) + try: + subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + msger.error("Unable to update. Command '%s' " + "returned %d:\n%s" % (cmd, e.returncode, e.output)) + + # Dummy installation + cmd = "%s %s --noaction install %s " % (self.opkg_cmd, + opkg_args, + ' '.join(pkgs)) + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + msger.error("Unable to dummy install packages. Command '%s' " + "returned %d:\n%s" % (cmd, e.returncode, e.output)) + + remove(temp_rootfs, True) + + return output + + def backup_packaging_data(self): + # Save the opkglib for increment ipk image generation + if os.path.exists(self.saved_opkg_dir): + remove(self.saved_opkg_dir, True) + shutil.copytree(self.opkg_dir, + self.saved_opkg_dir, + symlinks=True) + + def recover_packaging_data(self): + # Move the opkglib back + if os.path.exists(self.saved_opkg_dir): + if os.path.exists(self.opkg_dir): + remove(self.opkg_dir, True) + + msger.info('Recover packaging data') + shutil.copytree(self.saved_opkg_dir, + self.opkg_dir, + symlinks=True) + + +def wic_generate_index_files(d): + if d.has_key('PACKAGE_CLASSES'): + classes = d['PACKAGE_CLASSES'].replace("package_", "").split() + else: + classes = "" + msger.warning("No PACKAGE_CLASSES provided!") + + if d.has_key('DEPLOY_DIR_IPK'): + deploy_dir_ipk = d['DEPLOY_DIR_IPK'] + else: + deploy_dir_ipk = None + msger.warning("No DEPLOY_DIR_IPK provided!") + + indexer_map = { + "ipk": (WicOpkgIndexer, deploy_dir_ipk) + } + + result = None + + for pkg_class in classes: + if not pkg_class in indexer_map: + continue + + if os.path.exists(indexer_map[pkg_class][1]): + result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index() + + if result is not None: + msger.error(result) diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py new file mode 100644 index 0000000000..6607466a83 --- /dev/null +++ b/scripts/lib/mic/utils/partitionedfs.py @@ -0,0 +1,782 @@ +#!/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 * +from mic.utils.gpt_parser import GptParser +from mic.utils.oe.misc import * + +# Overhead of the MBR partitioning scheme (just one sector) +MBR_OVERHEAD = 1 +# Overhead of the GPT partitioning scheme +GPT_OVERHEAD = 34 + +# Size of a sector in bytes +SECTOR_SIZE = 512 + +class PartitionedMount(Mount): + def __init__(self, mountdir, skipformat = False): + Mount.__init__(self, mountdir) + self.disks = {} + self.partitions = [] + self.subvolumes = [] + self.mapped = False + self.mountOrder = [] + self.unmountOrder = [] + self.parted = find_binary_path("parted") + self.btrfscmd=None + self.skipformat = skipformat + self.snapshot_created = self.skipformat + # Size of a sector used in calculations + self.sector_size = SECTOR_SIZE + self._partitions_layed_out = False + + def __add_disk(self, disk_name): + """ Add a disk 'disk_name' to the internal list of disks. Note, + 'disk_name' is the name of the disk in the target system + (e.g., sdb). """ + + if disk_name in self.disks: + # We already have this disk + return + + assert not self._partitions_layed_out + + self.disks[disk_name] = \ + { 'disk': None, # Disk object + 'mapped': False, # True if kpartx mapping exists + 'numpart': 0, # Number of allocate partitions + 'partitions': [], # Indexes to self.partitions + 'offset': 0, # Offset of next partition (in sectors) + # Minimum required disk size to fit all partitions (in bytes) + 'min_size': 0, + 'ptable_format': "msdos" } # Partition table format + + def add_disk(self, disk_name, disk_obj): + """ Add a disk object which have to be partitioned. More than one disk + can be added. In case of multiple disks, disk partitions have to be + added for each disk separately with 'add_partition()". """ + + self.__add_disk(disk_name) + self.disks[disk_name]['disk'] = disk_obj + + def __add_partition(self, part): + """ This is a helper function for 'add_partition()' which adds a + partition to the internal list of partitions. """ + + assert not self._partitions_layed_out + + self.partitions.append(part) + self.__add_disk(part['disk_name']) + + def add_partition(self, size, disk_name, mountpoint, source_file = None, fstype = None, + label=None, fsopts = None, boot = False, align = None, + part_type = None): + """ Add the next partition. Prtitions have to be added in the + first-to-last order. """ + + ks_pnum = len(self.partitions) + + # 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_name': disk_name, # 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) + + part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file + 'size': size, # In sectors + 'mountpoint': mountpoint, # Mount relative to chroot + 'source_file': source_file, # partition contents + 'fstype': fstype, # Filesystem type + 'fsopts': fsopts, # Filesystem mount options + 'label': label, # Partition label + 'disk_name': disk_name, # 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 + 'part_type' : part_type, # Partition type + 'partuuid': None } # Partition UUID (GPT-only) + + self.__add_partition(part) + + def layout_partitions(self, ptable_format = "msdos"): + """ Layout the partitions, meaning calculate the position of every + partition on the disk. The 'ptable_format' parameter defines the + partition table format, and may be either "msdos" or "gpt". """ + + msger.debug("Assigning %s partitions to disks" % ptable_format) + + if ptable_format not in ('msdos', 'gpt'): + raise MountError("Unknown partition table format '%s', supported " \ + "formats are: 'msdos' and 'gpt'" % ptable_format) + + if self._partitions_layed_out: + return + + self._partitions_layed_out = True + + # 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_name']): + raise MountError("No disk %s for partition %s" \ + % (p['disk_name'], p['mountpoint'])) + + if p['part_type'] and ptable_format != 'gpt': + # The --part-type can also be implemented for MBR partitions, + # in which case it would map to the 1-byte "partition type" + # filed at offset 3 of the partition entry. + raise MountError("setting custom partition type is only " \ + "imlemented for GPT partitions") + + # Get the disk where the partition is located + d = self.disks[p['disk_name']] + d['numpart'] += 1 + d['ptable_format'] = ptable_format + + if d['numpart'] == 1: + if ptable_format == "msdos": + overhead = MBR_OVERHEAD + else: + overhead = GPT_OVERHEAD + + # Skip one sector required for the partitioning scheme overhead + d['offset'] += overhead + # Steal few sectors from the first partition to offset for the + # partitioning overhead + p['size'] -= overhead + + if 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 + + msger.debug("Realignment for %s%s with %s sectors, original" + " offset %s, target alignment is %sK." % + (p['disk_name'], d['numpart'], align_sectors, + d['offset'], p['align'])) + + # increase the offset so we actually start the partition on right alignment + d['offset'] += align_sectors + + p['start'] = d['offset'] + d['offset'] += p['size'] + + p['type'] = 'primary' + p['num'] = d['numpart'] + + if d['ptable_format'] == "msdos": + if d['numpart'] > 2: + # Every logical partition requires an additional sector for + # the EBR, so steal the last sector from the end of each + # partition starting from the 3rd one for the EBR. This + # will make sure the logical partitions are aligned + # correctly. + p['size'] -= 1 + + if d['numpart'] > 3: + p['type'] = 'logical' + p['num'] = d['numpart'] + 1 + + d['partitions'].append(n) + msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " + "sectors (%d bytes)." \ + % (p['mountpoint'], p['disk_name'], p['num'], + p['start'], p['start'] + p['size'] - 1, + p['size'], p['size'] * self.sector_size)) + + # Once all the partitions have been layed out, we can calculate the + # minumim disk sizes. + for disk_name, d in self.disks.items(): + d['min_size'] = d['offset'] + if d['ptable_format'] == 'gpt': + # Account for the backup partition table at the end of the disk + d['min_size'] += GPT_OVERHEAD + + d['min_size'] *= self.sector_size + + def __run_parted(self, args): + """ Run parted with arguments specified in the 'args' list. """ + + args.insert(0, self.parted) + msger.debug(args) + + rc, out = runner.runtool(args, catch = 3) + out = out.strip() + if out: + msger.debug('"parted" output: %s' % out) + + if rc != 0: + # 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" % rc) + + def __create_partition(self, device, parttype, fstype, start, size): + """ Create a partition on an image described by the 'device' object. """ + + # Start is included to the size so we need to substract one from the end. + end = start + size - 1 + msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % + (parttype, start, end, size)) + + args = ["-s", device, "unit", "s", "mkpart", parttype] + if fstype: + args.extend([fstype]) + args.extend(["%d" % start, "%d" % end]) + + return self.__run_parted(args) + + def __format_disks(self): + self.layout_partitions() + + 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)) + self.__run_parted(["-s", d['disk'].device, "mklabel", + d['ptable_format']]) + + msger.debug("Creating partitions") + + for p in self.partitions: + d = self.disks[p['disk_name']] + if d['ptable_format'] == "msdos" and p['num'] == 5: + # The last sector of the 3rd partition was reserved for the EBR + # of the first _logical_ partition. This is why the extended + # partition should start one sector before the first logical + # partition. + self.__create_partition(d['disk'].device, "extended", + None, p['start'] - 1, + d['offset'] - p['start']) + + 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 + + self.__create_partition(d['disk'].device, p['type'], + parted_fs_type, p['start'], p['size']) + + if p['boot']: + if d['ptable_format'] == 'gpt': + flag_name = "legacy_boot" + else: + flag_name = "boot" + msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ + (flag_name, p['num'], d['disk'].device)) + self.__run_parted(["-s", d['disk'].device, "set", + "%d" % p['num'], flag_name, "on"]) + + # Parted defaults to enabling the lba flag for fat16 partitions, + # which causes compatibility issues with some firmware (and really + # isn't necessary). + if parted_fs_type == "fat16": + if d['ptable_format'] == 'msdos': + msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ + (p['num'], d['disk'].device)) + self.__run_parted(["-s", d['disk'].device, "set", + "%d" % p['num'], "lba", "off"]) + + # If the partition table format is "gpt", find out PARTUUIDs for all + # the partitions. And if users specified custom parition type UUIDs, + # set them. + for disk_name, disk in self.disks.items(): + if disk['ptable_format'] != 'gpt': + continue + + pnum = 0 + gpt_parser = GptParser(d['disk'].device, SECTOR_SIZE) + # Iterate over all GPT partitions on this disk + for entry in gpt_parser.get_partitions(): + pnum += 1 + # Find the matching partition in the 'self.partitions' list + for n in d['partitions']: + p = self.partitions[n] + if p['num'] == pnum: + # Found, fetch PARTUUID (partition's unique ID) + p['partuuid'] = entry['part_uuid'] + msger.debug("PARTUUID for partition %d on disk '%s' " \ + "(mount point '%s') is '%s'" % (pnum, \ + disk_name, p['mountpoint'], p['partuuid'])) + if p['part_type']: + entry['type_uuid'] = p['part_type'] + msger.debug("Change type of partition %d on disk " \ + "'%s' (mount point '%s') to '%s'" % \ + (pnum, disk_name, p['mountpoint'], + p['part_type'])) + gpt_parser.change_partition(entry) + + del gpt_parser + + 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:] + + # Make sure kpartx reported the right count of partitions + if len(kpartxOutput) != d['numpart']: + # If this disk has more than 3 partitions, then in case of MBR + # paritions there is an extended parition. Different versions + # of kpartx behave differently WRT the extended partition - + # some map it, some ignore it. This is why we do the below hack + # - if kpartx reported one more partition and the partition + # table type is "msdos" and the amount of partitions is more + # than 3, we just assume kpartx mapped the extended parition + # and we remove it. + if len(kpartxOutput) == d['numpart'] + 1 \ + and d['ptable_format'] == 'msdos' and len(kpartxOutput) > 3: + kpartxOutput.pop(3) + else: + 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) + + # FIXME: there is a bit delay for multipath device setup, + # wait 10ms for the setup + import time + time.sleep(10) + 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: + if p['mountpoint']: + 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) + if self.disks: + 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_name': p['disk_name'], # 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_name'] == subvolume['disk_name']: + 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 __install_partition(self, num, source_file, start, size): + """ + Install source_file contents into a partition. + """ + if not source_file: # nothing to install + return + + # Start is included in the size so need to substract one from the end. + end = start + size - 1 + msger.debug("Installed %s in partition %d, sectors %d-%d, size %d sectors" % (source_file, num, start, end, size)) + + dd_cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \ + (source_file, self.image_file, self.sector_size, start, size) + rc, out = exec_cmd(dd_cmd) + + + def install(self, image_file): + msger.debug("Installing partitions") + + self.image_file = image_file + + for p in self.partitions: + d = self.disks[p['disk_name']] + if d['ptable_format'] == "msdos" and p['num'] == 5: + # The last sector of the 3rd partition was reserved for the EBR + # of the first _logical_ partition. This is why the extended + # partition should start one sector before the first logical + # partition. + self.__install_partition(p['num'], p['source_file'], + p['start'] - 1, + d['offset'] - p['start']) + + self.__install_partition(p['num'], p['source_file'], + p['start'], p['size']) + + def mount(self): + for dev in self.disks.keys(): + d = self.disks[dev] + d['disk'].create() + + self.__format_disks() + + self.__calculate_mountorder() + + return + + def resparse(self, size = None): + # Can't re-sparse a disk image - too hard + pass diff --git a/scripts/lib/mic/utils/proxy.py b/scripts/lib/mic/utils/proxy.py new file mode 100644 index 0000000000..91451a2d01 --- /dev/null +++ b/scripts/lib/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/scripts/lib/mic/utils/rpmmisc.py b/scripts/lib/mic/utils/rpmmisc.py new file mode 100644 index 0000000000..af15763e18 --- /dev/null +++ b/scripts/lib/mic/utils/rpmmisc.py @@ -0,0 +1,600 @@ +#!/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 +import sys +import re +import rpm + +from mic import msger +from mic.utils.errors import CreatorError +from mic.utils.proxy import get_proxy_for +from mic.utils import runner + + +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 + if hdr['name'] not in self.installed_pkg_names: + self.installed_pkg_names.append(hdr['name']) + self.total_installed += 1 + 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 mic.utils.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, XXXXXXXX" %(user)) + 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/scripts/lib/mic/utils/runner.py b/scripts/lib/mic/utils/runner.py new file mode 100644 index 0000000000..fded3c93fa --- /dev/null +++ b/scripts/lib/mic/utils/runner.py @@ -0,0 +1,109 @@ +#!/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 subprocess + +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 = subprocess.PIPE + serr = dev_null + elif catch == 2: + sout = dev_null + serr = subprocess.PIPE + elif catch == 3: + sout = subprocess.PIPE + serr = subprocess.STDOUT + + try: + p = subprocess.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] -- cgit v1.2.3-54-g00ecf