diff --git a/.gitignore b/.gitignore index 108062fa..7ccdfdf1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dependmodules.x *.cpio.gz *.sqsh *.pyc +*.pyo # Package cache and lock files .lock @@ -18,3 +19,7 @@ RELEASE/ .bash_history .buildroot-ccache + +# temporary files +*~ +.#* diff --git a/builds/amd64/installer/new-hotness/Makefile b/builds/amd64/installer/new-hotness/Makefile new file mode 100644 index 00000000..003238cf --- /dev/null +++ b/builds/amd64/installer/new-hotness/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/builds/amd64/installer/new-hotness/PKG.yml b/builds/amd64/installer/new-hotness/PKG.yml new file mode 100644 index 00000000..55693e33 --- /dev/null +++ b/builds/amd64/installer/new-hotness/PKG.yml @@ -0,0 +1,2 @@ +!include $ONL/builds/any/installer/new-hotness/APKG.yml ARCH=amd64 + diff --git a/builds/amd64/installer/new-hotness/builds/.gitignore b/builds/amd64/installer/new-hotness/builds/.gitignore new file mode 100644 index 00000000..fbd18542 --- /dev/null +++ b/builds/amd64/installer/new-hotness/builds/.gitignore @@ -0,0 +1 @@ +*INSTALLER diff --git a/builds/amd64/installer/new-hotness/builds/Makefile b/builds/amd64/installer/new-hotness/builds/Makefile new file mode 100644 index 00000000..065c502a --- /dev/null +++ b/builds/amd64/installer/new-hotness/builds/Makefile @@ -0,0 +1,2 @@ +include $(ONL)/make/config.amd64.mk +include $(ONL)/builds/any/installer/new-hotness/grub/builds/Makefile diff --git a/builds/amd64/installer/new-hotness/builds/boot-config b/builds/amd64/installer/new-hotness/builds/boot-config new file mode 100644 index 00000000..40fb0d31 --- /dev/null +++ b/builds/amd64/installer/new-hotness/builds/boot-config @@ -0,0 +1,4 @@ +NETDEV=ma1 +NETAUTO=dhcp +BOOTMODE=SWI +SWI=images::latest diff --git a/builds/any/installer/new-hotness/APKG.yml b/builds/any/installer/new-hotness/APKG.yml new file mode 100644 index 00000000..62ad5191 --- /dev/null +++ b/builds/any/installer/new-hotness/APKG.yml @@ -0,0 +1,35 @@ + +prerequisites: + broken: true + packages: [ "onl-swi:$ARCH" ] + +common: + arch: $ARCH + version: $FNAME_RELEASE_ID + copyright: Copyright 2016 Big Switch Networks + maintainer: support@bigswitch.com + +packages: + - name: onl-installer + summary: Open Network Linux $ARCH Installer + + files: + builds/*INSTALLER : $$PKG_INSTALL/ + builds/*.md5sum : $$PKG_INSTALL/ + + changelog: Change changes changes., + + +release: + - builds/*INSTALLER : $ARCH/ + - builds/*.md5sum : $ARCH/ + + + + + + + + + + diff --git a/builds/any/installer/new-hotness/grub/builds/Makefile b/builds/any/installer/new-hotness/grub/builds/Makefile new file mode 100644 index 00000000..fabadf3b --- /dev/null +++ b/builds/any/installer/new-hotness/grub/builds/Makefile @@ -0,0 +1,94 @@ +ifndef ARCH +$(error $$ARCH not set) +endif + +ONLPLATFORM = python $(ONL)/tools/onlplatform.py +PLATFORMS := $(shell $(ONLPM) --platform-manifest onl-loader-initrd:$(ARCH)) + +MKSHAR = $(ONL)/tools/mkshar +MKSHAR_OPTS = --lazy --unzip-pad --fixup-perms autoperms.sh +MKSHAR_PERMS = autoperms.sh + +# Hardcoded to match ONL File naming conventions. +include $(ONL)/make/version-onl.mk +INSTALLER_NAME=$(FNAME_PRODUCT_VERSION)_ONL-OS_$(FNAME_BUILD_ID)_$(UARCH)_INSTALLER + +ifeq ($(ARCH), amd64) +INSTALLER_ARCH = x86_64 +else +INSTALLER_ARCH = $(ARCH) +endif + +__installer: __installer_platform_files __installer_swi_files + $(ONL_V_at)rm -rf *INSTALLER* *.md5sum + $(ONL_V_at)cp /dev/null installer.sh + $(ONL_V_at): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + set dummy *.cpio.gz; initrd="$$2" ;\ + sed \ + -e 's^@ONLVERSION@^$(VERSION_STRING)^g' \ + -e "s^@INITRD_ARCHIVE@^$$initrd^g" \ + -e 's^@INITRD_OFFSET@^^g' \ + -e 's^@INITRD_SIZE@^^g' \ + -e 's^@ARCH@^$(INSTALLER_ARCH)^g' \ + $(ONL)/builds/any/installer/new-hotness/installer.sh.in \ + >> installer.sh + $(ONL_V_at)echo "PAYLOAD_FOLLOWS" >> installer.sh + $(ONL_V_at)cp /dev/null $(MKSHAR_PERMS) + $(ONL_V_at)cp $(ONL)/make/version-onl.sh . + $(ONL_V_at)echo "#!/bin/sh" >> $(MKSHAR_PERMS) + $(ONL_V_at)echo "set -e" >> $(MKSHAR_PERMS) + $(ONL_V_at)echo "set -x" >> $(MKSHAR_PERMS) + $(MKSHAR) $(MKSHAR_OPTS) "$(INSTALLER_NAME)" $(ONL)/tools/scripts/sfx.sh.in installer.sh kernel-* onl-loader-initrd-* *.swi version-onl.sh boot-config + $(ONL_V_at)rm -rf installer.sh kernel-* onl-loader-initrd-* $(ZTN_MANIFEST) *.swi version-onl.sh autoperms.sh + md5sum "$(INSTALLER_NAME)" | awk '{ print $$1 }' > "$(INSTALLER_NAME).md5sum" + +__installer_platform_files: + $(ONL_V_GEN): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + l="$(PLATFORMS)"; for p in $$l; do \ + src=$$($(ONLPLATFORM) $$p $(ARCH) kernel 2>/dev/null) || : ;\ + if test "$$src"; then \ + dst=$${src##*/} ;\ + if test "$dst" -ot Makefile; then \ + : ;\ + else \ + echo "Staging $$dst for $$p" ;\ + cp "$$src" "$$dst" ;\ + fi ;\ + fi ;\ + src=$$($(ONLPLATFORM) $$p $(ARCH) initrd 2>/dev/null) || : ;\ + if test "$$src"; then \ + dst=$${src##*/} ;\ + if test "$dst" -ot Makefile; then \ + : ;\ + else \ + echo "Staging $$dst for $$p" ;\ + cp "$$src" "$$dst" ;\ + fi ;\ + fi ;\ + done ;\ + : + +ifndef NO_SWI +__installer_swi_files: + $(ONL_V_GEN): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + swidir=$$(mktemp -d $(PWD)/swi-d-XXXXXX) ;\ + $(ONLPM) --extract-dir onl-swi:$(ARCH) $$swidir ;\ + mv $$swidir/usr/share/onl/packages/$(ARCH)/onl-swi/*.swi . ;\ + rm -fr $$swidir ;\ + : +else +__installer_swi_files: + $(ONL_V_GEN): +endif + +shar installer: installer + +clean: + rm -f *.swi *.installer $(notdir $(KERNELS)) *initrd*.cpio.gz + diff --git a/builds/any/installer/new-hotness/installer.sh.in b/builds/any/installer/new-hotness/installer.sh.in new file mode 100644 index 00000000..7982c5a1 --- /dev/null +++ b/builds/any/installer/new-hotness/installer.sh.in @@ -0,0 +1,513 @@ +#!/bin/sh +############################################################ +# +# +# Copyright 2013, 2014 BigSwitch Networks, Inc. +# +# +# +# +############################################################ +# +# SwitchLight Installation Script for PPC. +# +# The purpose of this script is to automatically install SwitchLight +# on the target system. +# +# This script is ONIE-compatible. +# +# This script is can be run under a manual boot of the SwitchLight +# Loader as the execution environment for platforms that do not +# support ONIE. +# +############################################################ + +IARCH="@ARCH@" +ARCH=`uname -m` +if test "$ARCH" != "$IARCH"; then + # identify mappings between kernel arch and debian arch + case "$IARCH:$ARCH" in + armel:armv7l) ;; + powerpc:ppc) ;; + *) + echo + echo "------------------------------------" + echo "Installer Architecture: $IARCH" + echo "Target Architecture: $ARCH" + echo + echo "This installer cannot be used on this" + echo "target." + echo + echo "------------------------------------" + sleep 5 + exit 1 + ;; + esac +fi +case "$ARCH" in + ppc|powerpc) + ARCH_PPC=$ARCH + ;; + x86*|amd*|i?86*) + ARCH_X86=$ARCH + ;; + arm*) + ARCH_ARM=$ARCH + ;; + *) + echo "Invalid Architecture: $ARCH" + sleep 5 + exit 1 + ;; +esac + +############################################################ +# +# Installation Main +# +# Installation is performed as follows: +# +# 1. Detect whether we are running under ONIE or SwitchLight +# and perform the appropriate setup. +# +# 2. Unpack the installer files. +# +# 3. Source the installer scriptlet for the current platform. +# 4. Run the installer function from the platform scriptlet. +# +# The platform scriptlet determines the entire installation +# sequence. +# +# Most platforms will just call the installation +# utilities in this script with the approprate platform settings. +# +############################################################ + +set -e + +installer_script=${0##*/} +installer_dir=${0%/*} +installer_dir=$(cd $installer_dir && pwd) +installer_zip=$1 + +installer_tmpfs= +installer_tmpfs_opts= +# installer_tmpfs=??*, installer_tmpfs_opts= --> temporary mount +# installer_tmpfs=??*, installer_tmpfs_opts=??* --> temporary remount + +installer_tmpfs_kmin=1048576 +# minimum tmpfs/ramfs size to run this installer +# (conservative, could be based on actual installer size) + +BOOTDIR=/mnt/onie-boot +# initial boot partition (onie) + +# Replaced during build packaging with the current version. +onl_version="@ONLVERSION@" +initrd_archive="@INITRD_ARCHIVE@" +initrd_offset="@INITRD_OFFSET@" +initrd_size="@INITRD_SIZE@" + +CR=" +" + +cd $installer_dir + +has_grub_env() +{ + local tag + tag=$1; shift + test -f $BOOTDIR/grub/grubenv || return 1 + case "`grub-editenv $BOOTDIR/grubenv list` 2>/dev/null" in + *${tag}*) return 0 ;; + esac + return 1 +} + +has_uboot_env() +{ + local tag + tag=$1; shift + test -x /usr/sbin/fw_printenv || return 1 + test -f /etc/fw_env.config || return 1 + /usr/sbin/fw_printenv $tag 1>/dev/null 2>&1 && return 0 + return 1 +} + +has_boot_env() +{ + local tag + tag=$1; shift + has_grub_env $tag && return 0 + has_uboot_env $tag && return 0 + return 1 +} + +# Check installer debug option from the boot environment +if has_boot_env onl_installer_debug; then installer_debug=1; fi + +if test "$installer_debug"; then + echo "Debug mode" + set -x +fi + +# Pickup ONIE defines for this machine. +if test -r /etc/machine.conf; then + . /etc/machine.conf +fi + +visit_proc_mounts() { + local ifs line dummy fn rest sts + fn=$1; shift + rest="$@" + + ifs=$IFS; IFS=$CR + for line in $(cat /proc/mounts); do + IFS=$ifs + if eval $fn $line $rest; then + : + else + sts=$? + if test $sts -eq 2; then break; fi + return $sts + fi + done + IFS=$ifs + + return 0 +} + +# +# Installation environment setup. +# + +installer_umount() { + local cwd mpt tdir + cwd=$PWD + cd / + + tdir=${TMPDIR-"/tmp"} + for mpt in $(cat /proc/mounts | cut -d' ' -f2 | sort -r); do + case "$mpt" in + "$tdir"/*) + installer_say "Unmounting $mpt" + umount "$mpt" + ;; + esac + done + + # handle installer_tmpfs specially + if test "$installer_tmpfs"; then + if grep -q " $installer_tmpfs " /proc/mounts; then + + if test "$installer_tmpfs_opts"; then + + # remount if still mounted + + case ",$installer_tmpfs_opts," in + *,size=*,*) ;; + *) + # default if unspecified is 50% of physical memory + installer_tmpfs_opts=${installer_tmpfs_opts},size=50% + ;; + esac + installer_say "Remounting $installer_tmpfs with options $installer_tmpfs_opts" + mount -o remount,$installer_tmpfs_opts $installer_tmpfs + + elif test "$installer_tmpfs" != "/tmp"; then + + # else unmount if still mounted + + installer_say "Unmounting $installer_tmpfs" + umount "$installer_tmpfs" + rmdir "$installer_tmpfs" + + fi + + fi + + fi + + cd $cwd || : + + return 0 +} + +if test "${onie_platform}"; then + # Running under ONIE, most likely in the background in installer mode. + # Our messages have to be sent to the console directly, not to stdout. + installer_say() + { + echo "$@" > /dev/console + } + + # Installation failure message. + installer_cleanup() + { + installer_say "Install failed." + cat /var/log/onie.log > /dev/console + installer_say "Install failed. See log messages above for details" + + installer_umount + + if installer_reboot; then + : + else + sync + sleep 3 + reboot + fi + } +else + if test "$ARCH_X86"; then + echo "Missing onie_platform (invalid /etc/machine.conf)" 1>&2 + exit 1 + fi + # + # Assume we are running in an interactive environment + # + installer_say() + { + echo + echo "* $@" + echo + } + + installer_cleanup() + { + installer_say "Install failed." + installer_umount + exit 1 + } +fi + +trap "installer_cleanup" 0 1 + +# Find a suitable location for TMPDIR + +scan_tmpfs() { + local dev mpt fstype opts tdir + dev=$1; shift + mpt=$1; shift + fstype=$1; shift + opts=$1; shift + shift + shift + tdir="$1" + + case "$fstype" in + ramfs|tmpfs) ;; + *) return 0 ;; + esac + + case "$tdir" in + "$mpt"|"$mpt"/*) + d1=$(stat -c '%D' "$tdir") + d2=$(stat -c '%D' $mpt) + if test "$d1" = "$d2"; then + installer_say "Found installer $fstype on $installer_dir ($mpt) using opts $opts" + installer_tmpfs=$mpt + installer_tmpfs_opts=${opts:-"defaults"} + return 2 + fi + ;; + esac + + return 0 +} + +# maybe installer script was unpacked to a tmpfs/ramfs filesystem +if test -z "$installer_tmpfs" -a "$installer_dir"; then + visit_proc_mounts scan_tmpfs "$installer_dir" + if test "$installer_tmpfs"; then + TMPDIR="$installer_dir" + export TMPDIR + fi +fi +# maybe TMPDIR is on a tmpfs/ramfs filesystem +if test -z "$installer_tmpfs" -a "$TMPDIR"; then + visit_proc_mounts scan_tmpfs "$TMPDIR" + if test "$installer_tmpfs"; then + : + else + installer_say "TMPDIR $TMPDIR is not actually tmpfs, ignoring" + unset TMPDIR + fi +fi +# else, hopefully /tmp is a tmpfs/ramfs +if test -z "$installer_tmpfs"; then + visit_proc_mounts scan_tmpfs /tmp + if test "$installer_tmpfs"; then + TMPDIR=/tmp + export TMPDIR + fi +fi + +if test "$installer_tmpfs"; then + set dummy $(df -k $installer_tmpfs | tail -1) + if test $3 -lt $installer_tmpfs_kmin; then + installer_say "Resizing tmpfs $installer_tmpfs to ${installer_tmpfs_kmin}k" + mount -o remount,size=${installer_tmpfs_kmin}k $installer_tmpfs + else + # existing installer_tmpfs is fine, + # no need to unmount or remount + installer_tmpfs= + installer_tmpfs_opts= + fi +else + installer_say "Creating tmpfs for installer" + installer_tmpfs=$(mktemp -d -t installer-tmpfs-XXXXXX) + installer_tmpfs_opts= + mount -t tmpfs -o size=1024m tmpfs $installer_tmpfs + export TMPDIR=$installer_tmpfs +fi + +# Unpack our distribution +if test "${installer_unpack_only}"; then + installer_list= +else + installer_list=$initrd_archive +fi + +installer_say "Unpacking ONL installer files..." +if test "$SFX_PAD"; then + # ha ha, busybox cannot exclude multiple files + unzip -o $installer_zip $installer_list -x $SFX_PAD +elif test "$SFX_UNZIP"; then + unzip -o $installer_zip $installer_list -x $installer_script +else + dd if=$installer_zip bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS \ + | unzip -o - $installer_list -x $installer_script +fi + +# Developer debugging +if has_boot_env onl_installer_unpack_only; then installer_unpack_only=1; fi +if test "${installer_unpack_only}"; then + installer_say "Unpack only requested." + exit 1 +fi + +rootdir=$(mktemp -d -t "initrd-XXXXXX") +installer_say "Extracting initrd to $rootdir" +if test "$initrd_offset"; then + tmprd=$(mktemp -t initrd-XXXXXX) + dd if="$initrd_archive" of="$tmprd" bs="$initrd_offset" skip=1 + dd if=/dev/null of="$tmprd" bs="$initrd_size" seek=1 + initrd=$tmprd +else + initrd="${installer_dir}/$initrd_archive" +fi +gzip -dc "$initrd" | ( cd "$rootdir" && cpio -imd ) + +# get common installer functions +. "${rootdir}/lib/vendor-config/onl/install/lib.sh" + +installer_mkchroot "${rootdir}" + +# make the installer available to the chroot +mkdir -p "${rootdir}/mnt/installer" +mount -o ro,bind "${installer_dir}" "${rootdir}/mnt/installer" + +# make the onie boot files available to the chroot +mkdir -p "${rootdir}/mnt/onie-boot" +if test -d "/mnt/onie-boot"; then + mount -o ro,bind "/mnt/onie-boot" "${rootdir}/mnt/onie-boot" +fi + +# generate config for installer environment +mkdir -p "${rootdir}/etc/onl" +cp /dev/null "${rootdir}/etc/onl/installer.conf" +echo "onl_version=\"$onl_version\"" >> "${rootdir}/etc/onl/installer.conf" + +# Generate the MD5 signature for ourselves for future reference. +installer_md5=$(md5sum "$0" | awk '{print $1}') +echo "installer_md5=\"$installer_md5\"" >> "${rootdir}/etc/onl/installer.conf" + +# expose the zip file for later expansion by the initrd +case "$installer_zip" in + "${installer_dir}"/*) + echo "installer_zip=\"${installer_zip##*/}\"" >> "${rootdir}/etc/onl/installer.conf" + ;; + *) + zf=$(mktemp "$rootdir/mnt/installer/installer-zip-XXXXXX") + installer_say "Exposing installer archive $installer_zip as $zf" + mount --bind "$installer_zip" $zf + echo "installer_zip=\"${zf##*/}\"" >> "${rootdir}/etc/onl/installer.conf" + ;; +esac + +# Cache our install URL if available +if test -f "$0.url"; then + installer_url=$(cat "$0.url") + echo "installer_url=\"$installer_url\"" >> "${rootdir}/etc/onl/installer.conf" +fi + +echo "installer_dir=/mnt/installer" >> "${rootdir}/etc/onl/installer.conf" + +# include access details for the initrd +if test "$initrd_offset"; then + echo "initrd_archive=\"$initrd_archive\"" >> "${rootdir}/etc/onl/installer.conf" + echo "initrd_offset=\"$initrd_offset\"" >> "${rootdir}/etc/onl/installer.conf" + echo "initrd_size=\"$initrd_size\"" >> "${rootdir}/etc/onl/installer.conf" +fi + +postinst=$(mktemp -t postinst-XXXXXX) +b=${postinst##*/} +echo "installer_chroot=\"${rootdir}\"" >> "${rootdir}/etc/onl/installer.conf" +echo "installer_postinst=\"/mnt/installer/$b\"" >> "${rootdir}/etc/onl/installer.conf" + +# for now, skip the other dot-files in /etc/onl, we do not need them +# to enable initial install + +# no special handling for /tmp or /run, since this is all in /tmp +# anyway + +installer_say "Launching ONL installer" + +installer_shell_dfl="/usr/bin/onl-install --force" +installer_shell=${installer_shell-"$installer_shell_dfl"} +# default, unmount flash filesystems and run the installer script + +# Ugh, unmount /mnt filesystems here, +# they are not accessible from within the chroot +installer_force_umount() { + local dev mpt + dev=$1; shift + mpt=$1; shift + case "$mpt" in + /mnt/*) + installer_say "Unmounting $mpt (--force)" + umount "$mpt" + ;; + esac +} +if test "$installer_shell" = "$installer_shell_dfl"; then + visit_proc_mounts installer_force_umount +else + installer_say "*** using non-default installer command: $installer_shell" + installer_say "*** watch out for lingering mount-points" +fi + +chroot "${rootdir}" $installer_shell + +if test -f "$postinst"; then + installer_say "Invoking post-install actions" + set -x + . "$postinst" + set +x +fi + +trap - 0 1 +installer_umount + +if test "${onie_platform}"; then + installer_reboot +fi + +exit + +# Local variables: +# mode: sh +# sh-basic-offset: 2 +# End: +# Do not add any additional whitespace after this point. diff --git a/builds/any/installer/new-hotness/uboot/builds/Makefile b/builds/any/installer/new-hotness/uboot/builds/Makefile new file mode 100644 index 00000000..ebd2248e --- /dev/null +++ b/builds/any/installer/new-hotness/uboot/builds/Makefile @@ -0,0 +1,111 @@ +ifndef ARCH +$(error $$ARCH not set) +endif + +ONLPLATFORM = python $(ONL)/tools/onlplatform.py +PLATFORMS := $(shell $(ONLPM) --platform-manifest onl-loader-initrd:$(ARCH)) + +MKSHAR = $(ONL)/tools/mkshar +MKSHAR_OPTS = --lazy --unzip-pad --fixup-perms autoperms.sh +MKSHAR_PERMS = autoperms.sh + +VONLDIR = $(ONL)/packages/base/all/vendor-config-onl +PYFIT = $(VONLDIR)/src/bin/pyfit +PYFIT_ENVIRONMENT = PYTHONPATH=$(VONLDIR)/src/python + +# Hardcoded to match ONL File naming conventions. +include $(ONL)/make/version-onl.mk +INSTALLER_NAME=$(FNAME_PRODUCT_VERSION)_ONL-OS_$(FNAME_BUILD_ID)_$(UARCH)_INSTALLER + +# default fit image can be used as the canonical location for the initrd +FIT_IMAGE_ALL := $(shell $(ONLPM) --find-file onl-loader-fit:$(ARCH) onl-loader-fit.itb) +INITRD := $(shell $(ONLPM) --find-file onl-loader-initrd:$(ARCH) onl-loader-initrd-$(ARCH).cpio.gz) +INITRD_BOUNDS := $(shell $(PYFIT_ENVIRONMENT) $(PYFIT) -v offset $(FIT_IMAGE_ALL) --initrd) + +__installer: installer.sh __installer_fit_files __installer_platform_files __installer_swi_files + $(ONL_V_at)rm -rf *INSTALLER* *.md5sum + $(ONL_V_at)cp /dev/null $(MKSHAR_PERMS) + $(ONL_V_at)cp $(ONL)/make/version-onl.sh . + $(ONL_V_at)echo "#!/bin/sh" >> $(MKSHAR_PERMS) + $(ONL_V_at)echo "set -e" >> $(MKSHAR_PERMS) + $(ONL_V_at)echo "set -x" >> $(MKSHAR_PERMS) + $(MKSHAR) $(MKSHAR_OPTS) "$(INSTALLER_NAME)" $(ONL)/tools/scripts/sfx.sh.in installer.sh *.swi *.itb version-onl.sh boot-config + $(ONL_V_at)rm -rf installer.sh *.itb *.swi version-onl.sh autoperms.sh + $(ONL_V_at)md5sum "$(INSTALLER_NAME)" | awk '{ print $$1 }' > "$(INSTALLER_NAME).md5sum" + +installer.sh: Makefile $(ONL)/builds/any/installer/new-hotness/installer.sh.in + $(ONL_V_GEN)cp /dev/null $@ + $(ONL_V_at): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + if test "$(INITRD_BOUNDS)"; then \ + a="$(FIT_IMAGE_ALL)"; a=$${a##*/} ;\ + else \ + a="$(INITRD)"; i=$${a##*/} ;\ + fi ;\ + set dummy $(INITRD_BOUNDS); start=$$2; end=$$3; sz=$$(($$end - $$start + 1)) ;\ + sed \ + -e 's^@ONLVERSION@^$(VERSION_STRING)^g' \ + -e "s^@INITRD_ARCHIVE@^$${a}^g" \ + -e "s^@INITRD_OFFSET@^$$start^g" \ + -e "s^@INITRD_SIZE@^$$sz^g" \ + -e 's^@ARCH@^$(ARCH)^g' \ + $(ONL)/builds/any/installer/new-hotness/installer.sh.in \ + >> $@ + $(ONL_V_at)echo "PAYLOAD_FOLLOWS" >> $@ + +__installer_fit_files: + $(ONL_V_GEN): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + src=$(FIT_IMAGE_ALL) ;\ + dst=$${src##*/} ;\ + if test "$$dst" -nt Makefile; then \ + : ;\ + else \ + echo "Staging $$dst" ;\ + cp $$src $$dst ;\ + fi ;\ + : + +############################## +# +# optionally include custom itb files for each platform +# +############################## + +__installer_platform_files: + $(ONL_V_GEN): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + l="$(PLATFORMS)"; for p in $$l; do \ + echo "Looking for an ITB specific to $$p, ignore errors..." ;\ + src=$$($(ONLPLATFORM) $$p $(ARCH) itb) 2>/dev/null || : ;\ + if test "$$src"; then :; else continue; fi ;\ + dst=$${src##*/} ;\ + echo "Found $$dst" ;\ + if test "$$dst" -nt Makefile; then continue; fi ;\ + echo "Staging $$dst for $$p" ;\ + cp "$$src" "$$dst" ;\ + done ;\ + : + +__installer_swi_files: +ifndef NO_SWI + $(ONL_V_GEN): ;\ + set -e ;\ + if $(ONL_V_P); then set -x; fi ;\ + swidir=$$(mktemp -d $(PWD)/swi-d-XXXXXX) ;\ + $(ONLPM) --extract-dir onl-swi:$(ARCH) $$swidir ;\ + mv $$swidir/usr/share/onl/packages/$(ARCH)/onl-swi/*.swi . ;\ + rm -fr $$swidir ;\ + : +else + $(ONL_V_GEN): +endif + +shar installer: installer + +clean: + rm -f *.swi *.installer *.cpio.gz + diff --git a/builds/any/rootfs/jessie/common/common-packages.yml b/builds/any/rootfs/jessie/common/common-packages.yml index 3b6c00df..ef765ea3 100644 --- a/builds/any/rootfs/jessie/common/common-packages.yml +++ b/builds/any/rootfs/jessie/common/common-packages.yml @@ -69,5 +69,10 @@ - onl-loader-initscripts - onlp-snmpd - oom-shim +- python-parted +- python-yaml +- bzip2 +- xz-utils +- unzip - onl-mibs - openssl diff --git a/builds/any/rootfs/wheezy/common/common-packages.yml b/builds/any/rootfs/wheezy/common/common-packages.yml index 17aae016..40a6bf5d 100644 --- a/builds/any/rootfs/wheezy/common/common-packages.yml +++ b/builds/any/rootfs/wheezy/common/common-packages.yml @@ -68,5 +68,10 @@ - onl-loader-initscripts - onlp-snmpd - oom-shim +- python-parted +- python-yaml +- bzip2 +- xz-utils +- unzip - onl-mibs - openssl diff --git a/builds/armel/installer/new-hotness/Makefile b/builds/armel/installer/new-hotness/Makefile new file mode 100644 index 00000000..003238cf --- /dev/null +++ b/builds/armel/installer/new-hotness/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/builds/armel/installer/new-hotness/PKG.yml b/builds/armel/installer/new-hotness/PKG.yml new file mode 100644 index 00000000..8cf5ff66 --- /dev/null +++ b/builds/armel/installer/new-hotness/PKG.yml @@ -0,0 +1,2 @@ +!include $ONL/builds/any/installer/new-hotness/APKG.yml ARCH=armel + diff --git a/builds/armel/installer/new-hotness/builds/.gitignore b/builds/armel/installer/new-hotness/builds/.gitignore new file mode 100644 index 00000000..fbd18542 --- /dev/null +++ b/builds/armel/installer/new-hotness/builds/.gitignore @@ -0,0 +1 @@ +*INSTALLER diff --git a/builds/armel/installer/new-hotness/builds/Makefile b/builds/armel/installer/new-hotness/builds/Makefile new file mode 100644 index 00000000..e2eb47a8 --- /dev/null +++ b/builds/armel/installer/new-hotness/builds/Makefile @@ -0,0 +1,2 @@ +include $(ONL)/make/config.armel.mk +include $(ONL)/builds/any/installer/new-hotness/uboot/builds/Makefile diff --git a/builds/armel/installer/new-hotness/builds/boot-config b/builds/armel/installer/new-hotness/builds/boot-config new file mode 100644 index 00000000..40fb0d31 --- /dev/null +++ b/builds/armel/installer/new-hotness/builds/boot-config @@ -0,0 +1,4 @@ +NETDEV=ma1 +NETAUTO=dhcp +BOOTMODE=SWI +SWI=images::latest diff --git a/builds/powerpc/installer/new-hotness/Makefile b/builds/powerpc/installer/new-hotness/Makefile new file mode 100644 index 00000000..003238cf --- /dev/null +++ b/builds/powerpc/installer/new-hotness/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/builds/powerpc/installer/new-hotness/PKG.yml b/builds/powerpc/installer/new-hotness/PKG.yml new file mode 100644 index 00000000..c7c734e7 --- /dev/null +++ b/builds/powerpc/installer/new-hotness/PKG.yml @@ -0,0 +1,2 @@ +!include $ONL/builds/any/installer/new-hotness/APKG.yml ARCH=powerpc + diff --git a/builds/powerpc/installer/new-hotness/builds/.gitignore b/builds/powerpc/installer/new-hotness/builds/.gitignore new file mode 100644 index 00000000..fbd18542 --- /dev/null +++ b/builds/powerpc/installer/new-hotness/builds/.gitignore @@ -0,0 +1 @@ +*INSTALLER diff --git a/builds/powerpc/installer/new-hotness/builds/Makefile b/builds/powerpc/installer/new-hotness/builds/Makefile new file mode 100644 index 00000000..e94db6bd --- /dev/null +++ b/builds/powerpc/installer/new-hotness/builds/Makefile @@ -0,0 +1,2 @@ +include $(ONL)/make/config.powerpc.mk +include $(ONL)/builds/any/installer/new-hotness/uboot/builds/Makefile diff --git a/builds/powerpc/installer/new-hotness/builds/boot-config b/builds/powerpc/installer/new-hotness/builds/boot-config new file mode 100644 index 00000000..40fb0d31 --- /dev/null +++ b/builds/powerpc/installer/new-hotness/builds/boot-config @@ -0,0 +1,4 @@ +NETDEV=ma1 +NETAUTO=dhcp +BOOTMODE=SWI +SWI=images::latest diff --git a/packages/base/all/initrds/loader-initrd-files/PKG.yml b/packages/base/all/initrds/loader-initrd-files/PKG.yml index 2fc41557..3ae21ef7 100644 --- a/packages/base/all/initrds/loader-initrd-files/PKG.yml +++ b/packages/base/all/initrds/loader-initrd-files/PKG.yml @@ -14,10 +14,11 @@ packages: - src/etc : /etc - src/lib : /lib - src/bootmodes : /bootmodes + - src/python: /usr/lib/python2.7/site-packages - $ONL/make/version-onl.sh : /etc/onl/loader/versions.sh - $ONL/make/version-onl.json : /etc/onl/loader/versions.json - $ONL/make/version-onl.mk : /etc/onl/loader/versions.mk - + changelog: Change changes changes., diff --git a/packages/base/all/initrds/loader-initrd-files/src/bin/sysinit b/packages/base/all/initrds/loader-initrd-files/src/bin/sysinit index 968d4a92..07b1f2e3 100755 --- a/packages/base/all/initrds/loader-initrd-files/src/bin/sysinit +++ b/packages/base/all/initrds/loader-initrd-files/src/bin/sysinit @@ -36,7 +36,12 @@ trap "restoreconsole; reboot -f" EXIT mount -t proc proc /proc mount -t sysfs sysfs /sys mount -o remount,size=1M /dev - +case "$(stat -f -c "%T" /tmp)" in + tmpfs|ramfs) ;; + *) + mount -t tmpfs tmpfs /tmp + ;; +esac # Grab cmdline settings diff --git a/packages/base/all/initrds/loader-initrd-files/src/python/debcompat.pth b/packages/base/all/initrds/loader-initrd-files/src/python/debcompat.pth new file mode 100644 index 00000000..5c1cb274 --- /dev/null +++ b/packages/base/all/initrds/loader-initrd-files/src/python/debcompat.pth @@ -0,0 +1 @@ +/usr/lib/python2.7/dist-packages diff --git a/packages/base/all/vendor-config-onl/PKG.yml b/packages/base/all/vendor-config-onl/PKG.yml index 5e5a397a..d2544bab 100644 --- a/packages/base/all/vendor-config-onl/PKG.yml +++ b/packages/base/all/vendor-config-onl/PKG.yml @@ -11,6 +11,7 @@ packages: src/python/onl : $PY_INSTALL/onl src/boot.d : /etc/boot.d src/bin : /usr/bin + src/lib : /lib/vendor-config/onl changelog: Changes @@ -23,7 +24,7 @@ packages: summary: ONL Base Configuration Package (Loader) files: - src/python/onl : /usr/lib/python2.7/onl + src/python/onl : /usr/lib/python2.7/dist-packages/onl src/bin/initmounts : /bin/initmounts src/bin/pki : /sbin/pki diff --git a/packages/base/all/vendor-config-onl/src/bin/loader-shell b/packages/base/all/vendor-config-onl/src/bin/loader-shell new file mode 100755 index 00000000..e006aef4 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/loader-shell @@ -0,0 +1,7 @@ +#!/usr/bin/python + +"""Run native ONIE tools +""" + +import onl.install.ShellApp +onl.install.ShellApp.Loader.main() diff --git a/packages/base/all/vendor-config-onl/src/bin/onie-shell b/packages/base/all/vendor-config-onl/src/bin/onie-shell new file mode 100755 index 00000000..8ff8343e --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/onie-shell @@ -0,0 +1,7 @@ +#!/usr/bin/python + +"""Run native ONIE tools +""" + +import onl.install.ShellApp +onl.install.ShellApp.Onie.main() diff --git a/packages/base/all/vendor-config-onl/src/bin/onl-install b/packages/base/all/vendor-config-onl/src/bin/onl-install new file mode 100755 index 00000000..4505eee5 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/onl-install @@ -0,0 +1,7 @@ +#!/usr/bin/python + +"""Install switch light +""" + +import onl.install.App +onl.install.App.main() diff --git a/packages/base/all/vendor-config-onl/src/bin/onl-recover b/packages/base/all/vendor-config-onl/src/bin/onl-recover new file mode 100755 index 00000000..6a9a8589 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/onl-recover @@ -0,0 +1,7 @@ +#!/usr/bin/python + +"""Recover switch light +""" + +import onl.install.RecoverApp +onl.install.RecoverApp.main() diff --git a/packages/base/all/vendor-config-onl/src/bin/pyfit b/packages/base/all/vendor-config-onl/src/bin/pyfit new file mode 100755 index 00000000..4ee57728 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/pyfit @@ -0,0 +1,7 @@ +#!/usr/bin/python + +"""Swiss-army-knife FIT decoder +""" + +import onl.install.Fit +onl.install.Fit.App.main() diff --git a/packages/base/all/vendor-config-onl/src/lib/install/lib.sh b/packages/base/all/vendor-config-onl/src/lib/install/lib.sh new file mode 100644 index 00000000..78a5033b --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/lib/install/lib.sh @@ -0,0 +1,129 @@ +#!/bin/sh +# +###################################################################### +# +# helper functions for install +# +###################################################################### + +installer_reboot() { + local dummy sts timeout trapsts + if test $# -gt 0; then + timeout=$1; shift + else + timeout=3 + fi + + installer_say "Rebooting in ${timeout}s" + + unset dummy trapsts + # ha ha, 'local' auto-binds the variables + + trap "trapsts=130" 2 + if read -t $timeout -r -p "Hit CR to continue, CTRL-D or CTRL-C to stop... " dummy; then + sts=0 + else + sts=$? + fi + trap - 2 + test "$trapsts" && sts=$trapsts + + if test ${dummy+set}; then + if test $sts -eq 0; then + installer_say "CR, rebooting" + exit + else + installer_say "CTRL-D, stopped" + exit + fi + fi + + # ha ha, busybox does not report SIGALRM + if test "${trapsts+set}"; then + : + else + installer_say "timeout, rebooting" + reboot + fi + + signo=$(( $sts - 128 )) + if test $signo -eq 14; then + # SIGALRM, possibly irrelevant for busybox + installer_say "timeout, rebooting" + reboot + fi + + # e.g. SIGQUIT + installer_say "signal $signo, stopped" + exit +} + +installer_mkchroot() { + local rootdir + rootdir=$1 + + # special handling for /dev, which usually already has nested mounts + installer_say "Setting up /dev" + rm -fr "${rootdir}/dev"/* + for dev in /dev/*; do + if test -d "$dev"; then + mkdir "${rootdir}${dev}" + else + cp -a "$dev" "${rootdir}${dev}" + fi + done + mkdir -p "${rootdir}/dev/pts" + + installer_say "Setting up /run" + rm -fr "${rootdir}/run"/* + mkdir -p "${rootdir}/run" + d1=$(stat -c "%D" /run) + for rdir in /run/*; do + if test -d "$rdir"; then + mkdir "${rootdir}${rdir}" + d2=$(stat -c "%D" $rdir) + t2=$(stat -f -c "%T" $rdir) + case "$t2" in + tmpfs|ramfs) + # skip tmpfs, we'll just inherit the initrd ramfs + ;; + *) + if test "$d1" != "$d2"; then + mount -o bind $rdir "${rootdir}${rdir}" + fi + ;; + esac + fi + done + + installer_say "Setting up mounts" + mount -t proc proc "${rootdir}/proc" + mount -t sysfs sysfs "${rootdir}/sys" + mount -t devpts devpts "${rootdir}/dev/pts" + + if test ${TMPDIR+set}; then + # make the tempdir available to the chroot + mkdir -p "${rootdir}${TMPDIR}" + fi + + # export ONIE defines to the installer + if test -r /etc/machine.conf; then + cp /etc/machine.conf "${rootdir}/etc/machine.conf" + fi + + # export ONL defines to the installer + mkdir -p "${rootdir}/etc/onl" + if test -d /etc/onl; then + cp -a /etc/onl/. "${rootdir}/etc/onl/." + fi + + # export firmware config + if test -r /etc/fw_env.config; then + cp /etc/fw_env.config "${rootdir}/etc/fw_env.config" + fi +} + +# Local variables +# mode: sh +# sh-basic-offset: 2 +# End: diff --git a/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-uboot.yml b/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-uboot.yml new file mode 100644 index 00000000..4343389e --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-uboot.yml @@ -0,0 +1,136 @@ +--- + +###################################################################### +# +# platform-config-defaults-uboot.yml +# +# Configuration for u-boot systems (powerpc and arm) +# +###################################################################### + +default: + + flat_image_tree: + + ############################## + # + # Default kernel packages provided by ONL + # + ############################## + + e500v-kernel-package: &e500v-kernel-package + package: onl-kernel-3.9.6-powerpc-e500v:powerpc + + e500v-kernel: &e500v-kernel + =: kernel-3.9.6-powerpc-e500v.bin.gz + <<: *e500v-kernel-package + + e500mc-kernel-package: &e500mc-kernel-package + package: onl-kernel-3.8.13-powerpc-e500mc:powerpc + + e500mc-kernel: &e500mc-kernel + =: kernel-3.8.13-powerpc-e500mc.bin.gz + <<: *e500mc-kernel-package + + arm-iproc-kernel-package: &arm-iproc-kernel-package + package: onl-kernel-3.2-deb7-arm-iproc-all:armel + + arm-iproc-kernel: &arm-iproc-kernel + =: kernel-3.2-deb7-arm-iproc-all.bin.gz + <<: *arm-iproc-kernel-package + + ############################## + # + # For your system, pick from the above list + # to compose a 'kernel' and 'dtb' key + # + ############################## + + ### Example, pick one kernel and one DTB + ##kernel: + ## <<: *e500v-kernel + ##dtb: + ## =: powerpc-quanta-lb9-r0.dtb + ## <<: *e500v-kernel-package + + ############################## + # + # pick an actual loader file, + # usually the 'all' image + # + ############################## + + powerpc-itb: &powerpc-itb + =: onl-loader-fit.itb + package: onl-loader-fit:powerpc + + arm-itb: &arm-itb + =: onl-loader-fit.itb + package: onl-loader-fit:armel + + itb: *powerpc-itb + + loader: + + device: /dev/sda + ##device: /dev/mmcblk0 + + loadaddr: 0x10000000 + ##loadaddr: 70000000 + + # Add your own 'setenv' clauses, + # otherwise lean back and coast with these implicit ones + setenv: + ##- onl_loadaddr: @loadaddr@ + ### added automatically + ##- onl_platform: @platform@ + ### added automatically + ##- onl_itb: @itb@ + - bootargs: >- + console=$consoledev,$baudrate + onl_platform=$onl_platform + + ide_bootcmds: &ide_bootcmds + - ext2load ide 0:1 $onl_loadaddr $onl_itb + - "bootm $onl_loadaddr#$onl_platform" + + usb_bootcmds: &usb_bootcmds + - usb start + - ext2load usb 0:1 $onl_loadaddr $onl_itb + - "bootm $onl_loadaddr#$onl_platform" + + # XXX roth arm example includes the 'usbiddev' magic + usb2_bootcmds: &usb2_bootcmds + - usb start + - usbiddev + - ext2load usb 0:1 $onl_loadaddr $onl_itb + - "bootm $onl_loadaddr#$onl_platform" + + mmc_bootcmds: &mmc_bootcmds + - mmc part 0 + - ext2load mmc 0:1 $onl_loadaddr $onl_itb + - "bootm $onl_loadaddr#$onl_platform" + + nos_bootcmds: *ide_bootcmds + + # Default partitioning scheme + # boot, config --> 128MiB (ext2) + # images --> 1GiB + # data --> rest of disk + # default format (as shown) is ext4 + installer: + - ONL-BOOT: + =: 128MiB + # NOTE that u-boot wants the boot partition ext2, not ext4 + format: ext2 + ##format: raw + + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 1GiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-x86-64.yml b/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-x86-64.yml new file mode 100644 index 00000000..2b188f5c --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/lib/platform-config-defaults-x86-64.yml @@ -0,0 +1,106 @@ +--- + +###################################################################### +# +# platform-config-defaults-x86-64.yml +# +# Default settings for x86-64 platform-config YAML declarations +# +# X86 platforms assume a GPT partition table and ext4 partitions +# +###################################################################### + +default: + + grub: + + label: gpt + # default, use a GPT (not msdos) label + # this is mostly to *reject* invalid disk labels, + # since we will never create our own + + initrd-amd64: &initrd-amd64 + =: onl-loader-initrd-amd64.cpio.gz + package: onl-loader-initrd:amd64 + + initrd: + <<: *initrd-amd64 + + kernel-3.2: &kernel-3-2 + =: kernel-3.2-deb7-x86_64-all + package: onl-kernel-3.2-deb7-x86-64-all:amd64 + + kernel-3.9.6: &kernel-3-9-6 + =: kernel-3.9.6-x86-64-all + package: onl-kernel-3.9.6-x86-64-all:amd64 + + kernel-3.18: &kernel-3-18 + =: kernel-3.18-x86_64-all + package: onl-kernel-3.18-x86-64-all:amd64 + + # pick one of the above kernels + kernel: + <<: *kernel-3-2 + + # GRUB command line arguments for 'serial' declaration + # this is equivalent to, but not in the same format as, + # the linux 'console=' arguments below + # Default for ttyS1 + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + # supplemental kernel arguments + # (not including kernel, initrd and ONL-specific options) + # Default for ttyS1 + args: >- + nopat + console=ttyS1,115200n8 + + ### Defaults for ttyS0 + ##serial: >- + ## --port=0x3f8 + ## --speed=115200 + ## --word=8 + ## --parity=no + ## --stop=1 + ##args: >- + ## nopat + ## console=ttyS0,115200n8 + + ##device: /dev/vda + ### install to a specific block device + + device: ONIE-BOOT + # install to the device that contains the ONIE-BOOT partition + # (query using parted and/or blkid) + + # Default partitioning scheme + # boot, config --> 128MiB + # images --> 1GiB + # data --> rest of disk + # default format (as shown) is ext4 + installer: + - ONL-BOOT: + =: 128MiB + format: ext4 + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 1GiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 + + ### Sample partitioning scheme experiencing disk space pressure + ##installer: + ##- ONL-BOOT: 128MiB + ##- ONL-CONFIG: 128MiB + ##- ONL-IMAGES: 384MiB + ##- ONL-DATA: 100% + diff --git a/packages/base/all/vendor-config-onl/src/python/onl/YamlUtils.py b/packages/base/all/vendor-config-onl/src/python/onl/YamlUtils.py new file mode 100644 index 00000000..4381f799 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/YamlUtils.py @@ -0,0 +1,101 @@ +"""YamlUtils.py + +""" + +import yaml + +def merge(p1, p2): + """Merge two YAML files. + + y1 is the 'default' source; leaf values from y2 will override. + Return the merged tree. + + y1 should be a dict with a single top-level key, 'default'. + y2 should be a dict with a single top-level key, not 'default'. + + Set a leaf in y2 to nil ('~') to create a tombstone (discard any key + from y1). + + if a (sub) key in y1, y2 differ in type (dict vs. non-dict) then + the merge will proceed with the non-dict promoted to a dict using + the default-key schema ('='). Consumers of this function should be + prepared to handle such keys. + """ + + with open(p1) as fd: + buf1 = fd.read() + with open(p2) as fd: + buf2 = fd.read() + + # read p1 as-is, make sure it looks like a 'default' YAML + c1 = yaml.load(buf1) + k1 = list(c1.keys()) + if k1 != ['default']: + raise ValueError("%s: invalid top-level keys for default mapping: %s" + % (p1, k1,)) + + # read p2 with the default YAML as a sub-key (to resolve anchors) + lines = buf2.splitlines(False) + lines = [x for x in lines if x != '---'] + buf3 = buf1 + "\n" + "\n".join(lines) + c2 = yaml.load(buf3) + c2.pop('default', None) + + k2 = list(c2.keys()) + if len(k2) != 1: + raise ValueError("invalid format for target mapping") + tgtKey = k2[0] + + merged = { tgtKey : {} } + q = [(c1['default'], c2[tgtKey], merged[tgtKey])] + while True: + if not q: break + c1, c2, c3 = q.pop(0) + # add in non-overlapping keys + # 'None' keys from p2 are tombstones + s1 = set(c1.keys()) + s2 = set(c2.keys()) + + for k in s1.difference(s2): + v = c1[k] + if type(v) == dict: + c3.setdefault(k, {}) + q.append((v, {}, c3[k],)) + else: + c3.setdefault(k, v) + + for k in s2.difference(s1): + v = c2[k] + if v is None: continue + if type(v) == dict: + c3.setdefault(k, {}) + q.append(({}, v, c3[k],)) + else: + c3.setdefault(k, v) + + # handle overlapping keys + for k in s1.intersection(s2): + v1 = c1[k] + v2 = c2[k] + + if v2 is None: continue + + # two dicts, key-by-key reconciliation required + if type(v1) == dict and type(v2) == dict: + c3.setdefault(k, {}) + q.append((v1, v2, c3[k],)) + continue + + # two non-dicts, p2 wins + if type(v1) != dict and type(v2) != dict: + c3[k] = v2 + continue + + if type(v1) != dict: + v1 = { '=' : v1, } + if type(v2) != dict: + v2 = { '=' : v2, } + c3.setdefault(k, {}) + q.append((v1, v2, c3[k],)) + + return merged diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/App.py b/packages/base/all/vendor-config-onl/src/python/onl/install/App.py new file mode 100644 index 00000000..ad98eb1a --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/App.py @@ -0,0 +1,364 @@ +"""App.py + +top-level install app +""" + +import subprocess +import sys, os +import logging +import imp +import glob +import argparse +import shutil +import urllib +import tempfile +import time + +from InstallUtils import InitrdContext +from InstallUtils import SubprocessMixin +from InstallUtils import ProcMountsParser +import ConfUtils, BaseInstall + +class App(SubprocessMixin): + + def __init__(self, url=None, + debug=False, force=False, + log=None): + + if log is not None: + self.log = log + else: + self.log = logging.getLogger(self.__class__.__name__) + + self.url = url + self.force = force + self.debug = debug + # remote-install mode + + self.installer = None + self.machineConf = None + self.installerConf = None + self.onlPlatform = None + # local-install mode + + self.nextUpdate = None + + def run(self): + + if self.url is not None: + return self.runUrl() + else: + return self.runLocal() + + def runUrl(self): + pm = ProcMountsParser() + for m in pm.mounts: + if m.dir.startswith('/mnt/onl'): + if not self.force: + self.log.error("directory %s is still mounted", m.dir) + return 1 + self.log.warn("unmounting %s (--force)", m.dir) + self.check_call(('umount', m.dir,)) + + def reporthook(blocks, bsz, sz): + if time.time() < self.nextUpdate: return + self.nextUpdate = time.time() + 0.25 + if sz: + pct = blocks * bsz * 100 / sz + sys.stderr.write("downloaded %d%% ...\r" % pct) + else: + icon = "|/-\\"[blocks % 4] + sys.stderr.write("downloading ... %s\r" % icon) + + p = tempfile.mktemp(prefix="installer-", + suffix=".bin") + try: + self.log.info("downloading installer from %s --> %s", + self.url, p) + self.nextUpdate = 0 + if os.isatty(sys.stdout.fileno()): + dst, headers = urllib.urlretrieve(self.url, p, reporthook) + else: + dst, headers = urllib.urlretrieve(self.url, p) + sys.stdout.write("\n") + + self.log.debug("+ chmod +x %s", p) + os.chmod(p, 0755) + + env = {} + env.update(os.environ) + + if os.path.exists("/etc/onl/platform"): + self.log.debug("enabling unzip features for ONL") + env['SFX_UNZIP'] = '1' + self.log.debug("+ export SFX_UNZIP=1") + env['SFX_LOOP'] = '1' + self.log.debug("+ export SFX_LOOP=1") + env['SFX_PIPE'] = '1' + self.log.debug("+ export SFX_PIPE=1") + + self.log.debug("enabling in-place fixups") + env['SFX_INPLACE'] = '1' + self.log.debug("+ export SFX_INPLACE=1") + + if self.debug: + self.log.debug("enabling installer debug") + env['installer_debug'] = 'y' + self.log.debug("+ export installer_debug=y") + if self.log.level < logging.INFO: + self.log.debug("enabling installer verbose logging") + env['installer_verbose'] = 'y' + self.log.debug("+ export installer_verbose=y") + + self.log.info("invoking installer...") + try: + self.check_call((p,), env=env) + except subprocess.CalledProcessError as ex: + self.log.error("installer failed") + return ex.returncode + finally: + if os.path.exists(p): + os.unlink(p) + + self.log.info("please reboot this system now.") + return 0 + + def runLocal(self): + + self.log.info("getting installer configuration") + if os.path.exists(ConfUtils.MachineConf.PATH): + self.machineConf = ConfUtils.MachineConf() + else: + self.log.warn("missing /etc/machine.conf from ONIE runtime") + self.machineConf = ConfUtils.MachineConf(path='/dev/null') + self.installerConf = ConfUtils.InstallerConf() + + ##self.log.info("using native GRUB") + ##self.grubEnv = ConfUtils.GrubEnv(log=self.log.getChild("grub")) + + pat = "/mnt/onie-boot/onie/initrd.img*" + l = glob.glob(pat) + if l: + initrd = l[0] + self.log.info("using native ONIE initrd+chroot GRUB (%s)", initrd) + initrdDir = InitrdContext.mkChroot(initrd, log=self.log) + self.grubEnv = ConfUtils.ChrootGrubEnv(initrdDir, + bootDir="/mnt/onie-boot", + path="/grub/grubenv", + log=self.log.getChild("grub")) + # direct access using ONIE initrd as a chroot + # (will need to fix up bootDir and bootPart later) + else: + self.log.info("using proxy GRUB") + self.grubEnv = ConfUtils.ProxyGrubEnv(self.installerConf, + bootDir="/mnt/onie-boot", + path="/grub/grubenv", + chroot=False, + log=self.log.getChild("grub")) + # indirect access through chroot host + # (will need to fix up bootDir and bootPart later) + + if os.path.exists(ConfUtils.UbootEnv.SETENV): + self.ubootEnv = ConfUtils.UbootEnv(log=self.log.getChild("u-boot")) + else: + self.ubootEnv = None + + self.log.info("ONL Installer %s", self.installerConf.onl_version) + + code = self.findPlatform() + if code: return code + + try: + import onl.platform.current + except: + self.log.exception("cannot find platform config") + code = 1 + if self.log.level < logging.INFO: + self.post_mortem() + if code: return code + + self.onlPlatform = onl.platform.current.OnlPlatform() + + if 'grub' in self.onlPlatform.platform_config: + self.log.info("trying a GRUB based installer") + iklass = BaseInstall.GrubInstaller + elif 'flat_image_tree' in self.onlPlatform.platform_config: + self.log.info("trying a U-Boot based installer") + iklass = BaseInstall.UbootInstaller + else: + self.log.error("cannot detect installer type") + return 1 + + # run the platform-specific installer + self.installer = iklass(machineConf=self.machineConf, + installerConf=self.installerConf, + platformConf=self.onlPlatform.platform_config, + grubEnv=self.grubEnv, + ubootEnv=self.ubootEnv, + force=self.force, + log=self.log) + try: + code = self.installer.run() + except: + self.log.exception("installer failed") + code = 1 + if self.log.level < logging.INFO: + self.post_mortem() + if code: return code + + if getattr(self.installer, 'grub', False): + code = self.finalizeGrub() + if code: return code + if getattr(self.installer, 'uboot', False): + code = self.finalizeUboot() + if code: return code + + self.log.info("Install finished.") + return 0 + + def findPlatform(self): + + plat = arch = None + if os.path.exists(ConfUtils.MachineConf.PATH): + plat = getattr(self.machineConf, 'onie_platform', None) + arch = getattr(self.machineConf, 'onie_arch', None) + if plat and arch: + self.log.info("ONL installer running under ONIE.") + plat = plat.replace('_', '-') + elif os.path.exists("/etc/onl/platform"): + with open("/etc/onl/platform") as fd: + plat = fd.read().strip() + if plat.startswith('x86-64'): + arch = 'x86_64' + else: + arch = plat.partition('-')[0] + self.log.info("ONL installer running under ONL or ONL loader.") + + if plat and arch: + self.installerConf.installer_platform = plat + self.installerConf.installer_arch = arch + else: + self.log.error("The installation platform cannot be determined.") + self.log.error("It does not appear that we are running under ONIE or the ONL loader.") + self.log.error("If you know what you are doing you can re-run this installer") + self.log.error("with an explicit 'installer_platform=' setting,") + self.log.error("though this is unlikely to be the correct procedure at this point.") + return 1 + + self.log.info("Detected platform %s", self.installerConf.installer_platform) + + self.installerConf.installer_platform_dir = ("/lib/platform-config/%s" + % (self.installerConf.installer_platform,)) + if not os.path.isdir(self.installerConf.installer_platform_dir): + self.log.error("This installer does not support the %s platform.", + self.installerConf.installer_platform) + self.log.error("Available platforms are:") + for d in os.listdir("/lib/platform-config"): + self.log.error(" %s", d) + self.log.error("Installation cannot continue.") + return 1 + + return 0 + + def finalizeGrub(self): + + def _m(src, dst): + val = getattr(self.installerConf, src, None) + if val is not None: + setattr(self.grubEnv, dst, val) + else: + delattr(self.grubEnv, dst) + + _m('installer_md5', 'onl_installer_md5') + _m('onl_version', 'onl_installer_version') + _m('installer_url', 'onl_installer_url') + + return 0 + + def finalizeUboot(self): + + if self.installer.platform.isOnie(): + def _m(src, dst): + val = getattr(self.installerConf, src, None) + if val is not None: + setattr(self.ubootEnv, dst, val) + else: + delattr(self.ubootEnv, dst) + + _m('installer_md5', 'onl_installer_md5') + _m('onl_version', 'onl_installer_version') + _m('installer_url', 'onl_installer_url') + else: + self.log.info("To configure U-Boot to boot ONL automatically, reboot the switch,") + self.log.info("enter the U-Boot shell, and run these 2 commands:") + self.log.info("=> setenv bootcmd '%s'", self.installer.platform.str_bootcmd()) + self.log.info("saveenv") + + return 0 + + def shutdown(self): + + installer, self.installer = self.installer, None + if installer is not None: + installer.shutdown() + + def post_mortem(self): + self.log.info("re-attaching to tty") + fdno = os.open("/dev/console", os.O_RDWR) + os.dup2(fdno, sys.stdin.fileno()) + os.dup2(fdno, sys.stdout.fileno()) + os.dup2(fdno, sys.stderr.fileno()) + os.close(fdno) + + self.log.info("entering Python debugger (installer_debug=1)") + import pdb + pdb.post_mortem(sys.exc_info()[2]) + + @classmethod + def main(cls): + + logging.basicConfig() + logger = logging.getLogger("install") + logger.setLevel(logging.DEBUG) + + # send to ONIE log + hnd = logging.FileHandler("/dev/console") + logger.addHandler(hnd) + logger.propagate = False + + onie_verbose = 'onie_verbose' in os.environ + installer_debug = 'installer_debug' in os.environ + + ap = argparse.ArgumentParser() + ap.add_argument('-v', '--verbose', action='store_true', + default=onie_verbose, + help="Enable verbose logging") + ap.add_argument('-D', '--debug', action='store_true', + default=installer_debug, + help="Enable python debugging") + ap.add_argument('-U', '--url', type=str, + help="Install from a remote URL") + ap.add_argument('-F', '--force', action='store_true', + help="Unmount filesystems before install") + ops = ap.parse_args() + + if ops.verbose: + logger.setLevel(logging.DEBUG) + + app = cls(url=ops.url, force=ops.force, + log=logger) + try: + code = app.run() + except: + logger.exception("runner failed") + code = 1 + if ops.debug: + app.post_mortem() + + app.shutdown() + sys.exit(code) + +main = App.main + +if __name__ == "__main__": + main() diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/BaseInstall.py b/packages/base/all/vendor-config-onl/src/python/onl/install/BaseInstall.py new file mode 100644 index 00000000..08e41e76 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/BaseInstall.py @@ -0,0 +1,817 @@ +"""BaseInstall.py + +Base classes for installers. +""" + +import os, stat +import subprocess +import re +import tempfile +import logging +import StringIO +import parted +import yaml +import zipfile +import shutil + +from InstallUtils import SubprocessMixin +from InstallUtils import MountContext, BlkidParser, PartedParser +from InstallUtils import ProcMountsParser + +import onl.YamlUtils + +class Base: + + class installmeta: + + grub = False + uboot = False + + def __init__(self, + installerConf=None, + machineConf=None, + platformConf=None, + grubEnv=None, ubootEnv=None): + self.installerConf = installerConf + self.machineConf = machineConf + self.platformConf = platformConf + self.grubEnv = grubEnv + self.ubootEnv = ubootEnv + + def isOnie(self): + if self.machineConf is None: return False + plat = getattr(self.machineConf, 'onie_platform', None) + return plat is not None + + def __init__(self, + machineConf=None, installerConf=None, platformConf=None, + grubEnv=None, ubootEnv=None, + force=False, + log=None): + self.im = self.installmeta(installerConf=installerConf, + machineConf=machineConf, + platformConf=platformConf, + grubEnv=grubEnv, + ubootEnv = ubootEnv) + self.log = log or logging.getLogger(self.__class__.__name__) + + self.force = force + # unmount filesystems as needed + + self.device = None + # target device, initialize this later + + self.minpart = None + self.nextBlock = None + # keep track of next partition/next block + + self.blkidParts = [] + # current scan of partitions and labels + + self.partedDevice = None + self.partedDisk = None + # parted state + + self.configArchive = None + # backup of ONL-CONFIG during re-partitioning + + self.zf = None + # zipfile handle to installer archive + + def run(self): + self.log.error("not implemented") + return 1 + + def shutdown(self): + zf, self.zf = self.zf, None + if zf: zf.close() + + def installerCopy(self, basename, dst, optional=False): + """Copy the file as-is, or get it from the installer zip.""" + + src = os.path.join(self.im.installerConf.installer_dir, basename) + if os.path.exists(src): + self.copy2(src, dst) + return + + if basename in self.zf.namelist(): + self.log.debug("+ unzip -p %s %s > %s", + self.im.installerConf.installer_zip, basename, dst) + with self.zf.open(basename, "r") as rfd: + with open(dst, "wb") as wfd: + shutil.copyfileobj(rfd, wfd) + return + + if not optional: + raise ValueError("missing installer file %s" % basename) + + def installerDd(self, basename, device): + + p = os.path.join(self.im.installerConf.installer_dir, basename) + if os.path.exists(p): + cmd = ('dd', + 'if=' + basename, + 'of=' + device,) + self.check_call(cmd, vmode=self.V2) + return + + if basename in self.zf.namelist(): + self.log.debug("+ unzip -p %s %s | dd of=%s", + self.im.installerConf.installer_zip, basename, device) + with self.zf.open(basename, "r") as rfd: + with open(device, "rb+") as wfd: + shutil.copyfileobj(rfd, wfd) + return + + raise ValueError("cannot find file %s" % basename) + + def installerExists(self, basename): + if basename in os.listdir(self.im.installerConf.installer_dir): return True + if basename in self.zf.namelist(): return True + return False + + def installSwi(self): + + files = os.listdir(self.im.installerConf.installer_dir) + self.zf.namelist() + swis = [x for x in files if x.endswith('.swi')] + if not swis: + self.log.info("No ONL Software Image available for installation.") + self.log.info("Post-install ZTN installation will be required.") + return + if len(swis) > 1: + self.log.warn("Multiple SWIs found in installer: %s", " ".join(swis)) + return + + base = swis[0] + + self.log.info("Installing ONL Software Image (%s)...", base) + dev = self.blkidParts['ONL-IMAGES'] + with MountContext(dev.device, log=self.log) as ctx: + dst = os.path.join(ctx.dir, base) + self.installerCopy(base, dst) + + return 0 + + def backupConfig(self, dev): + """Back up the ONL-CONFIG partition for later restore.""" + self.configArchive = tempfile.mktemp(prefix="onl-config-", + suffix=".tar.gz") + self.log.info("backing up ONL-CONFIG partition %s to %s", + dev, self.configArchive) + with MountContext(dev, log=self.log) as ctx: + self.log.debug("+ tar -zcf %s -C %s .", + self.configArchive, ctx.dir) + pipe = subprocess.Popen(["tar", "-zcf", self.configArchive, ".",], + cwd=ctx.dir) + pipe.communicate() + code = pipe.wait() + if code: + raise SystemExit("backup of ONL-CONFIG failed") + + def restoreConfig(self, dev): + """Restore the saved ONL-CONFIG.""" + archive, self.configArchive = self.configArchive, None + self.log.info("restoring ONL-CONFIG archive %s to %s", + archive, dev) + with MountContext(dev, log=self.log) as ctx: + self.log.debug("+ tar -zxf %s -C %s", + archive, ctx.dir) + pipe = subprocess.Popen(["tar", "-zxf", archive,], + cwd=ctx.dir) + pipe.communicate() + code = pipe.wait() + if code: + raise SystemExit("backup of ONL-CONFIG failed") + self.unlink(archive) + + def deletePartitions(self): + + nextBlock = -1 + dirty = False + for part in self.partedDisk.partitions: + self.log.info("examining %s part %d", + self.partedDisk.device.path, part.number) + if part.number < self.minpart: + self.log.info("skip this part") + nextBlock = max(part.geometry.start+part.geometry.length, + nextBlock) + else: + self.log.info("deleting this part") + self.partedDisk.removePartition(part) + dirty = True + + if dirty: + self.partedDisk.commit() + self.check_call(('partprobe', self.device,)) + + if nextBlock > -1: + self.nextBlock = nextBlock + else: + self.log.warn("no partitions, starting at block 1") + + return 0 + + def partitionParted(self): + """Build partitions according to the partition spec. + + XXX roth -- hopefully the GPT labels specified here + work correctly (that is, are ignored) on an msdos label + """ + + constraint = self.partedDevice.optimalAlignedConstraint + # default partition layout constraint + + devices = {} + + def _u2s(sz, u): + bsz = sz * u + bsz = bsz + self.partedDevice.physicalSectorSize - 1 + return bsz / self.partedDevice.physicalSectorSize + + UNITS = { + 'GiB' : 1024 * 1024 * 1024, + 'G' : 1000 * 1000 * 1000, + 'MiB' : 1024 * 1024, + 'M' : 1000 * 1000, + 'KiB' : 1024, + 'K' : 1000, + } + + for part in self.im.platformConf['installer']: + + label, partData = list(part.items())[0] + if type(partData) == dict: + sz, fmt = partData['='], partData.get('format', 'ext4') + else: + sz, fmt = partData, 'ext4' + + cnt = None + nextBlock = self.nextBlock or 1 + minpart = self.minpart or 1 + for ul, ub in UNITS.items(): + if sz.endswith(ul): + cnt = _u2s(int(sz[:-len(ul)], 10), ub) + break + if sz == '100%': + cnt = self.partedDevice.getLength() - nextBlock + if cnt is None: + self.log.error("invalid size (no units) for %s: %s", + part, sz) + return 1 + + start = nextBlock + end = start + cnt - 1 + if end <= self.partedDevice.getLength(): + self.log.info("Allocating %d sectors for %s", + cnt, label) + else: + self.log.warn("%s: start sector %d, end sector %d, max %d", + label, start, end, + self.partedDevice.getLength()) + self.log.error("invalid partition %s [%s] (too big)", + label, sz) + return 1 + + geom = parted.Geometry(device=self.partedDevice, + start=start, length=end-start+1) + fs = parted.FileSystem(type=fmt, geometry=geom) + part = parted.Partition(disk=self.partedDisk, + type=parted.PARTITION_NORMAL, + fs=fs, + geometry=geom) + if self.partedDisk.type == 'gpt': + part.getPedPartition().set_name(label) + self.partedDisk.addPartition(part, constraint=constraint) + self.partedDisk.commit() + self.check_call(('partprobe', self.device,)) + + if fmt == 'raw': + self.log.info("Leaving %s (%s) unformatted (raw)", + part.path, label) + else: + self.log.info("Formatting %s (%s) as %s", + part.path, label, fmt) + if fmt == 'msdos': + self.mkdosfs(part.path, label=label) + elif fmt == 'ext4': + self.mke4fs(part.path, label=label, huge_file=False) + elif fmt == 'ext2': + self.mke2fs(part.path, label=label) + else: + self.mkfs(part.path, fstype=fmt) + + self.nextBlock, self.minpart = end+1, minpart+1 + + devices[label] = part.path + + if label == 'ONL-CONFIG' and self.configArchive is not None: + self.restoreConfig(part.path) + + self.blkidParts = BlkidParser(log=self.log.getChild("blkid")) + # re-read the partitions + + return 0 + + def installBootConfig(self): + + try: + dev = self.blkidParts['ONL-BOOT'] + except IndexError as ex: + self.log.warn("cannot find ONL-BOOT partition (maybe raw?) : %s", str(ex)) + return 1 + + self.log.info("Installing boot-config to %s", dev.device) + + basename = 'boot-config' + with MountContext(dev.device, log=self.log) as ctx: + dst = os.path.join(ctx.dir, basename) + self.installerCopy(basename, dst) + with open(dst) as fd: + buf = fd.read() + + ecf = buf.encode('base64', 'strict').strip() + if self.im.grub and self.im.grubEnv is not None: + setattr(self.im.grubEnv, 'boot_config_default', ecf) + if self.im.uboot and self.im.ubootEnv is not None: + setattr(self.im.ubootEnv, 'boot-config-default', ecf) + + return 0 + + def assertUnmounted(self): + """Make sure the install device does not have any active mounts.""" + pm = ProcMountsParser() + for m in pm.mounts: + if m.device.startswith(self.device): + if not self.force: + self.log.error("mount %s on %s will be erased by install", + m.dir, m.device) + return 1 + else: + self.log.warn("unmounting %s from %s (--force)", + m.dir, m.device) + try: + self.check_call(('umount', m.dir,)) + except subprocess.CalledProcessError: + self.log.error("cannot unmount") + return 1 + + return 0 + +GRUB_TPL = """\ +#serial --port=0x3f8 --speed=115200 --word=8 --parity=no --stop=1 +serial %(serial)s +terminal_input serial +terminal_output serial +set timeout=5 + +menuentry OpenNetworkLinux { + search --no-floppy --label --set=root ONL-BOOT + echo 'Loading Open Network Linux ...' + insmod gzio + insmod part_msdos + #linux /kernel-3.9.6-x86-64-all nopat console=ttyS0,115200n8 onl_platform=x86-64-kvm-x86-64-r0 + linux /%(kernel)s %(args)s onl_platform=%(platform)s + initrd /%(initrd)s +} + +# Menu entry to chainload ONIE +menuentry ONIE { + search --no-floppy --label --set=root ONIE-BOOT + echo 'Loading ONIE ...' + chainloader +1 +} +""" + +class GrubInstaller(SubprocessMixin, Base): + """Installer for grub-based systems (x86).""" + + class installmeta(Base.installmeta): + grub = True + + def __init__(self, *args, **kwargs): + Base.__init__(self, *args, **kwargs) + + def findGpt(self): + self.blkidParts = BlkidParser(log=self.log.getChild("blkid")) + + deviceOrLabel = self.im.platformConf['grub']['device'] + if deviceOrLabel.startswith('/dev'): + tgtDevice, tgtLabel = deviceOrLabel, None + else: + tgtDevice, tgtLabel = None, deviceOrLabel + + # enumerate labeled partitions to try to identify + # the boot device + for part in self.blkidParts: + dev, partno = part.splitDev() + if tgtLabel is not None and tgtLabel == part.label: + if not len(partno): + self.log.error("cannot use whole disk") + return 1 + if self.device is None: + self.device = dev + else: + self.log.error("found multiple devices: %s, %s", + dev, self.device) + return 1 + elif tgtDevice is not None and tgtDevice == dev: + if not len(partno): + self.log.error("cannot use whole disk") + return 1 + if self.device is None: + self.device = dev + else: + self.log.error("found multiple devices: %s, %s", + dev, self.device) + return 1 + if self.device is None: + self.log.error("cannot find an install device") + return 1 + + code = self.assertUnmounted() + if code: return code + + # optionally back up a config partition + # if it's on the boot device + for part in self.blkidParts: + dev, partno = part.splitDev() + if dev == self.device and part.label == 'ONL-CONFIG': + self.backupConfig(part.device) + + self.partedDevice = parted.getDevice(self.device) + self.partedDisk = parted.newDisk(self.partedDevice) + + # enumerate the partitions that will stay and go + minpart = -1 + for part in self.partedDisk.partitions: + + if part.getFlag(parted.PARTITION_HIDDEN): + minpart = max(minpart, part.number+1) + continue + + # else, the partition should exist + blkidParts = [x for x in self.blkidParts if x.device == part.path] + if not blkidParts: + self.log.warn("cannot identify partition %s", part) + continue + + blkidPart = blkidParts[0] + if not blkidPart.isOnieReserved(): continue + + # else, check the GPT label for reserved-ness + if (part.name + and ('GRUB' in part.name + or 'ONIE-BOOT' in part.name + or 'DIAG' in part.name)): + minpart = max(minpart, part.number+1) + + if minpart < 0: + self.log.error("cannot find an install partition") + return 1 + self.minpart = minpart + + return 0 + + def installLoader(self): + + ctx = {} + + kernel = self.im.platformConf['grub']['kernel'] + ctx['kernel'] = kernel['='] if type(kernel) == dict else kernel + + initrd = self.im.platformConf['grub']['initrd'] + ctx['initrd'] = initrd['='] if type(initrd) == dict else initrd + + ctx['args'] = self.im.platformConf['grub']['args'] + ctx['platform'] = self.im.installerConf.installer_platform + ctx['serial'] = self.im.platformConf['grub']['serial'] + + cf = GRUB_TPL % ctx + + self.log.info("Installing kernel") + dev = self.blkidParts['ONL-BOOT'] + + files = set(os.listdir(self.im.installerConf.installer_dir) + self.zf.namelist()) + files = [b for b in files if b.startswith('kernel-') or b.startswith('onl-loader-initrd-')] + + with MountContext(dev.device, log=self.log) as ctx: + def _cp(b): + dst = os.path.join(ctx.dir, b) + self.installerCopy(b, dst, optional=True) + [_cp(e) for e in files] + + d = os.path.join(ctx.dir, "grub") + self.makedirs(d) + dst = os.path.join(ctx.dir, 'grub/grub.cfg') + with open(dst, "w") as fd: + fd.write(cf) + + return 0 + + def installGrub(self): + self.log.info("Installing GRUB to %s", self.partedDevice.path) + self.im.grubEnv.install(self.partedDevice.path) + return 0 + + def installGpt(self): + + code = self.findGpt() + if code: return code + + self.log.info("Installing to %s starting at partition %d", + self.device, self.minpart) + + self.log.info("disk is %s", self.partedDevice.path) + + if self.partedDisk.type != 'gpt': + self.log.error("not a GPT partition table") + return 1 + if self.partedDevice.sectorSize != 512: + self.log.error("invalid logical block size") + return 1 + if self.partedDevice.physicalSectorSize != 512: + self.log.error("invalid physical block size") + return 1 + + self.log.info("found a disk with %d blocks", + self.partedDevice.getLength()) + + code = self.deletePartitions() + if code: return code + + self.log.info("next usable block is %s", self.nextBlock) + + code = self.partitionParted() + if code: return code + + # once we assign the ONL-BOOT partition, + # we can re-target the grub environment + dev = self.blkidParts['ONL-BOOT'] + self.im.grubEnv.__dict__['bootPart'] = dev.device + self.im.grubEnv.__dict__['bootDir'] = None + + # get a handle to the installer zip + p = os.path.join(self.im.installerConf.installer_dir, + self.im.installerConf.installer_zip) + self.zf = zipfile.ZipFile(p) + + code = self.installSwi() + if code: return code + + code = self.installLoader() + if code: return code + + code = self.installBootConfig() + if code: return code + + code = self.installGrub() + if code: return code + + self.log.info("ONL loader install successful.") + self.log.info("GRUB installation is required next.") + + return 0 + + def run(self): + if 'grub' not in self.im.platformConf: + self.log.error("platform config is missing a GRUB section") + return 1 + label = self.im.platformConf['grub'].get('label', None) + if label != 'gpt': + self.log.error("invalid GRUB label in platform config: %s", label) + return 1 + return self.installGpt() + + def shutdown(self): + Base.shutdown(self) + +class UbootInstaller(SubprocessMixin, Base): + + class installmeta(Base.installmeta): + + uboot = True + + def getDevice(self): + loader = self.platformConf.get('loader', {}) + dev = loader.get('device', None) + return dev + + def str_bootcmd(self): + cmds = [] + cmds.append("setenv onl_loadaddr 0x%x" + % self.platformConf['loader']['loadaddr']) + cmds.append("setenv onl_platform %s" + % self.installerConf.installer_platform) + itb = self.platformConf['flat_image_tree']['itb'] + if type(itb) == dict: itb = itb['='] + cmds.append("setenv onl_itb %s" % itb) + for item in self.platformConf['loader']['setenv']: + k, v = list(item.items())[0] + cmds.append("setenv %s %s" % (k, v,)) + cmds.extend(self.platformConf['loader']['nos_bootcmds']) + return "; ".join(cmds) + + def __init__(self, *args, **kwargs): + Base.__init__(self, *args, **kwargs) + + self.device = self.im.getDevice() + + code = self.assertUnmounted() + if code: return code + + self.rawLoaderDevice = None + # set to a partition device for raw loader install, + # default to None for FS-based install + + def maybeCreateLabel(self): + """Set up an msdos label.""" + + self.partedDevice = parted.getDevice(self.device) + try: + self.partedDisk = parted.newDisk(self.partedDevice) + if self.partedDisk.type == 'msdos': + self.log.info("disk %s is already msdos", self.device) + return 0 + self.log.warn("disk %s has wrong label %s", + self.device, self.partedDisk.type) + except parted._ped.PartedException as ex: + self.log.error("cannot get partition table from %s: %s", + self.device, str(ex)) + + self.log.info("creating msdos label on %s") + self.partedDisk = parted.freshDisk(self.partedDevice, 'msdos') + + return 0 + + def findMsdos(self): + """Backup any existing data. + + The GPT version of this function is more tricky since it needs + to save some of the partitions. Here with and msdos label that + is on a different block device from u-boot or ONIE, we don't + really care. + """ + + # optionally back up a config partition + # if it's on the boot device + for part in self.blkidParts: + dev, partno = part.splitDev() + if dev == self.device and part.label == 'ONL-CONFIG': + self.backupConfig(part.device) + + self.minPart = -1 + # default, delete all partitions + # XXX roth -- tweak this if we intent to save e.g. + # a diag partition from the vendor + + return 0 + + def installLoader(self): + + c1 = self.im.platformConf['flat_image_tree'].get('itb', None) + if type(c1) == dict: c1 = c1.get('=', None) + c2 = ("%s.itb" + % (self.im.installerConf.installer_platform,)) + c3 = "onl-loader-fit.itb" + + loaderBasename = None + for c in (c1, c2, c3): + if c is None: continue + if self.installerExists(c): + loaderBasename = c + break + + if not loaderBasename: + self.log.error("The platform loader file is missing.") + return 1 + + self.log.info("Installing the ONL loader from %s...", loaderBasename) + + if self.rawLoaderDevice is not None: + self.log.info("Installing ONL loader %s --> %s...", + loaderBasename, self.rawLoaderDevice) + self.installerDd(loaderBasename, self.rawLoaderDevice) + return 0 + + dev = self.blkidParts['ONL-BOOT'] + self.log.info("Installing ONL loader %s --> %s:%s...", + loaderBasename, dev.device, loaderBasename) + with MountContext(dev.device, log=self.log) as ctx: + dst = os.path.join(ctx.dir, loaderBasename) + self.installerCopy(loaderBasename, dst) + + return 0 + + def installUbootEnv(self): + + # Special access instructions for initrd + off = getattr(self.im.installerConf, 'initrd_offset', None) + if off is not None: + if self.rawLoaderDevice is not None: + a = self.rawLoaderDevice + else: + a = self.im.installerConf.initrd_archive + s = int(self.im.installerConf.initrd_offset) + e = s + int(self.im.installerConf.initrd_size) - 1 + self.im.ubootEnv.onl_installer_initrd = ("%s:%x:%x" % (a, s, e,)) + else: + try: + del self.im.installerConf.onl_installer_initrd + except AttributeError: + pass + + if self.im.isOnie(): + self.log.info("Setting ONIE nos_bootcmd to boot ONL") + self.im.ubootEnv.nos_bootcmd = self.im.str_bootcmd() + else: + self.log.warn("U-boot boot setting is not changed") + + return 0 + + def installUboot(self): + + if self.device is None: + self.log.error("missing block device YAML config") + return 1 + st = os.stat(self.device) + if not stat.S_ISBLK(st[stat.ST_MODE]): + self.log.error("not a block device: %s", self.device) + return 1 + + code = self.maybeCreateLabel() + if code: return code + + self.log.info("Installing to %s", self.device) + + if self.partedDisk.type != 'msdos': + self.log.error("not an MSDOS partition table") + return 1 + if self.partedDevice.sectorSize != 512: + self.log.error("invalid logical block size") + return 1 + if self.partedDevice.physicalSectorSize != 512: + self.log.error("invalid physical block size") + return 1 + + self.log.info("found a disk with %d blocks", + self.partedDevice.getLength()) + + code = self.findMsdos() + if code: return code + + code = self.deletePartitions() + if code: return code + + self.log.info("next usable block is %s", self.nextBlock) + + code = self.partitionParted() + if code: return code + + # compute the path to the raw loader partition, + # if indicated by the configuration + + self.rawLoaderDevice = None + for item in self.im.platformConf['installer']: + partIdx, partData = list(item.items())[0] + label, part = list(partData.items())[0] + if label == 'ONL-BOOT' and part['format'] == 'raw': + self.rawLoaderDevice = self.device + str(partIdx+1) + break + + # get a handle to the installer zip + p = os.path.join(self.im.installerConf.installer_dir, + self.im.installerConf.installer_zip) + self.zf = zipfile.ZipFile(p) + + code = self.installSwi() + if code: return code + + code = self.installLoader() + if code: return code + + if self.rawLoaderDevice is None: + code = self.installBootConfig() + if code: return code + else: + self.log.info("ONL-BOOT is a raw partition (%s), skipping boot-config", + self.rawLoaderDevice) + + self.log.info("syncing block devices") + self.check_call(('sync',)) + # XXX roth probably not needed + + code = self.installUbootEnv() + if code: return code + + return 0 + + def run(self): + + if 'flat_image_tree' not in self.im.platformConf: + self.log.error("platform config is missing a FIT section") + return 1 + + return self.installUboot() + + def shutdown(self): + Base.shutdown(self) diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/BaseRecovery.py b/packages/base/all/vendor-config-onl/src/python/onl/install/BaseRecovery.py new file mode 100644 index 00000000..881b9b4f --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/BaseRecovery.py @@ -0,0 +1,240 @@ +"""BaseRecovery.py + +Base classes for recovery. +""" + +import subprocess, os, stat +import tempfile +import binascii +import glob +import logging +from InstallUtils import TempdirContext, MountContext, SubprocessMixin, ProcMountsParser +from InstallUtils import InitrdContext, BlkidParser +from ConfUtils import ChrootGrubEnv + +class Base(SubprocessMixin): + + class recovermeta: + + bootConfig = "/mnt/flash/boot-config" + bootConfigDfl = "/etc/boot-config.default" + + @property + def needRecovery(self): + if os.path.exists('/mnt/flash/.notmounted'): return True + if os.path.exists('/mnt/flash2/.notmounted'): return True + return False + + def __init__(self, + ubootEnv=None, + log=None): + self.platform = self.recovermeta() + self.ubootEnv = ubootEnv + self.log = log or logging.getLogger(self.__class__.__name__) + + def recoverFull(self): + self.log.error("not implemented") + return 1 + + def recoverConfig(self): + if os.path.exists(self.platform.bootConfig): return 0 + self.copy2(self.platform.bootConfigDfl, self.platform.bootConfig) + return 0 + + def run(self): + + if self.platform.needRecovery: + self.log.info("Attempting recovery") + code = self.recoverFull() + if code: return code + + code = self.recoverConfig() + if code: return code + + return 0 + + def umountAny(self, device=None, label=None): + p = ProcMountsParser() + if label is not None: + b = BlkidParser(log=self.log) + for e in b.parts: + if label == e.label: + device = e.device + break + + for m in p.mounts: + if device is not None and device in m.device: + try: + self.check_call(('umount', m.device,), + vmode=self.V1) + except CalledProcessError, what: + self.log.warn("cannot umount %s: %s", + m.device, str(what)) + return 0 + + def shutdown(self): + pass + +class GrubRecovery(Base): + + class recovermeta(Base.recovermeta): + pass + + def recoverX86(self): + + def _u(l): + self.umountAny(label=l) + def _l(l): + try: + return self.check_output(('blkid', '-L', l,)).strip() + except subprocess.CalledProcessError: + return None + def _r(l): + _u(l) + dev = _l(l) + if dev is not None: + self.log.info("Recovering %s partition", l) + self.check_call(('mkdosfs', '-n', l, dev,), + vmode=self.V1) + + _r('FLASH') + _r('FLASH2') + + return 0 + + def recoverGrubConfig(self): + + with MountContext(label='ONIE-BOOT', log=self.log) as octx: + + pat = "%s/onie/initrd.img*" % octx.dir + l = glob.glob(pat) + if not l: + raise ValueError("cannot find ONIE initrd") + initrd = l[0] + + with InitrdContext(initrd=initrd, log=self.log) as ictx: + + # copy the Switch Light grubenv out of its GRUB directory + dst = os.path.join(ictx.dir, "tmp/grubenv") + with MountContext(label='SL-BOOT', log=self.log) as sctx: + src = os.path.join(sctx.dir, "grub/grubenv") + self.copy2(src, dst) + + # use the ONIE runtime's GRUB tools to read it + grubEnv = ChrootGrubEnv(ictx.dir, mounted=True, + bootDir="/", + path="/tmp/grubenv", + log=self.log) + buf = getattr(grubEnv, 'boot_config_default', None) + + if buf is None: + raise ValueError("Cannot recover filesystem(s) -- missing boot_config_default.") + if buf == "": + raise ValueError("Cannot recover filesystem(s) -- empty boot_config_default.") + try: + buf = buf.decode('base64', 'strict') + except binascii.Error: + raise ValueError("Cannot recover filesystem(s) -- corrupted boot_config_default.") + if "SWI=flash" in buf: + raise ValueError("Cannot recover filesystem(s) -- local SWI cannot be recovered.") + + with MountContext(label='FLASH', log=self.log) as ctx: + dst = os.path.join(ctx.dir, 'boot-config') + with open(dst, "w") as fd: + self.log.debug("+ cat > %s", dst) + fd.write(buf) + + return 0 + + def recoverFull(self): + self.log.info("Recovering flash partitions.") + + code = self.recoverX86() + if code: return code + + code = self.recoverGrubConfig() + if code: return code + + self.check_call(('initmounts',)) + + return 0 + +class UbootRecovery(Base): + + class recovermeta(Base.recovermeta): + + def __init__(self, ubootEnv=None): + self.ubootEnv = ubootEnv + + device = None + # fill this in per-platform + + @property + def bootConfigEnv(self): + if self.ubootEnv is None: + raise ValueError("missing u-boot environment tools") + buf = getattr(self.ubootEnv, 'boot-config-default', None) + if buf is None: + raise ValueError("Cannot recover filesystem(s) -- missing boot-config-default.") + if buf == "": + raise ValueError("Cannot recover filesystem(s) -- empty boot-config-default.") + try: + buf = buf.decode('base64', 'strict') + except binascii.Error: + raise ValueError("Cannot recover filesystem(s) -- corrupted boot-config-default.") + if "SWI=flash" in buf: + raise ValueError("Cannot recover filesystem(s) -- local SWI cannot be recovered.") + return buf + + def __init__(self, + ubootEnv=None, + log=None): + self.ubootEnv = ubootEnv + self.platform = self.recovermeta(ubootEnv=ubootEnv) + self.log = log or logging.getLogger(self.__class__.__name__) + + self.flashDev = self.platform.device + '2' + self.flash2Dev = self.platform.device + '3' + + def recoverUboot(self): + if not os.path.exists(self.platform.device): + self.log.error("missing block device, cannot recover") + return 1 + st = os.stat(self.platform.device) + if not stat.S_ISBLK(st[stat.ST_MODE]): + self.log.error("invalid block device") + return 1 + + code = self.umountAny(device=self.platform.device) + if code: return code + + self.log.info("Re-formatting %s", self.platform.device) + cmd = ('mkdosfs', '-n', 'FLASH', self.flashDev,) + self.check_call(cmd, vmode=self.V1) + cmd = ('mkdosfs', '-n', 'FLASH2', self.flash2Dev,) + self.check_call(cmd, vmode=self.V1) + return 0 + + def recoverUbootConfig(self): + with MountContext(self.flashDev, log=self.log) as ctx: + dst = os.path.join(ctx.dir, 'boot-config') + with open(dst, "w") as fd: + self.log.debug("+ cat > %s", dst) + fd.write(self.platform.bootConfigEnv) + return 0 + + def recoverFull(self): + + code = self.recoverUboot() + if code: return code + + self.recoverUbootConfig() + if code: return code + + self.log.info("syncing block devices") + self.check_call(('sync',)) + # XXX roth probably not needed + + self.check_call(('initmounts',)) + + return 0 diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/ConfUtils.py b/packages/base/all/vendor-config-onl/src/python/onl/install/ConfUtils.py new file mode 100644 index 00000000..b81fd347 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/ConfUtils.py @@ -0,0 +1,390 @@ +"""ConfUtils.py + +Config interfaces to different backend mechanisms. +""" + +import os +import logging +import subprocess +from InstallUtils import SubprocessMixin, ChrootSubprocessMixin, MountContext + +class ConfBase: + + def __init__(self): + self._parse() + + def _parse(self): + raise NotImplementedError + + def _feedLine(self, line): + line = line.strip() + if not line: return + + idx = line.find('=') + if idx < 0: + raise ValueError("invalid line in %s: %s" + % (self.path, line,)) + key, val = line[:idx], line[idx+1:] + if val[:1] == '"' and val[-1:] == '"': + val = val[1:-1] + if val[:1] == "'" and val[-1:] == "'": + val = val[1:-1] + self.__dict__['_data'][key] = val + + def __getattr__(self, attr, *args): + if len(args) == 1: + return self.__dict__['_data'].get(attr, args[0]) + elif len(args) == 0: + try: + return self.__dict__['_data'][attr] + except KeyError, what: + raise AttributeError(str(what)) + else: + raise ValueError("extra arguments") + + def __setattr__(self, attr, val): + self.__dict__['_data'][attr] = val + +class ConfFileBase(ConfBase): + + PATH = None + # Override me + + def __init__(self, path=None): + self.__dict__['path'] = path or self.PATH + ConfBase.__init__(self) + + def _parse(self): + self.__dict__['_data'] = {} + with open(self.path) as fd: + for line in fd.xreadlines(): + self._feedLine(line) + +class MachineConf(ConfFileBase): + PATH = "/etc/machine.conf" + +class InstallerConf(ConfFileBase): + PATH = "/etc/onl/installer.conf" + +class ConfBuf(ConfBase): + + def __init__(self, buf): + self.__dict__['buf'] = buf + ConfBase.__init__(self) + + def _parse(self): + self.__dict__['_data'] = {} + for line in self.buf.splitlines(): + self._feedLine(line) + +class GrubEnv(SubprocessMixin): + + INSTALL = "grub-install" + EDITENV = "grub-editenv" + # system default + + ENV_PATH = "/grub/grubenv" + # override me + + def __init__(self, + bootDir=None, bootPart=None, + path=None, + log=None): + + if bootDir and bootPart: + raise ValueError("cannot specify bootDir and bootPart") + if not bootDir and not bootPart: + raise ValueError("missing bootDir or bootPart") + self.__dict__['bootDir'] = bootDir + self.__dict__['bootPart'] = bootPart + # location of GRUB boot files (mounted directory or unmounted partition) + + self.__dict__['path'] = path or self.ENV_PATH + # path to grubenv, relative to above + + self.__dict__['log'] = log or logging.getLogger("grub") + + def mountCtx(self, device): + return MountContext(device, fsType='ext4', log=self.log) + + def asDict(self): + if self.bootPart: + with self.mountCtx(self.bootPart) as ctx: + p = os.path.join(ctx.dir, self.path.lstrip('/')) + buf = self.check_output((self.EDITENV, p, 'list',)).strip() + else: + p = os.path.join(self.bootDir, self.path.lstrip('/')) + buf = self.check_output((self.EDITENV, p, 'list',)).strip() + cf = ConfBuf(buf) + return cf.__dict__['_data'] + + toDict = asDict + + def __getattr__(self, *args): + + args = list(args) + attr = args.pop(0) + + d = self.asDict() + if args: + return d.get(attr, args[0]) + try: + return d[attr] + except KeyError, what: + raise AttributeError(str(what)) + + def __setattr__(self, attr, val): + if self.bootPart: + with self.mountCtx(self.bootPart) as ctx: + p = os.path.join(ctx.dir, self.path.lstrip('/')) + cmd = (self.EDITENV, p, 'set', ("%s=%s" % (attr, val,)),) + self.check_call(cmd) + else: + p = os.path.join(self.bootDir, self.path.lstrip('/')) + cmd = (self.EDITENV, p, 'set', ("%s=%s" % (attr, val,)),) + self.check_call(cmd) + + def __delattr__(self, attr): + if self.bootPart: + with self.mountCtx(self.bootPart) as ctx: + p = os.path.join(ctx.dir, self.path.lstrip('/')) + cmd = (self.EDITENV, p, 'unset', attr,) + self.check_call(cmd) + else: + p = os.path.join(self.bootDir, self.path.lstrip('/')) + cmd = (self.EDITENV, p, 'unset', attr,) + self.check_call(cmd) + + def install(self, device): + if self.bootDir is not None: + self.check_call((self.INSTALL, '--boot-directory=' + self.bootDir, device,)) + elif self.bootPart is not None: + with self.mountCtx(self.bootPart) as ctx: + self.check_call((self.INSTALL, '--boot-directory=' + ctx.dir, device,)) + else: + self.check_call((self.INSTALL, device,)) + +class ChrootGrubEnv(ChrootSubprocessMixin, GrubEnv): + + def __init__(self, + chrootDir, + mounted=False, + bootDir=None, bootPart=None, + path=None, + log=None): + self.__dict__['chrootDir'] = chrootDir + self.__dict__['mounted'] = mounted + GrubEnv.__init__(self, + bootDir=bootDir, bootPart=bootPart, + path=path, + log=log) + + def mountCtx(self, device): + return MountContext(device, + chroot=self.chrootDir, fsType='ext4', + log=self.log) + +class ProxyGrubEnv: + """Pretend to manipulate the GRUB environment. + + Instead, write a trace of shell commands to a log + so that e.g. the chroot's host can execute it with + the proper GRUB runtime. + """ + + INSTALL = "grub-install" + EDITENV = "grub-editenv" + # system defaults + + ENV_PATH = "/grub/grubenv" + # override this + + def __init__(self, + installerConf, + bootDir=None, chroot=True, bootPart=None, + path=None, + log=None): + + self.__dict__['installerConf'] = installerConf + # installer state, to retrieve e.g. chroot directory and trace log + + if bootDir and bootPart: + raise ValueError("cannot specify bootDir and bootPart") + if not bootDir and not bootPart: + raise ValueError("missing bootDir or bootPart") + self.__dict__['bootDir'] = bootDir + self.__dict__['bootPart'] = bootPart + # location of GRUB boot files (mounted directory or unmounted partition) + + self.__dict__['chroot'] = chroot + # True of the bootDir is inside the chroot, + # else bootDir is in the host's file namespace + + self.__dict__['path'] = path or self.ENV_PATH + # path to grubenv, relative to above + + self.__dict__['log'] = log or logging.getLogger("grub") + + def asDict(self): + raise NotImplementedError("proxy grubenv list not implemented") + + toDict = asDict + + def __getattr__(self, *args): + raise NotImplementedError("proxy grubenv list not implemented") + + def __setattr__(self, attr, val): + self.log.warn("deferring commands to %s...", self.installerConf.installer_postinst) + + cmds = [] + if self.bootDir and self.chroot: + p = os.path.join(self.installerConf.installer_chroot, + self.bootDir.lstrip('/'), + self.path.lstrip('/')) + cmds.append(("%s %s set %s=\"%s\"" % (self.EDITENV, p, attr, val,))) + elif self.bootDir: + p = os.path.join(self.bootDir, + self.path.lstrip('/')) + cmds.append(("%s %s set %s=\"%s\"" % (self.EDITENV, p, attr, val,))) + else: + p = ("${mpt}/%s" + % (self.path.lstrip('/'),)) + cmds.append("mpt=$(mktemp -t -d)") + cmds.append("mount %s $mpt" % self.bootPart) + cmds.append(("sts=0; %s %s set %s=\"%s\" || sts=$?" + % (self.EDITENV, p, attr, val,))) + cmds.append("umount $mpt") + cmds.append("rmdir $mpt") + cmds.append("test $sts -eq 0") + + with open(self.installerConf.installer_postinst, "a") as fd: + for cmd in cmds: + self.log.debug("+ [PROXY] " + cmd) + fd.write(cmd) + fd.write("\n") + + def __delattr__(self, attr): + self.log.warn("deferring commands to %s...", self.installerConf.installer_postinst) + + cmds = [] + if self.bootDir and self.chroot: + p = os.path.join(self.installerConf.installer_chroot, + self.bootDir.lstrip('/'), + self.path.lstrip('/')) + cmds.append(("%s %s unset %s" % (self.EDITENV, p, attr,))) + elif self.bootDir: + p = os.path.join(self.bootDir, + self.path.lstrip('/')) + cmds.append(("%s %s unset %s" % (self.EDITENV, p, attr,))) + else: + p = ("$mpt%s" + % (self.path.lstrip('/'),)) + cmds.append("mpt=$(mktemp -t -d)") + cmds.append("mount %s $mpt" % self.bootPart) + cmds.append(("sts=0; %s %s unset %s || sts=$?" + % (self.EDITENV, p, attr,))) + cmds.append("umount $mpt") + cmds.append("rmdir $mpt") + cmds.append("test $sts -eq 0") + + with open(self.installerConf.installer_postinst, "a") as fd: + for cmd in cmds: + self.log.debug("+ [PROXY] " + cmd) + fd.write(cmd) + fd.write("\n") + + def install(self, device): + self.log.warn("deferring commands to %s...", self.installerConf.installer_postinst) + cmds = [] + if self.bootDir and self.chroot: + p = os.pat.join(self.installerConf.installer_chroot, + self.bootDir.lstrip('/')) + cmds.append(("%s --boot-directory=\"%s\" %s" % (self.INSTALL, p, device,))) + elif self.bootDir: + p = self.bootDir + cmds.append(("%s --boot-directory=\"%s\" %s" % (self.INSTALL, p, device,))) + elif self.bootPart: + cmds.append("mpt=$(mktemp -t -d)") + cmds.append("mount %s $mpt" % self.bootPart) + cmds.append(("sts=0; %s --boot-directory=\"$mpt\" %s || sts=$?" + % (self.INSTALL, device,))) + cmds.append("umount $mpt") + cmds.append("rmdir $mpt") + cmds.append("test $sts -eq 0") + else: + cmds.append(("%s %s" + % (self.INSTALL, device,))) + + with open(self.installerConf.installer_postinst, "a") as fd: + for cmd in cmds: + self.log.debug("+ [PROXY] " + cmd) + fd.write(cmd) + fd.write("\n") + +class UbootEnv(SubprocessMixin): + + # ha ha, loader and SWI use different paths + if os.path.exists("/usr/sbin/fw_setenv"): + SETENV = "/usr/sbin/fw_setenv" + elif os.path.exists("/usr/bin/fw_setenv"): + SETENV = "/usr/bin/fw_setenv" + else: + SETENV = "/bin/false" + + if os.path.exists("/usr/sbin/fw_printenv"): + PRINTENV = "/usr/sbin/fw_printenv" + elif os.path.exists("/usr/bin/fw_printenv"): + PRINTENV = "/usr/bin/fw_printenv" + else: + PRINTENV = "/bin/false" + + def __init__(self, log=None): + self.__dict__['log'] = log or logging.getLogger("u-boot") + + self.__dict__['hasForceUpdate'] = False + try: + out = self.check_output((self.SETENV, '--help',), + stderr=subprocess.STDOUT) + if "-f" in out and "Force update" in out: + self.__dict__['hasForceUpdate'] = True + except subprocess.CalledProcessError: + if self.SETENV != '/bin/false': + raise + + def __getattr__(self, *args): + + args = list(args) + attr = args.pop(0) + + with open(os.devnull, "w") as nfd: + try: + out = self.check_output((self.PRINTENV, '-n', attr,), + stderr=nfd.fileno()) + except subprocess.CalledProcessError: + out = None + + if out is not None: return out + + if args: + return args[0] + + raise AttributeError("firmware tag not found") + + def __setattr__(self, attr, val): + if self.hasForceUpdate: + self.check_call((self.SETENV, '-f', attr, val,)) + else: + self.check_call((self.SETENV, attr, val,)) + + def __delattr__(self, attr): + + if self.hasForceUpdate: + self.check_call((self.SETENV, '-f', attr,)) + else: + self.check_call((self.SETENV, attr,)) + + def asDict(self): + buf = self.check_output((self.PRINTENV,)).strip() + return ConfBuf(buf).__dict__['_data'] + + toDict = asDict diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/Fit.py b/packages/base/all/vendor-config-onl/src/python/onl/install/Fit.py new file mode 100644 index 00000000..b3ca037f --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/Fit.py @@ -0,0 +1,578 @@ +"""Fit.py + +Parse FIT files. +""" + +import os, sys +import logging +import struct +import argparse +import time + +class FdtProperty: + def __init__(self, name, offset, sz): + self.name = name + self.offset = offset + self.sz = sz + +class FdtNode: + def __init__(self, name): + self.name = name + self.properties = {} + self.nodes = {} + +class Parser: + + FDT_MAGIC = 0xd00dfeed + + FDT_BEGIN_NODE = 1 + FDT_END_NODE = 2 + FDT_PROP = 3 + FDT_NOP = 4 + FDT_END = 9 + + def __init__(self, path=None, stream=None, log=None): + self.log = log or logging.getLogger(self.__class__.__name__) + self.path = path + self.stream = stream + self.rootNodes = {} + self._parse() + + def _parse(self): + if self.stream is not None: + try: + pos = self.stream.tell() + self._parseStream(self.stream) + finally: + self.stream.seek(pos, 0) + elif self.path is not None: + with open(self.path) as fd: + self._parseStream(fd) + else: + raise ValueError("missing file or stream") + + def _parseStream(self, fd): + strings = {} + + buf = fd.read(40) + hdr = list(struct.unpack(">10I", buf)) + magic = hdr.pop(0) + if magic != self.FDT_MAGIC: + raise ValueError("missing magic") + self.fdtSize = hdr.pop(0) + self.structPos = hdr.pop(0) + self.stringPos = hdr.pop(0) + self.version = hdr.pop(0) + if self.version < 17: + raise ValueError("invalid format version") + hdr.pop(0) # last compatible version + hdr.pop(0) # boot cpu + self.stringSize = hdr.pop(0) + self.structSize = hdr.pop(0) + + fd.seek(self.structPos, 0) + + def _align(): + pos = fd.tell() + pos = (pos+3) & ~3 + fd.seek(pos, 0) + + def _label(): + buf = "" + while True: + c = fd.read(1) + if c == '\x00': break + if c: + buf += c + return buf + + def _string(off): + if off in strings: + return strings[off] + pos = fd.tell() + fd.seek(self.stringPos, 0) + fd.seek(off, 1) + buf = _label() + fd.seek(pos) + return buf + + nodeStack = [] + + while True: + buf = fd.read(4) + s = list(struct.unpack(">I", buf)) + tag = s.pop(0) + + if tag == self.FDT_BEGIN_NODE: + name = _label() + _align() + + newNode = FdtNode(name) + + if nodeStack: + if name in nodeStack[-1].nodes: + raise ValueError("duplicate node") + nodeStack[-1].nodes[name] = newNode + nodeStack.append(newNode) + else: + if name in self.rootNodes: + raise ValueError("duplicate node") + self.rootNodes[name] = newNode + nodeStack.append(newNode) + + continue + + if tag == self.FDT_PROP: + buf = fd.read(8) + s = list(struct.unpack(">2I", buf)) + plen = s.pop(0) + nameoff = s.pop(0) + name = _string(nameoff) + pos = fd.tell() + fd.seek(plen, 1) + _align() + + newProp = FdtProperty(name, pos, plen) + + if nodeStack: + if name in nodeStack[-1].properties: + raise ValueError("duplicate property") + nodeStack[-1].properties[name] = newProp + else: + raise ValueError("property with no node") + + continue + + if tag == self.FDT_END_NODE: + if nodeStack: + nodeStack.pop(-1) + else: + raise ValueError("missing begin node") + continue + + if tag == self.FDT_NOP: + print "NOP" + continue + + if tag == self.FDT_END: + if nodeStack: + raise ValueError("missing end node(s)") + break + + raise ValueError("invalid tag %d" % tag) + + def report(self, stream=sys.stdout): + q = [(x, "") for x in self.rootNodes.values()] + while q: + n, pfx = q.pop(0) + + name = n.name or "/" + stream.write("%s%s\n" % (pfx, name,)) + + if n.properties: + stream.write("\n") + for p in n.properties.values(): + stream.write("%s %s (%d bytes)\n" + % (pfx, p.name, p.sz,)) + if n.properties: + stream.write("\n") + + pfx2 = pfx + " " + q[0:0] = [(x, pfx2) for x in n.nodes.values()] + + def getNode(self, path): + if path == '/': + return self.rootNodes.get('', None) + + els = path.split('/') + n = None + while els: + b = els.pop(0) + if n is None: + if b not in self.rootNodes: return None + n = self.rootNodes[b] + else: + if b not in n.nodes: return None + n = n.nodes[b] + return n + + def getNodeProperty(self, node, propName): + if propName not in node.properties: return None + prop = node.properties[propName] + def _get(fd): + fd.seek(self.structPos, 0) + fd.seek(prop.offset) + buf = fd.read(prop.sz) + if buf[-1] == '\x00': + return buf[:-1] + return buf + if self.stream is not None: + return _get(self.stream) + else: + with open(self.path) as fd: + return _get(fd) + + def dumpNodeProperty(self, node, propIsh, outPath): + if isinstance(propIsh, FdtProperty): + prop = propIsh + else: + if propIsh not in node.properties: + raise ValueError("missing property") + prop = node.properties[propIsh] + def _dump(fd): + with open(outPath, "w") as wfd: + fd.seek(prop.offset, 0) + buf = fd.read(prop.sz) + wfd.write(buf) + if self.stream is not None: + try: + pos = self.stream.tell() + _dump(self.stream) + finally: + self.stream.seek(pos, 0) + else: + with open(self.path) as fd: + _dump(fd) + + def getInitrdNode(self, profile=None): + """U-boot mechanism to retrieve boot profile.""" + + node = self.getNode('/configurations') + if node is None: + self.log.warn("missing /configurations node") + return None + if profile is not None: + if profile not in node.nodes: + self.log.warn("missing profile %s", profile) + return None + node = node.nodes[profile] + elif 'default' in node.properties: + pf = self.getNodeProperty(node, 'default') + self.log.debug("default profile is %s", pf) + node = node.nodes[pf] + else: + pf = node.nodes.keys()[0] + self.log.debug("using profile %s", pf) + node = node.nodes[pf] + + if 'ramdisk' not in node.properties: + self.log.warn("ramdisk property not found") + return None + rdName = self.getNodeProperty(node, 'ramdisk') + + self.log.debug("retrieving ramdisk %s", rdName) + node = self.getNode('/images/' + rdName) + return node + +class DumpRunner: + + def __init__(self, stream, + log=None): + self.log = log or logging.getLogger(self.__class__.__name__) + self.stream = stream + + def run(self): + p = Parser(stream=self.stream, log=self.log) + p.report() + return 0 + + def shutdown(self): + stream, self.stream = self.stream, None + if stream is not None: stream.close() + +class ExtractBase: + + def __init__(self, stream, + initrd=False, profile=None, path=None, + property=None, + log=None): + self.log = log or logging.getLogger(self.__class__.__name__) + self.stream = stream + self.initrd = initrd + self.profile = profile + self.path = path + self.property = property + + self.parser = None + self.node = None + self.dataProp = None + + def run(self): + self.parser = Parser(stream=self.stream, log=self.log) + if self.path is not None: + self.node = self.parser.getNode(self.path) + if self.node is None: + self.log.error("cannot find path") + return 1 + elif self.initrd: + self.node = self.parser.getInitrdNode(profile=self.profile) + if self.node is None: + self.log.error("cannot find initrd") + return 1 + else: + self.log.error("missing path or initrd") + return 1 + + def _t(n): + if n is None: return + self.dataProp = self.dataProp or self.node.properties.get(n, None) + _t(self.property) + _t('data') + _t('value') + if self.dataProp is None: + self.log.error("cannot find %s property", self.property) + return 1 + + return self._handleParsed() + + def _handleParsed(self): + raise NotImplementedError + + def shutdown(self): + stream, self.stream = self.stream, None + if stream is not None: stream.close() + +class ExtractRunner(ExtractBase): + + def __init__(self, stream, + outStream=None, + initrd=False, profile=None, path=None, + property=None, + text=False, numeric=False, timestamp=False, hex=False, + log=None): + ExtractBase.__init__(self, stream, + initrd=initrd, profile=profile, + path=path, + property=property, + log=log) + self.outStream = outStream + self.text = text + self.numeric = numeric + self.timestamp = timestamp + self.hex = hex + + def _handleParsed(self): + if (self.numeric or self.timestamp) and self.dataProp.sz != 4: + self.log.error("invalid size for number") + return 1 + def _dump(rfd, wfd): + rfd.seek(self.dataProp.offset, 0) + buf = rfd.read(self.dataProp.sz) + if self.text: + if buf[-1:] != '\x00': + self.log.error("missing NUL terminator") + return 1 + wfd.write(buf[:-1]) + return 0 + if self.numeric: + n = struct.unpack(">I", buf)[0] + wfd.write(str(n)) + return 0 + if self.timestamp: + n = struct.unpack(">I", buf)[0] + wfd.write(time.ctime(n)) + return 0 + if self.hex: + for c in buf: + wfd.write("%02x" % ord(c)) + return 0 + wfd.write(buf) + return 0 + if self.outStream is not None: + return _dump(self.stream, self.outStream) + else: + return _dump(self.stream, sys.stdout) + +class OffsetRunner(ExtractBase): + + def __init__(self, stream, + initrd=False, profile=None, path=None, + property=None, + log=None): + ExtractBase.__init__(self, stream, + initrd=initrd, profile=profile, + path=path, + property=property, + log=log) + + def _handleParsed(self): + start = self.dataProp.offset + self.log.debug("first byte is %d", start) + end = start + self.dataProp.sz - 1 + self.log.debug("data size is %d", self.dataProp.sz) + self.log.debug("last byte is %d", end) + sys.stdout.write("%s %s\n" % (start, end,)) + return 0 + +USAGE = """\ +pyfit [OPTIONS] dump|extract ... +""" + +EPILOG = """\ +Payload for 'offset' and 'extract' is specified as a given +PROPERTY for a tree node at PATH. + +Alternately, the initrd/ramdisk can be specified with '--initrd', +using the PROFILE machine configuration. If no PROFILE is specified, +the built-in default configuration from the FDT is used. +""" + +DESC="""\ +Extract or examine FIT file contents. +""" + +DUMP_USAGE = """\ +pyfit [OPTIONS] dump FIT-FILE +""" + +EXTRACT_USAGE = """\ +pyfit [OPTIONS] extract [OPTIONS] FIT-FILE +""" + +EXTRACT_EPILOG = """\ +Extracts payload to OUTPUT or to stdout if not specified. + +Output can be optionally reformatted +as a NUL-terminated string ('--text'), +as a decimal number ('--number'), +as a UNIX timestamp ('--timestamp'), +or as hex data ('--hex'). + +Numbers and timestamps must be 4-byte payloads. +""" + +OFFSET_USAGE = """\ +pyfit [OPTIONS] offset [OPTIONS] FIT-FILE +""" + +OFFSET_EPILOG = """\ +Outputs the first and last byte offsets, inclusive, containing the +payload. +""" + +class App: + + def __init__(self, log=None): + self.log = log or logging.getLogger("pyfit") + + def run(self): + + ap = argparse.ArgumentParser(usage=USAGE, + description=DESC, + epilog=EPILOG) + ap.add_argument('-q', '--quiet', action='store_true', + help="Suppress log messages") + ap.add_argument('-v', '--verbose', action='store_true', + help="Add more logging") + + sp = ap.add_subparsers() + + apd = sp.add_parser('dump', + help="Dump tree structure", + usage=DUMP_USAGE) + apd.set_defaults(mode='dump') + apd.add_argument('fit-file', type=open, + help="FIT file") + + apx = sp.add_parser('extract', + help="Extract items", + usage=EXTRACT_USAGE, + epilog=EXTRACT_EPILOG) + apx.set_defaults(mode='extract') + apx.add_argument('fit-file', type=open, + help="FIT file") + apx.add_argument('-o', '--output', + type=argparse.FileType('wb', 0), + help="File destination") + apx.add_argument('--initrd', action="store_true", + help="Extract platform initrd") + apx.add_argument('--profile', type=str, + help="Platform profile for initrd selection") + apx.add_argument('--path', type=str, + help="Tree path to extract") + apx.add_argument('--property', type=str, + help="Node property to extract") + apx.add_argument('--text', action='store_true', + help="Format property as text") + apx.add_argument('--numeric', action='store_true', + help="Format property as a number") + apx.add_argument('--hex', action='store_true', + help="Format property as hex") + apx.add_argument('--timestamp', action='store_true', + help="Format property as a date") + + apo = sp.add_parser('offset', + help="Extract item offset", + usage=OFFSET_USAGE, + epilog=OFFSET_EPILOG) + apo.set_defaults(mode='offset') + apo.add_argument('fit-file', type=open, + help="FIT file") + apo.add_argument('--initrd', action="store_true", + help="Extract platform initrd") + apo.add_argument('--profile', type=str, + help="Platform profile for initrd selection") + apo.add_argument('--path', type=str, + help="Tree path to extract") + apo.add_argument('--property', type=str, + help="Node property to extract") + + try: + args = ap.parse_args() + except SystemExit, what: + return what.code + + if args.quiet: + self.log.setLevel(logging.ERROR) + if args.verbose: + self.log.setLevel(logging.DEBUG) + + if args.mode == 'dump': + r = DumpRunner(getattr(args, 'fit-file'), log=self.log) + elif args.mode == 'extract': + r = ExtractRunner(getattr(args, 'fit-file'), + outStream=args.output, + path=args.path, + initrd=args.initrd, profile=args.profile, + property=args.property, + text=args.text, numeric=args.numeric, + timestamp=args.timestamp, hex=args.hex, + log=self.log) + elif args.mode == 'offset': + r = OffsetRunner(getattr(args, 'fit-file'), + path=args.path, + initrd=args.initrd, profile=args.profile, + property=args.property, + log=self.log) + else: + self.log.error("invalid mode") + return 1 + + try: + code = r.run() + except: + self.log.exception("runner failed") + code = 1 + r.shutdown() + return code + + def shutdown(self): + pass + + @classmethod + def main(cls): + logging.basicConfig() + logger = logging.getLogger("pyfit") + app = cls(log=logger) + try: + code = app.run() + except: + logger.exception("app failed") + code = 1 + app.shutdown() + sys.exit(code) + +main = App.main + +if __name__ == "__main__": + main() diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/InstallUtils.py b/packages/base/all/vendor-config-onl/src/python/onl/install/InstallUtils.py new file mode 100644 index 00000000..f2e2232f --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/InstallUtils.py @@ -0,0 +1,871 @@ +"""InstallUtils.py + +""" + +import os, sys +import stat +import logging +import subprocess +import tempfile +import string +import shutil + +class SubprocessMixin: + + V1 = "V1" + V2 = "V2" + + def check_call(self, *args, **kwargs): + args = list(args) + kwargs = dict(kwargs) + + cwd = kwargs.pop('cwd', None) + if cwd is not None: + self.log.debug("+ cd " + cwd) + + if args: + cmd = args.pop(0) + else: + cmd = kwargs.pop('cmd') + + vmode = kwargs.pop('vmode', None) + if vmode == self.V1 and self.log.isEnabledFor(logging.DEBUG): + if isinstance(cmd, basestring): + raise ValueError("vmode=V1 requires a list") + cmd = list(cmd) + cmd[1:1] = ['-v',] + if vmode == self.V2 and self.log.isEnabledFor(logging.DEBUG): + stdout = kwargs.pop('stdout', None) + stderr = kwargs.pop('stderr', None) + if stdout is not None: + raise ValueError("vmode=V2 conflicts with stdout") + if stderr is not None and stderr != subprocess.STDOUT: + raise ValueError("vmode=V2 conflicts with stderr") + fno, v2Out = tempfile.mkstemp(prefix='subprocess-', + suffix='out') + kwargs['stdout'] = fno + kwargs['stderr'] = subprocess.STDOUT + + if isinstance(cmd, basestring): + self.log.debug("+ " + cmd) + else: + self.log.debug("+ " + " ".join(cmd)) + + if vmode == self.V2 and self.log.isEnabledFor(logging.DEBUG): + try: + subprocess.check_call(cmd, *args, cwd=cwd, **kwargs) + finally: + with open(v2Out) as fd: + sys.stderr.write(fd.read()) + os.unlink(v2Out) + else: + subprocess.check_call(cmd, *args, cwd=cwd, **kwargs) + + def check_output(self, *args, **kwargs): + args = list(args) + kwargs = dict(kwargs) + + cwd = kwargs.pop('cwd', None) + if cwd is not None: + self.log.debug("+ cd " + cwd) + + if args: + cmd = args.pop(0) + else: + cmd = kwargs.pop('cmd') + + vmode = kwargs.pop('vmode', None) + if vmode == self.V1 and self.log.isEnabledFor(logging.DEBUG): + if isinstance(cmd, basestring): + raise ValueError("vmode=V1 requires a list") + cmd = list(cmd) + cmd[1:1] = ['-v',] + if vmode == self.V2 and self.log.isEnabledFor(logging.DEBUG): + stdout = kwargs.pop('stdout', None) + stderr = kwargs.pop('stderr', None) + if stdout is not None: + raise ValueError("vmode=V2 conflicts with stdout") + if stderr is not None and stderr != subprocess.STDOUT: + raise ValueError("vmode=V2 conflicts with stderr") + fno, v2Out = tempfile.mkstemp(prefix='subprocess-', + suffix='out') + kwargs['stderr'] = fno + + if isinstance(cmd, basestring): + self.log.debug("+ " + cmd) + else: + self.log.debug("+ " + " ".join(cmd)) + + if vmode == self.V2 and self.log.isEnabledFor(logging.DEBUG): + try: + return subprocess.check_output(cmd, *args, cwd=cwd, **kwargs) + finally: + with open(v2Out) as fd: + sys.stderr.write(fd.read()) + os.unlink(v2Out) + else: + return subprocess.check_output(cmd, *args, cwd=cwd, **kwargs) + + def rmdir(self, path): + self.log.debug("+ /bin/rmdir %s", path) + os.rmdir(path) + + def unlink(self, path): + self.log.debug("+ /bin/rm %s", path) + os.unlink(path) + + def rmtree(self, path): + self.log.debug("+ /bin/rm -fr %s", path) + shutil.rmtree(path) + + def mkdtemp(self, *args, **kwargs): + path = tempfile.mkdtemp(*args, **kwargs) + self.log.debug("+ /bin/mkdir %s", path) + return path + + def copy2(self, src, dst): + self.log.debug("+ /bin/cp -a %s %s", src, dst) + shutil.copy2(src, dst) + + def copyfile(self, src, dst): + self.log.debug("+ /bin/cp %s %s", src, dst) + shutil.copyfile(src, dst) + + def mkdir(self, path): + self.log.debug("+ /bin/mkdir %s", path) + os.mkdir(path) + + def makedirs(self, path): + self.log.debug("+ /bin/mkdir -p %s", path) + os.makedirs(path) + + def symlink(self, tgt, dst): + self.log.debug("+ /bin/ln -s %s %s", tgt, dst) + os.symlink(tgt, dst) + + def mkdosfs(self, dev, label=None): + if label is not None: + cmd = ('mkdosfs', '-n', label, dev,) + else: + cmd = ('mkdosfs', dev,) + self.check_call(cmd, vmode=self.V1) + + def mke2fs(self, dev, label=None): + if label is not None: + cmd = ('mkfs.ext2', '-L', label, dev,) + else: + cmd = ('mkfs.ext2', dev,) + self.check_call(cmd, vmode=self.V1) + + def mke4fs(self, dev, label=None, huge_file=True): + if label is not None: + cmd = ['mkfs.ext4', '-L', label, dev,] + else: + cmd = ['mkfs.ext4', dev,] + + if not huge_file: + cmd[1:1] = ['-O', '^huge_file',] + # hack needed for some old ONIE kernels + + self.check_call(cmd, vmode=self.V1) + + def mkfs(self, dev, fstype): + mkfs = 'mkfs.%s' % fstype + cmd = (mkfs, dev,) + + # 'mkfs -h' says to use '-V' for verbose, + # don't believe it + self.check_call(cmd, vmode=self.V1) + +class TempdirContext(SubprocessMixin): + + def __init__(self, prefix=None, suffix=None, chroot=None, log=None): + self.prefix = prefix + self.suffix = suffix + self.chroot = chroot + self.dir = None + self.hostDir = None + self.log = log or logging.getLogger("mount") + + def __enter__(self): + if self.chroot is not None: + self.hostDir = self.mkdtemp(prefix=self.prefix, + suffix=self.suffix, + dir=self.chroot + "/tmp") + self.dir = self.hostDir[len(self.chroot):] + else: + self.dir = self.hostDir = self.mkdtemp(prefix=self.prefix, + suffix=self.suffix) + return self + + def __exit__(self, type, value, tb): + if self.path: self.rmtree(self.hostDir) + return False + +class MountContext(SubprocessMixin): + + def __init__(self, device=None, chroot=None, label=None, fsType=None, log=None): + self.device = device + self.chroot = chroot + self.label = label + self.fsType = fsType + self.dir = None + self.hostDir = None + self.mounted = False + self.log = log or logging.getLogger("mount") + + if self.device and self.label: + raise ValueError("cannot specify device and label") + if not self.device and not self.label: + raise ValueError("no device or label specified") + + def __enter__(self): + dev = self.device + if dev is None: + try: + dev = self.check_output(('blkid', '-L', self.label,)).strip() + except subprocess.CalledProcessError, what: + raise ValueError("cannot find label %s: %s" + % (self.label, str(what),)) + + if self.chroot is not None: + self.hostDir = self.mkdtemp(prefix="mount-", + suffix=".d", + dir=self.chroot + "/tmp") + self.dir = self.hostDir[len(self.chroot):] + else: + self.dir = self.hostDir = self.mkdtemp(prefix="mount-", + suffix=".d") + + if self.fsType is not None: + cmd = ('mount', '-t', self.fsType, dev, self.hostDir,) + else: + cmd = ('mount', dev, self.hostDir,) + self.check_call(cmd, vmode=self.V1) + self.mounted = True + return self + + def __exit__(self, type, value, tb): + + mounted = False + if self.mounted: + p = ProcMountsParser() + for e in p.mounts: + if e.dir == self.hostDir: + mounted = True + break + # really mounted? + # maybe unmounted e.g. if inside a chroot + if mounted: + cmd = ('umount', self.hostDir,) + self.check_call(cmd, vmode=self.V1) + + self.rmdir(self.hostDir) + return False + +class BlkidEntry: + + def __init__(self, device, **kwargs): + + self.device = device + + kwargs = dict(kwargs) + self.label = kwargs.pop('label', None) + self.uuid = kwargs.pop('uuid', None) + self.fsType = kwargs.pop('fsType', None) + + @classmethod + def fromLine(cls, line): + line = line.strip() + p = line.find(':') + if p < 0: + raise ValueError("invalid blkid output %s" + % line) + dev, line = line[:p], line[p+1:].strip() + + attrs = {} + while line: + p = line.find('=') + if p < 0: + raise ValueError("invalid blkid output %s" + % line) + key = line[:p].lower() + if line[p+1:p+2] == "'": + q = line.find("'", p+2) + if q < 0: + val, line = line[p+1:], "" + else: + val, line = line[p+2:q], line[q+1:].strip() + elif line[p+1:p+2] == '"': + q = line.find('"', p+2) + if q < 0: + val, line = line[p+1:], "" + else: + val, line = line[p+2:q], line[q+1:].strip() + else: + q = line.find(" ", p+1) + if q < 0: + val, line = line[p+1:], "" + else: + val, line = line[p+1:], line[:q].strip() + + if key == 'type': key = 'fsType' + attrs[key] = val + + return cls(dev, **attrs) + + def splitDev(self): + dev, part = self.device, "" + while dev[-1:] in string.digits: + dev, part = dev[:-1], dev[-1] + part + return dev, part + + def isOnieReserved(self): + if self.label is None: return False + + if 'GRUB' in self.label: return True + if 'ONIE-BOOT' in self.label: return True + if 'DIAG' in self.label: return True + + return False + +class BlkidParser(SubprocessMixin): + + def __init__(self, log=None): + self.log = log or logging.getLogger("blkid") + self.parse() + + def parse(self): + cmd = ('blkid',) + lines = self.check_output(cmd).splitlines() + self.parts = [BlkidEntry.fromLine(line) for line in lines] + + def __getitem__(self, idxOrName): + if type(idxOrName) == int: + return self.parts[idxOrName] + for part in self.parts: + if part.label == idxOrName: return part + if part.uuid == idxOrName: return part + raise IndexError("cannot find partition %s" % repr(idxOrName)) + + def __len__(self): + return len(self.parts) + +class ProcMtdEntry: + + def __init__(self, + charDevice, blockDevice, + offset, size, eraseSize, + label=None): + + self.charDevice = charDevice + self.blockDevice = blockDevice + self.offset = offset + self.size = size + self.eraseSize = eraseSize + self.label = label + + @classmethod + def fromLine(cls, line, offset=0): + buf = line.strip() + p = buf.find(':') + if p < 0: + raise ValueError("invalid /proc/mtd entry %s" + % line) + dev, buf = buf[:p], buf[p+1:].strip() + dev = '/dev/' + dev + if not os.path.exists(dev): + raise ValueError("invalid /proc/mtd entry %s (missing device)" + % line) + st = os.stat(dev) + if stat.S_ISBLK(st.st_mode): + cdev, bdev = None, dev + elif stat.S_ISCHR(st.st_mode): + cdev, bdev = dev, None + else: + cdev, bdev = None, None + + if cdev and not bdev: + if cdev.startswith("/dev/mtd") and not cdev.startswith("/dev/mtdblock"): + bdev = "/dev/mtdblock" + cdev[8:] + if not os.path.exists(bdev): + raise ValueError("invalid /proc/mtd entry %s (cannot find block device)" + % line) + st = os.stat(bdev) + if not stat.S_ISBLK(st.st_mode): + raise ValueError("invalid /proc/mtd entry %s (cannot find block device)" + % line) + else: + raise ValueError("invalid /proc/mtd entry %s (cannot find block device)" + % line) + elif not bdev: + raise ValueError("invalid /proc/mtd entry %s (not a block or char device)" + % line) + + p = buf.find(" ") + if p < 0: + raise ValueError("invalid /proc/mtd entry %s (missing size)" + % line) + sz, buf = buf[:p], buf[p+1:].strip() + sz = int(sz, 16) + + if not buf: + raise ValueError("invalid /proc/mtd entry %s (missing erase size)" + % line) + p = buf.find(" ") + if p < 0: + esz, buf = buf, "" + else: + esz, buf = buf[:p], buf[p+1:].strip() + esz = int(esz, 16) + + if not buf: + label = None + elif len(buf) > 1 and buf[0:1] == "'" and buf[-1:] == "'": + label = buf[1:-1] + elif len(buf) > 1 and buf[0:1] == '"' and buf[-1:] == '"': + label = buf[1:-1] + else: + label = buf + + return cls(cdev, bdev, offset, sz, esz, label=label) + +class ProcMtdParser(): + + def __init__(self, log=None): + self.log = log or logging.getLogger("blkid") + self.parse() + + def parse(self): + self.parts = [] + offset = 0 + if os.path.exists("/proc/mtd"): + with open("/proc/mtd") as fd: + for line in fd.xreadlines(): + if line.startswith("dev:"): + pass + else: + part = ProcMtdEntry.fromLine(line, offset=offset) + offset += part.size + self.parts.append(part) + + def __getitem__(self, idxOrName): + if type(idxOrName) == int: + return self.parts[idxOrName] + for part in self.parts: + if part.label == idxOrName: return part + raise IndexError("cannot find MTD partition %s" % repr(idxOrName)) + + def __len__(self): + return len(self.parts) + +class PartedDiskEntry: + + def __init__(self, device, blocks, lbsz, pbsz, + model=None, typ=None, flags=[]): + self.device = device + + self.blocks = blocks + self.lbsz = lbsz + self.pbsz = pbsz + + self.model = model + self.typ = typ + self.flags = flags + + @classmethod + def fromLine(cls, line): + + line = line.strip() + if not line.endswith(';'): + raise ValueError("invalid parted line %s" % line) + line = line[:-1] + rec = line.split(':') + + def _s(): + secs = rec.pop(0) + if secs[-1:] != 's': + raise ValueError("invalid sector count %s" % secs) + return int(secs[:-1]) + + dev = rec.pop(0) + blocks = _s() + model = rec.pop(0) or None + lbsz = int(rec.pop(0), 10) + pbsz = int(rec.pop(0), 10) + typ = rec.pop(0) + label = rec.pop(0) or None + flags = rec.pop(0) + flags = [x.strip() for x in flags.split(',')] + + if rec: + raise ValueError("invalid parted line %s" % line) + + return cls(dev, blocks, lbsz, pbsz, + model=model, typ=typ, + flags=flags) + +class PartedPartEntry: + + def __init__(self, part, start, end, sz, + fs=None, label=None, flags=[]): + self.part = part + self.start = start + self.end = end + self.sz = sz + self.fs = fs + self.label = label + self.flags = flags + + @classmethod + def fromLine(cls, line): + + line = line.strip() + if not line.endswith(';'): + raise ValueError("invalid parted line %s" % line) + line = line[:-1] + rec = line.split(':') + + def _s(): + secs = rec.pop(0) + if secs[-1:] != 's': + raise ValueError("invalid sector count %s" % secs) + return int(secs[:-1]) + + part = int(rec.pop(0), 10) + if part < 1: + raise ValueError("invalid partition %d" % part) + start = _s() + end = _s() + sz = _s() + fs = rec.pop(0) or None + label = rec.pop(0) or None + flags = rec.pop(0) + flags = [x.strip() for x in flags.split(',')] + + if rec: + raise ValueError("invalid parted line %s" % line) + + return cls(part, start, end, sz, + fs=fs, label=label, + flags=flags) + +class PartedParser(SubprocessMixin): + + def __init__(self, device, log=None): + self.device = device + self.log = log or logging.getLogger("parted") + self.parse() + + def parse(self): + + cmd = ('parted', '-m', self.device, + 'unit', 's', + 'print',) + lines = self.check_output(cmd).splitlines() + self.disk = None + parts = {} + for line in lines: + if line.startswith('/dev/'): + self.disk = PartedDiskEntry.fromLine(line) + elif line[0:1] in string.digits: + ent = PartedPartEntry.fromLine(line) + if ent.part in parts: + raise ValueError("duplicate partition") + parts[ent.part] = ent + + self.parts = [] + for partno in sorted(parts.keys()): + self.parts.append(parts[partno]) + + if self.disk is None: + raise ValueError("no partition table found") + + def __len__(self): + return len(self.parts) + +class ProcMountsEntry: + + def __init__(self, device, dir, fsType, flags={}): + self.device = device + self.dir = dir + self.fsType = fsType + self.flags = flags + + @classmethod + def fromLine(cls, line): + buf = line.strip() + + idx = buf.find(' ') + if idx < 0: + raise ValueError("invalid /proc/mounts line %s", line) + + device, buf = buf[:idx], buf[idx+1:].strip() + + idx = buf.find(' ') + if idx < 0: + raise ValueError("invalid /proc/mounts line %s", line) + + dir, buf = buf[:idx], buf[idx+1:].strip() + + idx = buf.find(' ') + if idx < 0: + raise ValueError("invalid /proc/mounts line %s", line) + + fsType, buf = buf[:idx], buf[idx+1:].strip() + + idx = buf.rfind(' ') + if idx < 0: + raise ValueError("invalid /proc/mounts line %s", line) + + buf, _ = buf[:idx], buf[idx+1:].strip() + + idx = buf.rfind(' ') + if idx < 0: + buf = "" + else: + buf, _ = buf[:idx], buf[idx+1:].strip() + + flags = {} + if buf: + for flag in buf.split(','): + idx = flag.find('=') + if idx > -1: + key, val = flag[:idx], flag[idx+1:] + else: + key, val = flag, True + flags[key] = val + + return cls(device, dir, fsType, flags) + +class ProcMountsParser: + + def __init__(self): + self.parse() + + def parse(self): + self.mounts = [] + with open("/proc/mounts") as fd: + for line in fd.readlines(): + self.mounts.append(ProcMountsEntry.fromLine(line)) + +class InitrdContext(SubprocessMixin): + + def __init__(self, initrd=None, dir=None, log=None): + if initrd is None and dir is None: + raise ValueError("missing initrd or initrd dir") + if initrd and dir: + raise ValueError("cannot specify initrd and initrd dir") + self.initrd = initrd + self.dir = dir + self.hlog = log or logging.getLogger("mount") + self.ilog = self.hlog.getChild("initrd") + self.ilog.setLevel(logging.INFO) + self.log = self.hlog + + def _unpack(self): + self.dir = self.mkdtemp(prefix="chroot-", + suffix=".d") + with open(self.initrd) as fd: + mbuf = fd.read(1024) + if mbuf[0:2] == "\x1f\x8b": + c1 = ('gzip', '-dc', self.initrd,) + elif mbuf[0:2] == "BZ": + c1 = ('bzip2', '-dc', self.initrd,) + elif mbuf[0:6] == "\xfd7zXZ\x00": + c1 = ('xz', '-dc', self.initrd,) + else: + raise ValueError("cannot decode initrd") + c2 = ('cpio', '-imd',) + self.log.debug("+ %s | %s", + " ".join(c1), " ".join(c2)) + try: + p1 = subprocess.Popen(c1, + stdout=subprocess.PIPE) + except OSError as ex: + self.log.exception("command not found: %s" % c1[0]) + raise ValueError("cannot start pipe") + try: + if self.log.isEnabledFor(logging.DEBUG): + p2 = subprocess.Popen(c2, + cwd=self.dir, + stdin=p1.stdout) + else: + p2 = subprocess.Popen(c2, + cwd=self.dir, + stdin=p1.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + except OSError as ex: + self.log.exception("cannot start command: %s" % c2[0]) + raise ValueError("cannot start pipe") + c1 = p1.wait() + out, _ = p2.communicate() + c2 = p2.wait() + if c2 and out: + sys.stderr.write(out) + if c1 or c2: + raise ValueError("initrd unpack failed") + + def _prepDirs(self): + + dev2 = os.path.join(self.dir, "dev") + if not os.path.exists(dev2): + self.mkdir(dev2) + + for e in os.listdir(dev2): + dst = os.path.join(dev2, e) + if os.path.islink(dst): + self.unlink(dst) + elif os.path.isdir(dst): + self.rmtree(dst) + else: + self.unlink(dst) + + for e in os.listdir("/dev"): + src = os.path.join("/dev", e) + dst = os.path.join(dev2, e) + if os.path.islink(src): + self.symlink(os.readlink(src), dst) + elif os.path.isdir(src): + self.mkdir(dst) + elif os.path.isfile(src): + self.copy2(src, dst) + else: + st = os.stat(src) + if stat.S_ISBLK(st.st_mode): + maj, min = os.major(st.st_rdev), os.minor(st.st_rdev) + self.log.debug("+ mknod %s b %d %d", dst, maj, min) + os.mknod(dst, st.st_mode, st.st_rdev) + elif stat.S_ISCHR(st.st_mode): + maj, min = os.major(st.st_rdev), os.minor(st.st_rdev) + self.log.debug("+ mknod %s c %d %d", dst, maj, min) + os.mknod(dst, st.st_mode, st.st_rdev) + else: + self.log.debug("skipping device %s", src) + + dst = os.path.join(self.dir, "dev/pts") + if not os.path.exists(dst): + self.mkdir(dst) + + if 'TMPDIR' in os.environ: + dst = self.dir + os.environ['TMPDIR'] + if not os.path.exists(dst): + self.makedirs(dst) + + def __enter__(self): + + if self.initrd is not None: + + self.log.debug("extracting initrd %s", self.initrd) + self._unpack() + + self.log.debug("preparing chroot in %s", self.dir) + try: + self.log = self.ilog + self._prepDirs() + finally: + self.log = self.hlog + + dst = os.path.join(self.dir, "proc") + cmd = ('mount', '-t', 'proc', 'proc', dst,) + self.check_call(cmd, vmode=self.V1) + + dst = os.path.join(self.dir, "sys") + cmd = ('mount', '-t', 'sysfs', 'sysfs', dst,) + self.check_call(cmd, vmode=self.V1) + + dst = os.path.join(self.dir, "dev/pts") + cmd = ('mount', '-t', 'devpts', 'devpts', dst,) + self.check_call(cmd, vmode=self.V1) + + return self + + def __exit__(self, type, value, tb): + + p = ProcMountsParser() + dirs = [e.dir for e in p.mounts if e.dir.startswith(self.dir)] + + # XXX probabaly also kill files here + + # umount any nested mounts + self.log.debug("un-mounting mounts points in chroot %s", self.dir) + dirs.sort(reverse=True) + for p in dirs: + cmd = ('umount', p,) + self.check_call(cmd, vmode=self.V1) + + if self.initrd is not None: + self.log.debug("cleaning up chroot in %s", self.dir) + self.rmtree(self.dir) + else: + self.log.debug("saving chroot in %s", self.dir) + + return False + + @classmethod + def mkChroot(self, initrd, log=None): + with InitrdContext(initrd=initrd, log=log) as ctx: + initrdDir = ctx.dir + ctx.initrd = None + # save the unpacked directory, do not clean it up + # (it's inside this chroot anyway) + return initrdDir + +class ChrootSubprocessMixin: + + chrootDir = None + mounted = False + # initialize this in a concrete class + + def check_call(self, *args, **kwargs): + args = list(args) + kwargs = dict(kwargs) + + cwd = kwargs.pop('cwd', None) + if cwd is not None: + self.log.debug("+ cd " + cwd) + + if args: + cmd = args.pop(0) + else: + cmd = kwargs.pop('cmd') + if isinstance(cmd, basestring): + cmd = ('chroot', self.chrootDir, + '/bin/sh', '-c', 'IFS=;' + cmd,) + else: + cmd = ['chroot', self.chrootDir,] + list(cmd) + + if not self.mounted: + with InitrdContext(dir=self.chrootDir, log=self.log) as ctx: + self.log.debug("+ " + " ".join(cmd)) + subprocess.check_call(cmd, *args, cwd=cwd, **kwargs) + else: + self.log.debug("+ " + " ".join(cmd)) + subprocess.check_call(cmd, *args, cwd=cwd, **kwargs) + + def check_output(self, *args, **kwargs): + args = list(args) + kwargs = dict(kwargs) + + cwd = kwargs.pop('cwd', None) + if cwd is not None: + self.log.debug("+ cd " + cwd) + + if args: + cmd = args.pop(0) + else: + cmd = kwargs.pop('cmd') + if isinstance(cmd, basestring): + cmd = ('chroot', self.chrootDir, + '/bin/sh', '-c', 'IFS=;' + cmd,) + else: + cmd = ['chroot', self.chrootDir,] + list(cmd) + + if not self.mounted: + with InitrdContext(self.chrootDir, log=self.log) as ctx: + self.log.debug("+ " + " ".join(cmd)) + return subprocess.check_output(cmd, *args, cwd=cwd, **kwargs) + else: + self.log.debug("+ " + " ".join(cmd)) + return subprocess.check_output(cmd, *args, cwd=cwd, **kwargs) diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/RecoverApp.py b/packages/base/all/vendor-config-onl/src/python/onl/install/RecoverApp.py new file mode 100644 index 00000000..2a4abf4b --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/RecoverApp.py @@ -0,0 +1,81 @@ +"""RecoverApp.py + +Application-level code for Switch Light recovery. +""" + +import os, sys +import imp +import logging +from ConfUtils import UbootEnv + +class App: + + def __init__(self, log=None): + + if log is not None: + self.log = log + else: + self.log = logging.getLogger(self.__class__.__name__) + + self.recovery = None + + def run(self): + + if os.path.exists(UbootEnv.SETENV): + self.ubootEnv = UbootEnv(log=self.log.getChild("u-boot")) + else: + self.ubootEnv = None + + # load the platform-specific blob + if os.path.exists("/etc/onl/platform"): + with open("/etc/onl/platform") as fd: + plat = fd.read().strip() + else: + self.log.error("cannot recover non-ONL platform") + return 1 + + p = ("/lib/platform-config/%s/python/recover.py" + % (plat,)) + if not os.path.exists(p): + self.log.error("missing recover profile %s", p) + return 1 + mod = imp.load_source("platform_recover", p) + + # run the platform-specific installer + self.recovery = mod.Recovery(ubootEnv=self.ubootEnv, + log=self.log) + try: + code = self.recovery.run() + except: + self.log.exception("recovery failed") + code = 1 + if code: return code + + return 0 + + def shutdown(self): + + recovery, self.recovery = self.recovery, None + if recovery is not None: + recovery.shutdown() + + @classmethod + def main(cls): + + logging.basicConfig() + logger = logging.getLogger("recover") + logger.setLevel(logging.DEBUG) + + app = cls(log=logger) + try: + code = app.run() + except: + logger.exception("runner failed") + code = 1 + app.shutdown() + sys.exit(code) + +main = App.main + +if __name__ == "__main__": + main() diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/ShellApp.py b/packages/base/all/vendor-config-onl/src/python/onl/install/ShellApp.py new file mode 100644 index 00000000..c0ff2fa2 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/ShellApp.py @@ -0,0 +1,303 @@ +"""ShellApp.py +""" + +import os, sys +import glob +import tempfile +import logging +import subprocess +import argparse +import string +import struct +from InstallUtils import InitrdContext, MountContext +from InstallUtils import SubprocessMixin +from InstallUtils import ProcMountsParser, ProcMtdParser +from InstallUtils import BlkidParser +import Fit + +import onl.platform.current + +class AppBase(SubprocessMixin): + + @property + def PROG(self): + raise NotImplementedError + + def __init__(self, command=None, log=None): + + if log is not None: + self.log = log + else: + self.log = logging.getLogger(self.__class__.__name__) + self.command = command + + def _runInitrdShell(self, initrd): + with InitrdContext(initrd=initrd, log=self.log) as ctx: + if self.command is not None: + cmd = ('chroot', ctx.dir, + '/bin/sh', '-c', 'IFS=;' + self.command) + else: + cmd = ('chroot', ctx.dir, + '/bin/sh', '-i') + try: + self.check_call(cmd) + except subprocess.CalledProcessError, what: + pass + return 0 + + def _runFitShell(self, device): + self.log.debug("parsing FIT image in %s", device) + p = Fit.Parser(path=device, log=self.log) + node = p.getInitrdNode() + if node is None: + self.log.error("cannot find initrd node in FDT") + return 1 + prop = node.properties.get('data', None) + if prop is None: + self.log.error("cannot find initrd data property in FDT") + return 1 + with open(device) as fd: + self.log.debug("reading initrd at [%x:%x]", + prop.offset, prop.offset+prop.sz) + fd.seek(prop.offset, 0) + buf = fd.read(prop.sz) + try: + fno, initrd = tempfile.mkstemp(prefix="initrd-", + suffix=".img") + self.log.debug("+ cat > %s", initrd) + with os.fdopen(fno, "w") as fd: + fd.write(buf) + return self._runInitrdShell(initrd) + finally: + self.unlink(initrd) + + def shutdown(self): + pass + + @classmethod + def main(cls): + + logging.basicConfig() + logger = logging.getLogger(cls.PROG) + logger.setLevel(logging.INFO) + + ap = argparse.ArgumentParser(prog=cls.PROG) + ap.add_argument('-v', '--verbose', action='store_true', + help='Enable verbose logging') + ap.add_argument('-q', '--quiet', action='store_true', + help='Suppress logging') + ap.add_argument('-c', type=str, dest='command', + help='Run a batch command') + + try: + args = ap.parse_args() + except SystemExit, what: + sys.exit(what.code) + + if args.verbose: + logger.setLevel(logging.DEBUG) + if args.quiet: + logger.setLevel(logging.ERROR) + + app = cls(command=args.command, log=logger) + try: + code = app.run() + except: + logger.exception("runner failed") + code = 1 + app.shutdown() + sys.exit(code) + +class Onie(AppBase): + + PROG = "onie-shell" + + def run(self): + + self.pm = ProcMountsParser() + self.blkid = BlkidParser(log=self.log.getChild("blkid")) + self.mtd = ProcMtdParser(log=self.log.getChild("mtd")) + + def _g(d): + pat = os.path.join(d, "onie/initrd.img*") + l = glob.glob(pat) + if l: return l[0] + return None + + # try to find a mounted, labeled partition + try: + dev = self.blkid['ONIE-BOOT'].device + except IndexError: + dev = None + if dev is not None: + self.log.debug("found ONIE boot device %s", dev) + + parts = [p for p in self.pm.mounts if p.device == dev] + if parts: + onieDir = parts[0] + self.log.debug("found ONIE boot mounted at %s", onieDir) + initrd = _g(onieDir) + if initrd is None: + self.log.warn("cannot find ONIE initrd on %s", onieDir) + else: + self.log.debug("found ONIE initrd at %s", initrd) + return _runInitrdShell(initrd) + + with MountContext(dev, log=self.log) as ctx: + initrd = _g(ctx.dir) + if initrd is None: + self.log.warn("cannot find ONIE initrd on %s", dev) + else: + self.log.debug("found ONIE initrd at %s", initrd) + return self._runInitrdShell(initrd) + + self.log.warn("cannot find an ONIE initrd") + return 1 + + # try to find onie initrd on a mounted fs (GRUB); + # for ONIE images this is usually /mnt/onie-boot + for part in self.pm.mounts: + if not part.device.startswith('/dev/'): continue + initrd = _g(part.dir) + if initrd is None: + self.log.debug("cannot find ONIE initrd on %s (%s)", + part.device, part.dir) + else: + self.log.debug("found ONIE initrd at %s", initrd) + return self._runInitrdShell(initrd) + + # grovel through MTD devices (u-boot) + parts = [p for p in self.mtd.parts if p.label == "onie"] + if parts: + part = parts[0] + self.log.debug("found ONIE MTD device %s", + part.charDevice or part.blockDevice) + return self._runFitShell(part.blockDevice) + elif self.mtd.mounts: + self.log.error("cannot find ONIE MTD device") + return 1 + + self.log.error("cannot find ONIE initrd") + return 1 + +class Loader(AppBase): + + PROG = "loader-shell" + + def runGrub(self): + + try: + dev = self.blkid['ONL-BOOT'].device + except KeyError: + pass + if dev is None: + self.log.error("cannot find GRUB partition %s", dev) + return 1 + + initrd = self.pc['grub']['initrd'] + if type(initrd) == dict: initrd = initrd['='] + + parts = [p for p in self.pm.mounts if p.device == dev] + if parts: + grubDir = parts[0] + self.log.debug("found loader device %s mounted at %s", + dev, grubDir) + p = os.path.join(grubDir, initrd) + if not os.path.exists(p): + self.log.error("cannot find initrd %s", p) + return 1 + self.log.debug("found loader initrd at %s", p) + return self._runInitrdShell(p) + + with MountContext(dev, log=self.log) as ctx: + p = os.path.join(ctx.dir, initrd) + if not os.path.exists(p): + self.log.error("cannot find initrd %s:%s", dev, p) + return 1 + self.log.debug("found loader initrd at %s:%s", dev, p) + return self._runInitrdShell(p) + + def runUboot(self): + + dev = self.pc['loader']['device'] + self.log.info("found loader device %s", dev) + + parts = self.pc['installer'] + bootPart = None + bootPartno = None + for idx, part in enumerate(self.pc['installer']): + label, pdata = list(part.items())[0] + if label == 'ONL-BOOT': + bootPart = pdata + bootPartno = idx + 1 + break + if bootPart is None: + self.log.info("cannot find ONL-BOOT declaration") + return 1 + + fmt = bootPart.get('format', 'ext2') + if fmt == 'raw': + bootDevice = dev + str(bootPartno) + else: + bootDevice = self.blkid['ONL-BOOT'].device + + # run from a raw partition + if fmt == 'raw': + self.log.info("found (raw) boot partition %s", bootDevice) + return self._runFitShell(bootDevice) + + l = [] + + p = self.pc['flat_image_tree']['itb'] + if type(p) == dict: p = p['='] + if p not in l: l.append(p) + + p = self.platform.platform() + '.itb' + if p not in l: l.append(p) + + p = 'onl-loader-fit.itb' + if p not in l: l.append(p) + + self.log.info("looking for loader images %s", ", ".join(l)) + + # run from a file in a mounted filesystem + parts = [p for p in self.pm.mounts if p.device == bootDevice] + if parts: + loaderDir = parts[0] + self.log.debug("found loader device mounted at %s", loaderDir) + for e in l: + p = os.path.join(loaderDir, e) + if os.path.exists(p): return self._runFitShell(p) + self.log.error("cannot find an ITB") + return 1 + + # run from a file in an umounted filesystem + with MountContext(bootDevice, log=self.log) as ctx: + self.log.info("found (%s) loader device %s", fmt, bootDevice) + for e in l: + p = os.path.join(ctx.dir, e) + if os.path.exists(p): return self._runFitShell(p) + self.log.error("cannot find an ITB") + return 1 + + def run(self): + + self.platform = onl.platform.current.OnlPlatform() + self.pc = self.platform.platform_config + + self.pm = ProcMountsParser() + self.blkid = BlkidParser(log=self.log.getChild("blkid")) + + if 'grub' in self.pc: + return self.runGrub() + + if 'flat_image_tree' in self.pc: + return self.runUboot() + + self.log.error("invalid platform-config") + return 1 + +main = Onie.main + +if __name__ == "__main__": + main() diff --git a/packages/base/all/vendor-config-onl/src/python/onl/install/__init__.py b/packages/base/all/vendor-config-onl/src/python/onl/install/__init__.py new file mode 100644 index 00000000..584287a7 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/install/__init__.py @@ -0,0 +1,4 @@ +"""__init__.py + +Module setup for switchlight.install +""" diff --git a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py index 8bf9a2a2..874782a4 100644 --- a/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py +++ b/packages/base/all/vendor-config-onl/src/python/onl/platform/base.py @@ -10,11 +10,13 @@ ############################################################ import pprint -import yaml import json import os import re +import yaml +import onl.YamlUtils + class OnlInfoObject(object): DEFAULT_INDENT=" " @@ -99,18 +101,41 @@ class OnlPlatformBase(object): CONFIG_DIR='/lib/platform-config' CURRENT_DIR=os.path.join(CONFIG_DIR, 'current') + CONFIG_DEFAULT_GRUB = "/lib/vendor-config/onl/platform-config-defaults-x86-64.yml" + CONFIG_DEFAULT_UBOOT = "/lib/vendor-config/onl/platform-config-defaults-uboot.yml" + def __init__(self): self.add_info_json("onie_info", "%s/onie-info.json" % self.basedir_onl(), OnieInfo, required=False) self.add_info_json("platform_info", "%s/platform-info.json" % self.basedir_onl(), required=False) - # Load the platform config yaml file - y = os.path.join(self.basedir_onl(), "%s.yml" % self.platform()) - if os.path.exists(y): - self.platform_config = yaml.load(open(y)) + # Find the base platform config + if self.platform().startswith('x86-64'): + y1 = self.CONFIG_DEFAULT_GRUB + elif self.platform().startswith('powerpc'): + y1 = self.CONFIG_DEFAULT_UBOOT + elif self.platform().startswith('arm'): + y1 = self.CONFIG_DEFAULT_UBOOT + else: + y1 = None + + # Find and load the platform config yaml file + y2 = os.path.join(self.basedir_onl(), "%s.yml" % self.platform()) + if os.path.exists(y1) and os.path.exists(y2): + self.platform_config = onl.YamlUtils.merge(y1, y2) if self.platform() in self.platform_config: self.platform_config = self.platform_config[self.platform()] + elif os.path.exists(y2): + with open(y2) as fd: + self.platform_config = yaml.load(fd) + if self.platform() in self.platform_config: + self.platform_config = self.platform_config[self.platform()] + elif os.path.exists(y1): + with open(y1) as fd: + self.platform_config = yaml.load(fd) + if 'default' in self.platform_config: + self.platform_config = self.platform_config['default'] else: self.platform_config = {} diff --git a/packages/base/all/vendor-config-onl/src/python/onl/platform/current.py b/packages/base/all/vendor-config-onl/src/python/onl/platform/current.py index 68606154..545fac04 100644 --- a/packages/base/all/vendor-config-onl/src/python/onl/platform/current.py +++ b/packages/base/all/vendor-config-onl/src/python/onl/platform/current.py @@ -14,12 +14,23 @@ # platform-config packages. # ############################################################ +import os import importlib def import_subsystem_platform_class(subsystem='onl', klass='OnlPlatform'): # Determine the current platform name. - with open("/etc/onl/platform", 'r') as f: - platform=f.read().strip() + platform = None + if os.path.exists("/etc/onl/platform"): + with open("/etc/onl/platform", 'r') as f: + platform=f.read().strip() + elif os.path.exists("/etc/machine.conf"): + with open("/etc/machine.conf", 'r') as f: + lines = f.readlines(False) + lines = [x for x in lines if x.startswith('onie_platform=')] + if lines: + platform = lines[0].partition('=')[2].strip() + if platform is None: + raise RuntimeError("cannot find a platform declaration") platform_module = platform.replace('-', '_') diff --git a/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.Config.in b/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.Config.in deleted file mode 100644 index 6207230a..00000000 --- a/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.Config.in +++ /dev/null @@ -1,6 +0,0 @@ -config BR2_PACKAGE_PYTHON_PYBLKID - bool "python-pyblkid" - depends on BR2_PACKAGE_PYTHON - depends on BR2_PACKAGE_UTIL_LINUX - help - Include the 'pyblkid' Python library diff --git a/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.mk b/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.mk deleted file mode 100644 index 8299426b..00000000 --- a/packages/base/any/initrds/buildroot/builds/patches/python-pyblkid.mk +++ /dev/null @@ -1,41 +0,0 @@ -###################################################################### -## -## python-pyblkid.mk -## -###################################################################### - -PYTHON_PYBLKID_VERSION = 0.0.1 -PYTHON_PYBLKID_SOURCE = pyblkid-$(PYTHON_PYBLKID_VERSION).tar.bz2 -PYTHON_PYBLKID_INSTALL_STAGING = NO -PYTHON_PYBLKID_INSTALL_TARGET = YES -PYTHON_PYBLKID_LICENSE = GPL -PYTHON_PYBLKID_LICENSE_FILES = COPYING - -PYTHON_PYBLKID_DEPENDENCIES = python util-linux - -PYTHON_PYBLKID_INCLUDES = \ - --include-dirs $(STAGING_DIR)/usr/include:$(STAGING_DIR)/usr/include/python$(PYTHON_VERSION_MAJOR) \ - # THIS LINE INTENTIONALLY LEFT BLANK - -PYTHON_PYBLKID_LIBDIRS = \ - --library-dirs $(STAGING_DIR)/usr/lib \ - # THIS LINE INTENTIONALLY LEFT BLANK - -# see python-mad.mk -PYTHON_PYBLKID_ENVIRONMENT = \ - CC="$(TARGET_CC)" \ - CFLAGS="$(TARGET_CFLAGS)" \ - LDSHARED="$(TARGET_CC) -shared" \ - LDFLAGS="$(TARGET_LDFLAGS)" \ - # THIS LINE INTENTIONALLY LEFT BLANK - -define PYTHON_PYBLKID_BUILD_CMDS - (cd $(@D); $(HOST_DIR)/usr/bin/python setup.py build_py) - (cd $(@D); $(PYTHON_PYBLKID_ENVIRONMENT) $(HOST_DIR)/usr/bin/python setup.py build_ext $(PYTHON_PYBLKID_INCLUDES) $(PYTHON_PYBLKID_LIBDIRS)) -endef - -define PYTHON_PYBLKID_INSTALL_TARGET_CMDS - (cd $(@D); $(HOST_DIR)/usr/bin/python setup.py install --prefix=$(TARGET_DIR)/usr --install-scripts=$(TARGET_DIR)/usr/bin) -endef - -$(eval $(generic-package)) diff --git a/packages/base/any/initrds/loader/builds/Makefile b/packages/base/any/initrds/loader/builds/Makefile index 50eea529..b117a086 100644 --- a/packages/base/any/initrds/loader/builds/Makefile +++ b/packages/base/any/initrds/loader/builds/Makefile @@ -13,6 +13,7 @@ PLATFORMS := $(shell onlpm --list-platforms --arch $(ARCH)) endif PLATFORM_PACKAGES := $(foreach p,$(PLATFORMS),onl-platform-config-$(p):$(ARCH)) +VENDOR_PACKAGES := $(foreach p,$(PLATFORMS),$(shell python $(ONL)/tools/onlplatform.py $(p) $(ARCH) vendor)) ROOT := root TARGET := onl-loader-initrd-$(ARCH).cpio.gz @@ -24,13 +25,22 @@ $(TARGET): $(ONLPM) --sudo --force --extract-dir onl-loader-initrd-files:all $(ROOT) $(ONLPM) --sudo --force --extract-dir onl-vendor-config-onl-loader:all $(ROOT) $(ONLPM) --sudo $(foreach p,$(PLATFORM_PACKAGES),--extract-dir $(p) $(ROOT)) + $(MAKE) __vendor_config_data + $(ONLPM) --sudo --force --extract-dir onl-vendor-config-onl:all $(ROOT) $(ONL)/tools/sjson.py --kj version $(ONL)/make/version-onl.json --kl platforms $(PLATFORMS) --kv arch $(ARCH) --out manifest.json sudo mkdir -p $(ROOT)/etc/onl/loader && sudo cp manifest.json $(ROOT)/etc/onl/loader sudo $(ONL)/tools/makedevs -d $(ROOT)/etc/rootperms $(abspath $(ROOT)) sudo $(ONL)/tools/cpiomod.py --cpio onl-buildroot-initrd-$(ARCH).cpio.gz --add-directory $(ROOT) --out $@ sudo rm -rf $(ROOT) onl-buildroot-initrd-$(ARCH).cpio.gz - - - - +__vendor_config_data: + set -e ;\ + vpkgs= ;\ + l="$(PLATFORMS)"; for p in $$l; do \ + vpkg=$$(python $(ONL)/tools/onlplatform.py $$p $(ARCH) vendor) ;\ + case " $$vpkgs " in *" $$vpkg "*) continue ;; esac ;\ + vpkgs=$$vpkgs$${vpkgs:+" "}$$vpkg ;\ + echo "Adding vendor package $$vpkg" ;\ + $(ONLPM) --sudo --force --extract-dir $$vpkg $(ROOT) ;\ + done ;\ + : diff --git a/packages/platforms/accton/armel/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml b/packages/platforms/accton/armel/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml index b6df1308..010c3b7d 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml +++ b/packages/platforms/accton/armel/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml @@ -1,7 +1,23 @@ +--- + +###################################################################### +# +# platform-config for AS4610 +# +###################################################################### + arm-accton-as4610-54-r0: flat_image_tree: - kernel: onl-kernel-3.2-deb7-arm-iproc-all:armel, kernel-3.2-deb7-arm-iproc-all.bin.gz - dtb: onl-kernel-3.2-deb7-arm-iproc-all:armel, accton_as4610_54.dtb + kernel: + <<: *arm-iproc-kernel + dtb: + =: accton_as4610_54.dtb + <<: *arm-iproc-kernel-package + itb: + <<: *arm-itb loader: - partition: /dev/sda1 \ No newline at end of file + device: /dev/sda + ##partition: /dev/sda1 + loadaddr: 0x70000000 + nos_bootcmds: *usb2_bootcmds diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as4600-54t/platform-config/r0/src/lib/powerpc-accton-as4600-54t-r0.yml b/packages/platforms/accton/powerpc/powerpc-accton-as4600-54t/platform-config/r0/src/lib/powerpc-accton-as4600-54t-r0.yml index 4e178c5e..b147cb47 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as4600-54t/platform-config/r0/src/lib/powerpc-accton-as4600-54t-r0.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as4600-54t/platform-config/r0/src/lib/powerpc-accton-as4600-54t-r0.yml @@ -1,7 +1,34 @@ +--- + +###################################################################### +# +# platform-config for AS4600 +# +###################################################################### + powerpc-accton-as4600-54t-r0: flat_image_tree: - kernel: onl-kernel-3.9.6-powerpc-e500v:powerpc, kernel-3.9.6-powerpc-e500v.bin.gz - dtb: onl-kernel-3.9.6-powerpc-e500v:powerpc, powerpc-as4600-54t.dtb + kernel: + <<: *e500v-kernel + dtb: + =: powerpc-as4600-54t.dtb + <<: *e500v-kernel-package loader: - partition: /dev/sda1 + device: /dev/sda + ##partition: /dev/sda1 + nos_bootcmds: *usb_bootcmds + + installer: + - ONL-BOOT: + =: 32MiB + format: ext2 + - ONL-CONFIG: + =: 32MiB + format: ext4 + - ONL-IMAGES: + =: 448MiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as5610-52x/platform-config/r0/src/lib/powerpc-accton-as5610-52x-r0.yml b/packages/platforms/accton/powerpc/powerpc-accton-as5610-52x/platform-config/r0/src/lib/powerpc-accton-as5610-52x-r0.yml index 011674d7..a02ae482 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as5610-52x/platform-config/r0/src/lib/powerpc-accton-as5610-52x-r0.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as5610-52x/platform-config/r0/src/lib/powerpc-accton-as5610-52x-r0.yml @@ -1,7 +1,35 @@ +--- + +###################################################################### +# +# platform-config for AS5610 +# +###################################################################### + powerpc-accton-as5610-52x-r0: + flat_image_tree: - kernel: onl-kernel-3.9.6-powerpc-e500v:powerpc, kernel-3.9.6-powerpc-e500v.bin.gz - dtb: onl-kernel-3.9.6-powerpc-e500v:powerpc, powerpc-as5610-52x.dtb + kernel: + <<: *e500v-kernel + dtb: + =: powerpc-as5610-52x.dtb + <<: *e500v-kernel-package loader: - partition: /dev/sda1 \ No newline at end of file + device: /dev/sda + ##partition: /dev/sda1 + nos_bootcmds: *usb_bootcmds + + installer: + - ONL-BOOT: + =: 128MiB + format: ext2 + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 768MiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0/src/lib/powerpc-accton-as5710-54x-r0.yml b/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0/src/lib/powerpc-accton-as5710-54x-r0.yml index 2e3bde00..91c166c8 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0/src/lib/powerpc-accton-as5710-54x-r0.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0/src/lib/powerpc-accton-as5710-54x-r0.yml @@ -1,9 +1,35 @@ +--- + +###################################################################### +# +# platform-config for AS5710 +# +###################################################################### + powerpc-accton-as5710-54x-r0: + flat_image_tree: - kernel: onl-kernel-3.8.13-powerpc-e500mc:powerpc, kernel-3.8.13-powerpc-e500mc.bin.gz - dtb: onl-kernel-3.8.13-powerpc-e500mc:powerpc, powerpc-accton-as5710-54x-r0.dtb + kernel: + <<: *e500mc-kernel + dtb: + =: powerpc-accton-as5710-54x-r0.dtb + <<: *e500mc-kernel-package loader: - partition: /dev/sda1 - raw: True + device: /dev/sda + nos_bootcmds: *usb_bootcmds + installer: + - ONL-BOOT: + =: 128MiB + format: ext2 + ##format: raw + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 1GiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0b/src/lib/powerpc-accton-as5710-54x-r0b.yml b/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0b/src/lib/powerpc-accton-as5710-54x-r0b.yml index 1ceabcfe..72b71b5d 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0b/src/lib/powerpc-accton-as5710-54x-r0b.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as5710-54x/platform-config/r0b/src/lib/powerpc-accton-as5710-54x-r0b.yml @@ -1,9 +1,21 @@ +--- + +###################################################################### +# +# platform-config for as5710 +# +###################################################################### + powerpc-accton-as5710-54x-r0b: + flat_image_tree: - kernel: onl-kernel-3.8.13-powerpc-e500mc:powerpc, kernel-3.8.13-powerpc-e500mc.bin.gz - dtb: onl-kernel-3.8.13-powerpc-e500mc:powerpc, powerpc-accton-as5710-54x-r0b.dtb + kernel: + <<: *e500mc-kernel + dtb: + =: powerpc-accton-as5710-54x-r0b.dtb + <<: *e500mc-kernel-package loader: - partition: /dev/sda1 - raw: True + device: /dev/sda + nos_bootcmds: *usb_bootcmds diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r0/src/lib/powerpc-accton-as6700-32x-r0.yml b/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r0/src/lib/powerpc-accton-as6700-32x-r0.yml index a5287fe1..58f15076 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r0/src/lib/powerpc-accton-as6700-32x-r0.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r0/src/lib/powerpc-accton-as6700-32x-r0.yml @@ -1,8 +1,36 @@ +--- + +###################################################################### +# +# platform-config for AS6700 +# +###################################################################### + powerpc-accton-as6700-32x-r0: + flat_image_tree: - kernel: onl-kernel-3.8.13-powerpc-e500mc:powerpc, kernel-3.8.13-powerpc-e500mc.bin.gz - dtb: onl-kernel-3.8.13-powerpc-e500mc:powerpc, powerpc-accton-as6700-32x-r0.dtb + kernel: + <<: *e500mc-kernel + dtb: + =: powerpc-accton-as6700-32x-r0.dtb + <<: *e500mc-kernel-package loader: - partition: /dev/sda1 + device: /dev/sda + ##partition: /dev/sda1 + nos_bootcmds: *usb_bootcmds + + installer: + - ONL-BOOT: + =: 128MiB + format: ext2 + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 768MiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r1/src/lib/powerpc-accton-as6700-32x-r1.yml b/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r1/src/lib/powerpc-accton-as6700-32x-r1.yml index dbb1187d..f35a9391 100644 --- a/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r1/src/lib/powerpc-accton-as6700-32x-r1.yml +++ b/packages/platforms/accton/powerpc/powerpc-accton-as6700-32x/platform-config/r1/src/lib/powerpc-accton-as6700-32x-r1.yml @@ -1,8 +1,36 @@ +--- + +###################################################################### +# +# platform-config for AS6700 +# +###################################################################### + powerpc-accton-as6700-32x-r1: + flat_image_tree: - kernel: onl-kernel-3.8.13-powerpc-e500mc:powerpc, kernel-3.8.13-powerpc-e500mc.bin.gz - dtb: onl-kernel-3.8.13-powerpc-e500mc:powerpc, powerpc-accton-as6700-32x-r1.dtb + kernel: + <<: *e500mc-kernel + dtb: + =: powerpc-accton-as6700-32x-r1.dtb + <<: *e500mc-kernel-package loader: - partition: /dev/sda1 + device: /dev/sda + ##partition: /dev/sda1 + nos_bootcmds: *usb_bootcmds + + installer: + - ONL-BOOT: + =: 128MiB + format: ext2 + - ONL-CONFIG: + =: 128MiB + format: ext4 + - ONL-IMAGES: + =: 768MiB + format: ext4 + - ONL-DATA: + =: 100% + format: ext4 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5712-54x/platform-config/r0/src/lib/x86-64-accton-as5712-54x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as5712-54x/platform-config/r0/src/lib/x86-64-accton-as5712-54x-r0.yml new file mode 100644 index 00000000..7820c773 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5712-54x/platform-config/r0/src/lib/x86-64-accton-as5712-54x-r0.yml @@ -0,0 +1,27 @@ +--- + +###################################################################### +# +# platform-config for AS5712 +# +###################################################################### + +x86-64-accton-as5712-54x-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 + + \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/platform-config/r0/src/lib/x86-64-accton-as5812-54t-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/platform-config/r0/src/lib/x86-64-accton-as5812-54t-r0.yml new file mode 100644 index 00000000..35fdeeda --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/platform-config/r0/src/lib/x86-64-accton-as5812-54t-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for AS5812 +# +###################################################################### + +x86-64-accton-as5812-54t-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54x/platform-config/r0/src/lib/x86-64-accton-as5812-54x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54x/platform-config/r0/src/lib/x86-64-accton-as5812-54x-r0.yml new file mode 100644 index 00000000..b9f49b96 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54x/platform-config/r0/src/lib/x86-64-accton-as5812-54x-r0.yml @@ -0,0 +1,23 @@ +--- + +###################################################################### +# +# platform-config for AS5812 +# +###################################################################### + +x86-64-accton-as5812-54x-r0: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6712-32x/platform-config/r0/src/lib/x86-64-accton-as6712-32x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as6712-32x/platform-config/r0/src/lib/x86-64-accton-as6712-32x-r0.yml new file mode 100644 index 00000000..e6274083 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6712-32x/platform-config/r0/src/lib/x86-64-accton-as6712-32x-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for AS6712 +# +###################################################################### + +x86-64-accton-as6712-32x-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/platform-config/r0/src/lib/x86-64-accton-as6812-32x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/platform-config/r0/src/lib/x86-64-accton-as6812-32x-r0.yml new file mode 100644 index 00000000..6a6c75d0 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/platform-config/r0/src/lib/x86-64-accton-as6812-32x-r0.yml @@ -0,0 +1,26 @@ +--- + +###################################################################### +# +# platform-config for AS6812 +# +###################################################################### + +x86-64-accton-as6812-32x-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 + diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as7512-32x/platform-config/r0/src/lib/x86-64-accton-as7512-32x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as7512-32x/platform-config/r0/src/lib/x86-64-accton-as7512-32x-r0.yml new file mode 100644 index 00000000..2c83eb0d --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as7512-32x/platform-config/r0/src/lib/x86-64-accton-as7512-32x-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for AS7512 +# +###################################################################### + +x86-64-accton-as5712-32x-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as7712-32x/platform-config/r0/src/lib/x86-64-accton-as7712-32x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as7712-32x/platform-config/r0/src/lib/x86-64-accton-as7712-32x-r0.yml new file mode 100644 index 00000000..9630c73f --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as7712-32x/platform-config/r0/src/lib/x86-64-accton-as7712-32x-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for AS7712 +# +###################################################################### + +x86-64-accton-as7712-32x-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as7716-32x/platform-config/r0/src/lib/x86-64-accton-as7716-32x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-as7716-32x/platform-config/r0/src/lib/x86-64-accton-as7716-32x-r0.yml new file mode 100644 index 00000000..c528084b --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as7716-32x/platform-config/r0/src/lib/x86-64-accton-as7716-32x-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for AS7716 +# +###################################################################### + +x86-64-accton-as7716-32x-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS0,115200n8 diff --git a/packages/platforms/accton/x86-64/x86-64-accton-wedge-16x/platform-config/r0/src/lib/x86-64-accton-wedge-16x-r0.yml b/packages/platforms/accton/x86-64/x86-64-accton-wedge-16x/platform-config/r0/src/lib/x86-64-accton-wedge-16x-r0.yml new file mode 100644 index 00000000..bc55cd86 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-wedge-16x/platform-config/r0/src/lib/x86-64-accton-wedge-16x-r0.yml @@ -0,0 +1,28 @@ +--- + +###################################################################### +# +# platform-config for WEDGE +# +###################################################################### + +x86-64-accton-wedge-16x-r0: + + grub: + + serial: >- + --unit=0 + --speed=57600 + --word=8 + --parity=0 + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS1,57600n8 + rd_NO_MD + rd_NO_LUKS + intel_iommu=off diff --git a/packages/platforms/celestica/x86-64/x86-64-cel-redstone-xp/platform-config/r0/src/lib/x86-64-cel-redstone-xp-r0.yml b/packages/platforms/celestica/x86-64/x86-64-cel-redstone-xp/platform-config/r0/src/lib/x86-64-cel-redstone-xp-r0.yml new file mode 100644 index 00000000..69652363 --- /dev/null +++ b/packages/platforms/celestica/x86-64/x86-64-cel-redstone-xp/platform-config/r0/src/lib/x86-64-cel-redstone-xp-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for REDSTONE +# +###################################################################### + +x86-64-cel-redstone-xp-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-2 + + args: >- + nopat + console=ttyS0,115200n8 diff --git a/packages/platforms/kvm/x86-64/x86-64-kvm-x86-64/platform-config/r0/src/lib/x86-64-kvm-x86-64-r0.yml b/packages/platforms/kvm/x86-64/x86-64-kvm-x86-64/platform-config/r0/src/lib/x86-64-kvm-x86-64-r0.yml new file mode 100644 index 00000000..83133bce --- /dev/null +++ b/packages/platforms/kvm/x86-64/x86-64-kvm-x86-64/platform-config/r0/src/lib/x86-64-kvm-x86-64-r0.yml @@ -0,0 +1,26 @@ +--- + +###################################################################### +# +# platform-config for KVM +# +###################################################################### + +x86-64-kvm-x86-64-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-9-6 + + args: >- + nopat + console=ttyS0,115200n8 + \ No newline at end of file diff --git a/packages/platforms/qemu/arm/arm-qemu-armv7a/platform-config/r0/src/lib/arm-qemu-armv7a-r0.yml b/packages/platforms/qemu/arm/arm-qemu-armv7a/platform-config/r0/src/lib/arm-qemu-armv7a-r0.yml index ce884b4a..dd31aaad 100644 --- a/packages/platforms/qemu/arm/arm-qemu-armv7a/platform-config/r0/src/lib/arm-qemu-armv7a-r0.yml +++ b/packages/platforms/qemu/arm/arm-qemu-armv7a/platform-config/r0/src/lib/arm-qemu-armv7a-r0.yml @@ -1,4 +1,18 @@ +--- + +###################################################################### +# +# platform-config for ARM/QEMU +# +###################################################################### + arm-qemu-armv7a-r0: + flat_image_tree: - kernel: onl-kernel-3.2-deb7-arm-iproc-all:armel, kernel-3.2-deb7-arm-iproc-all.bin.gz - dtb: onl-kernel-3.2-deb7-arm-iproc-all:armel, accton_as4610_54.dtb + kernel: + <<: *arm-iproc-kernel + dtb: + =: accton_as4610_54.dtb + <<: *arm-iproc-kernel-package + itb: + <<: *arm-itb diff --git a/packages/platforms/quanta/powerpc/powerpc-quanta-lb9/platform-config/r0/src/lib/powerpc-quanta-lb9-r0.yml b/packages/platforms/quanta/powerpc/powerpc-quanta-lb9/platform-config/r0/src/lib/powerpc-quanta-lb9-r0.yml index 309845f9..e21363f2 100644 --- a/packages/platforms/quanta/powerpc/powerpc-quanta-lb9/platform-config/r0/src/lib/powerpc-quanta-lb9-r0.yml +++ b/packages/platforms/quanta/powerpc/powerpc-quanta-lb9/platform-config/r0/src/lib/powerpc-quanta-lb9-r0.yml @@ -1,8 +1,21 @@ +--- + +###################################################################### +# +# platform definition for LB9 +# +###################################################################### + powerpc-quanta-lb9-r0: + flat_image_tree: - kernel: onl-kernel-3.9.6-powerpc-e500v:powerpc, kernel-3.9.6-powerpc-e500v.bin.gz - dtb: onl-kernel-3.9.6-powerpc-e500v:powerpc, powerpc-quanta-lb9-r0.dtb + + kernel: + <<: *e500v-kernel + dtb: + =: powerpc-quanta-lb9-r0.dtb + <<: *e500v-kernel-package loader: - partition: /dev/sda1 - raw: True + device: /dev/sda + nos_bootcmds: *ide_bootcmds diff --git a/packages/platforms/quanta/powerpc/powerpc-quanta-ly2/platform-config/r0/src/lib/powerpc-quanta-ly2-r0.yml b/packages/platforms/quanta/powerpc/powerpc-quanta-ly2/platform-config/r0/src/lib/powerpc-quanta-ly2-r0.yml index 95c591b1..80af8e38 100644 --- a/packages/platforms/quanta/powerpc/powerpc-quanta-ly2/platform-config/r0/src/lib/powerpc-quanta-ly2-r0.yml +++ b/packages/platforms/quanta/powerpc/powerpc-quanta-ly2/platform-config/r0/src/lib/powerpc-quanta-ly2-r0.yml @@ -1,7 +1,21 @@ +--- + +###################################################################### +# +# platform-config for LY2 +# +###################################################################### + powerpc-quanta-ly2-r0: + flat_image_tree: - kernel: onl-kernel-3.9.6-powerpc-e500v:powerpc, kernel-3.9.6-powerpc-e500v.bin.gz - dtb: onl-kernel-3.9.6-powerpc-e500v:powerpc, powerpc-quanta-ly2-r0.dtb + kernel: + <<: *e500v-kernel + dtb: + =: powerpc-quanta-ly2-r0.dtb + <<: *e500v-kernel-package loader: - partition: /dev/mmcblk0p1 + device: /dev/mmcblk0 + ##partition: /dev/mmcblk0p1 + nos_bootcmds: *mmc_bootcmds diff --git a/packages/platforms/quanta/x86-64/x86-64-quanta-ly6-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly6-rangeley-r0.yml b/packages/platforms/quanta/x86-64/x86-64-quanta-ly6-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly6-rangeley-r0.yml new file mode 100644 index 00000000..cf0815d2 --- /dev/null +++ b/packages/platforms/quanta/x86-64/x86-64-quanta-ly6-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly6-rangeley-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for LY6 +# +###################################################################### + +x86-64-quanta-ly6-rangeley-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-9-6 + + args: >- + console=ttyS1,115200n8 + \ No newline at end of file diff --git a/packages/platforms/quanta/x86-64/x86-64-quanta-ly8-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly8-rangeley-r0.yml b/packages/platforms/quanta/x86-64/x86-64-quanta-ly8-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly8-rangeley-r0.yml new file mode 100644 index 00000000..2fa1247a --- /dev/null +++ b/packages/platforms/quanta/x86-64/x86-64-quanta-ly8-rangeley/platform-config/r0/src/lib/x86-64-quanta-ly8-rangeley-r0.yml @@ -0,0 +1,25 @@ +--- + +###################################################################### +# +# platform-config for LY8 +# +###################################################################### + +x86-64-quanta-ly8-rangeley-r0: + + grub: + + serial: >- + --port=0x2f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-3-9-6 + + args: >- + console=ttyS1,115200n8 + \ No newline at end of file diff --git a/tools/flat-image-tree.py b/tools/flat-image-tree.py index 06afa1f9..fe95dab8 100755 --- a/tools/flat-image-tree.py +++ b/tools/flat-image-tree.py @@ -7,7 +7,16 @@ import subprocess import yaml import tempfile -import json + +import os, sys +toolsdir = os.path.dirname(os.path.abspath(__file__)) +onldir = os.path.dirname(toolsdir) +pydir = os.path.join(onldir, "packages/base/all/vendor-config-onl/src/python") +sys.path.append(pydir) +import onl.YamlUtils + +from onlpm import * +pm = defaultPm() class Image(object): """Base ITS Image Class""" @@ -20,19 +29,31 @@ class Image(object): self.entry = None self.os = None - if ',' in data: - # Shorthand for tuple specifier - data = tuple([ x.strip() for x in data.split(',') ]) + if type(data) == str: + if ',' in data: + pkg, fname = [x.strip() for x in data.split(',')] + else: + pkg, fname = None, data + elif type(data) == list: + pkg, fname = data + elif type(data) == dict: + fname = data['='] + pkg = data.get('package', None) + else: + raise ValueError("invalid image specifier: %s" % repr(data)) - if(isinstance(data, tuple)): - # - # The data specifies an ONLPM (package,file) pair. - # - self.data = subprocess.check_output("onlpm --quiet --find-file %s %s" % data, shell=True).strip() + if pkg is not None: + pm.require(pkg, force=False, build_missing=False) + self.data = pm.opr.get_file(pkg, fname) else: self.data = data - self.name = os.path.basename(self.data) + try: + self.name = os.path.basename(fname) + except: + import pdb + pdb.set_trace() + raise self.description = self.name @@ -66,8 +87,8 @@ class Image(object): class KernelImage(Image): """Kernel image entry""" - def __init__(self, fname, arch): - Image.__init__(self, "kernel", fname, compression='gzip') + def __init__(self, fdata, arch): + Image.__init__(self, "kernel", fdata, compression='gzip') self.os = '"linux"' # Fixme -- thse should be parameterized @@ -87,8 +108,8 @@ class KernelImage(Image): class InitrdImage(Image): """Initrd image entry""" - def __init__(self, fname, arch): - Image.__init__(self, "ramdisk", fname, compression='gzip') + def __init__(self, fdata, arch): + Image.__init__(self, "ramdisk", fdata, compression='gzip') # Fixme -- thse should be parameterized if arch == 'powerpc': @@ -108,8 +129,8 @@ class InitrdImage(Image): class DtbImage(Image): """DTB Image Entry""" - def __init__(self, fname): - Image.__init__(self, "flat_dt", fname, compression="none") + def __init__(self, fdata): + Image.__init__(self, "flat_dt", fdata, compression="none") def write(self, f): self.start_image(f) @@ -169,18 +190,31 @@ class FlatImageTree(object): initrd = d.get('initrd', None) + sys.stderr.write("*** platform %s kernel %s\n" + % (name, kernel,)) self.add_config(name, kernel, dtb, initrd) - def add_yaml(self, name, fname): - d = yaml.load(open(fname)) + def add_yaml(self, name, fname, defaults=None): + if defaults is not None: + d = onl.YamlUtils.merge(defaults, fname) + else: + with open(fname) as fd: + d = yaml.load(fd) self.add_dict(name, d) def add_platform_package(self, package): print package platform = package.replace(":%s" % ops.arch, "").replace("onl-platform-config-", "") - y = subprocess.check_output("onlpm --quiet --find-file %s %s.yml" % (package, platform), shell=True).strip() - self.add_yaml(platform, y) + + vpkg = "onl-vendor-config-onl:all" + pm.require(vpkg, force=False, build_missing=False) + y1 = pm.opr.get_file(vpkg, "platform-config-defaults-uboot.yml") + + pm.require(package, force=False, build_missing=False) + y2 = pm.opr.get_file(package, platform + '.yml') + + self.add_yaml(platform, y2, defaults=y1) def add_platform(self, platform): if (":%s" % ops.arch) in platform: @@ -195,17 +229,19 @@ class FlatImageTree(object): def writef(self, f): kdict = {} - for k in set(self.kernels): - kdict[k] = KernelImage(k, ops.arch) + for k in self.kernels: + ki = KernelImage(k, ops.arch) + kdict[ki.name] = ki ddict = {} - for d in set(self.dtbs): - ddict[d] = DtbImage(d) + for d in self.dtbs: + di = DtbImage(d) + ddict[di.name] = di idict = {} - for i in set(self.initrds): - idict[i] = InitrdImage(i, ops.arch) - + for i in self.initrds: + ii = InitrdImage(i, ops.arch) + idict[ii.name] = ii f.write("""/* \n""") @@ -220,27 +256,27 @@ class FlatImageTree(object): f.write(""" images {\n\n""") f.write(""" /* Kernel Images */\n""") - for k in set(self.kernels): - KernelImage(k, ops.arch).write(f) + for k in kdict.values(): + k.write(f) f.write("""\n""") f.write(""" /* DTB Images */\n""") - for d in set(self.dtbs): - DtbImage(d).write(f) + for d in ddict.values(): + d.write(f) f.write("""\n""") f.write(""" /* Initrd Images */\n""") - for i in set(self.initrds): - InitrdImage(i, ops.arch).write(f) + for i in idict.values(): + i.write(f) f.write(""" };\n""") f.write(""" configurations {\n""") for (name, (kernel, dtb, initrd)) in self.configurations.iteritems(): f.write(""" %s {\n""" % name) f.write(""" description = "%s";\n""" % name) - f.write(""" kernel = "%s";\n""" % (kdict[kernel].name)) - f.write(""" ramdisk = "%s";\n""" % (idict[initrd].name)) - f.write(""" fdt = "%s";\n""" % (ddict[dtb].name)) + f.write(""" kernel = "%s";\n""" % (KernelImage(kernel, ops.arch).name)) + f.write(""" ramdisk = "%s";\n""" % (InitrdImage(initrd, ops.arch).name)) + f.write(""" fdt = "%s";\n""" % (DtbImage(dtb).name)) f.write(""" };\n\n""") f.write(""" };\n""") f.write("""};\n""") @@ -291,12 +327,14 @@ if __name__ == '__main__': fit.add_yaml(y) if ops.add_platform == [['all']]: - ops.add_platform = [ subprocess.check_output("onlpm --list-platforms --arch %s" % (ops.arch), shell=True).split() ] + ops.add_platform = [ pm.list_platforms(ops.arch) ] if ops.add_platform == [['initrd']]: # Add support for the platforms listed in the initrd's platform manifest (package,f) = initrd.split(':') - mfile = subprocess.check_output("onlpm --find-file %s:%s manifest.json" % (package, ops.arch), shell=True).strip() + pkg = package + ':' + ops.arch + pm.require(pkg, force=False, build_missing=False) + mfile = pm.opr.get_file(pkg, "manifest.json") manifest = json.load(open(mfile)) ops.add_platform = [[ "%s" % p for p in manifest['platforms'] ]] diff --git a/tools/mkshar b/tools/mkshar index 138f2755..fa63795a 100755 --- a/tools/mkshar +++ b/tools/mkshar @@ -36,6 +36,9 @@ parser.add_option('--unzip-loop', parser.add_option('--unzip-pad', action='store_true', help="Special pad options for deficient unzip") +parser.add_option('--inplace', + action='store_true', + help="Perform fixups in-place") parser.add_option('--fixup-perms', type=str, help="Post-unpack shell script to fix permissions") @@ -142,6 +145,10 @@ def _splice(tag, val): line = line + ('#' * llen) buf = buf[:p] + line + buf[q:] +def _spliceMaybe(tag, val): + val = "${%s-\"%s\"}" % (tag, val,) + _splice(tag, val) + logger.info("prepping SFX") _splice('SFX_BYTES', len(buf)) @@ -153,23 +160,27 @@ if opts.lazy: else: _splice('SFX_LAZY', '') if opts.unzip_sfx: - _splice('SFX_UNZIP', '1') + _spliceMaybe('SFX_UNZIP', '1') else: - _splice('SFX_UNZIP', '') + _spliceMaybe('SFX_UNZIP', '') if opts.unzip_pipe: - _splice('SFX_PIPE', '1') + _spliceMaybe('SFX_PIPE', '1') else: - _splice('SFX_PIPE', '') + _spliceMaybe('SFX_PIPE', '') if opts.unzip_loop: - _splice('SFX_LOOP', '1') + _spliceMaybe('SFX_LOOP', '1') else: - _splice('SFX_LOOP', '') + _spliceMaybe('SFX_LOOP', '') if opts.unzip_pad: _splice('SFX_PAD', 'pad.bin') else: _splice('SFX_PAD', '') if opts.fixup_perms: _splice('SFX_PERMS', opts.fixup_perms) +if opts.inplace: + _spliceMaybe('SFX_INPLACE', '1') +else: + _spliceMaybe('SFX_INPLACE', '') # remember the checksum offset ckStart = buf.find("SFX_CHECKSUM=") diff --git a/tools/onlplatform.py b/tools/onlplatform.py new file mode 100644 index 00000000..633bc722 --- /dev/null +++ b/tools/onlplatform.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +"""onlplatform.py + +Extract install file requirements from the platform YAML file and/or +the platform package metadata. +""" + +import sys, os +import itertools + +toolsdir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(toolsdir) + +onldir = os.path.dirname(toolsdir) +onlpydir = os.path.join(onldir, "packages/base/all/vendor-config-onl/src/python") +sys.path.append(onlpydir) + +import onl.YamlUtils + +from onlpm import * +# glob import is required here so pickle load load properly + +pm = defaultPm() + +platform = sys.argv[1] +arch = sys.argv[2] +key = sys.argv[3] + +def extractKey(platform, arch, key): + + pkg = "onl-platform-config-%s:%s" % (platform, arch,) + basename = "%s.yml" % platform + pm.require(pkg, force=False, build_missing=False) + platformConfigPath = pm.opr.get_file(pkg, basename) + + if arch in ('amd64',): + pkg = "onl-vendor-config-onl:all" + basename = "platform-config-defaults-x86-64.yml" + subkey = 'grub' + else: + pkg = "onl-vendor-config-onl:all" + basename = "platform-config-defaults-uboot.yml" + subkey = 'flat_image_tree' + pm.require(pkg, force=False, build_missing=False) + defaultConfigPath = pm.opr.get_file(pkg, basename) + + platformConf = onl.YamlUtils.merge(defaultConfigPath, platformConfigPath) + resource = platformConf[platform][subkey][key] + if type(resource) == dict: + pkg = resource['package'] + basename = resource['='] + else: + pkg, sep, basename = resource.partition(',') + if not sep: + raise ValueError("resource missing package declaration: %s" % resource) + pkg = pkg.strip() + basename = basename.strip() + pm.require(pkg, force=False, build_missing=False) + resourcePath = pm.opr.get_file(pkg, basename) + return resourcePath + +def extractVendor(platform, arch): + pkg = "onl-platform-config-%s:%s" % (platform, arch,) + l = pm.opr.lookup_all(pkg) + if not l: + raise SystemExit("cannot find package %s:%s" + % (platform, arch,)) + l = [x for x in pm.package_groups if pkg in x] + l = list(itertools.chain(*[x.prerequisite_packages() for x in l])) + l = [x for x in l if x.startswith('onl-vendor-config-')] + return "\n".join(l) + +if key in ('kernel', 'initrd', 'dtb', 'itb',): + print extractKey(platform, arch, key) + sys.exit(0) + +if key == 'vendor': + print extractVendor(platform, arch) + sys.exit(0) + +raise SystemExit("invalid key %s" % key) diff --git a/tools/onlpm.py b/tools/onlpm.py index 552f4f1f..a3ff9a0c 100755 --- a/tools/onlpm.py +++ b/tools/onlpm.py @@ -366,12 +366,12 @@ class OnlPackage(object): if 'init' in self.pkg: if not os.path.exists(self.pkg['init']): raise OnlPackageError("Init script '%s' does not exist." % self.pkg['init']) - command = command + "--deb-init %s" % self.pkg['init'] + command = command + "--deb-init %s " % self.pkg['init'] if 'post-install' in self.pkg: if not os.path.exists(self.pkg['post-install']): raise OnlPackageError("Post-install script '%s' does not exist." % self.pkg['post-install']) - command = command + "--after-install %s" % self.pkg['post-install'] + command = command + "--after-install %s " % self.pkg['post-install'] if logger.level < logging.INFO: command = command + "--verbose " @@ -561,7 +561,6 @@ class OnlPackageGroup(object): with onlu.Lock(os.path.join(self._pkgs['__directory'], '.lock')): self.gmake_locked("clean", 'Clean') - class OnlPackageRepo(object): """Package Repository and Interchange Class @@ -765,8 +764,8 @@ class OnlPackageManager(object): self.package_groups = [] self.opr = None - def set_repo(self, repodir): - self.opr = OnlPackageRepo(repodir, ops.repo_package_dir) + def set_repo(self, repodir, packagedir='packages'): + self.opr = OnlPackageRepo(repodir, packagedir=packagedir) def filter(self, subdir=None, arches=None, substr=None): @@ -999,6 +998,43 @@ class OnlPackageManager(object): def pkg_info(self): return "\n".join([ pg.pkg_info() for pg in self.package_groups if not pg.filtered ]) + def list_platforms(self, arch): + platforms = [] + for pg in self.package_groups: + for p in pg.packages: + (name, pkgArch) = OnlPackage.idparse(p.id()) + m = re.match(r'onl-platform-config-(?P.*)', name) + if m: + if arch in [ pkgArch, "all", None ]: + platforms.append(m.groups('platform')[0]) + return platforms + +def defaultPm(): + repo = os.environ.get('ONLPM_OPTION_REPO', None) + envJson = os.environ.get('ONLPM_OPTION_INCLUDE_ENV_JSON', None) + packagedirs = os.environ['ONLPM_OPTION_PACKAGEDIRS'].split(':') + repoPackageDir = os.environ.get('ONLPM_OPTION_REPO_PACKAGE_DIR', 'packages') + subdir = os.getcwd() + arches = ['amd64', 'powerpc', 'armel', 'all',] + + if envJson: + for j in envJson.split(':'): + data = json.load(open(j)) + for (k, v) in data.iteritems(): + try: + v = v.encode('ascii') + except UnicodeEncodeError: + pass + os.environ[k] = v + + pm = OnlPackageManager() + pm.set_repo(repo, packagedir=repoPackageDir) + for pdir in packagedirs: + pm.load(pdir, usecache=True, rebuildcache=False) + pm.filter(subdir = subdir, arches=arches) + + return pm + if __name__ == '__main__': ap = argparse.ArgumentParser("onlpm") @@ -1090,7 +1126,7 @@ if __name__ == '__main__': pm = OnlPackageManager() if ops.repo: logger.debug("Setting repo as '%s'..." % ops.repo) - pm.set_repo(ops.repo) + pm.set_repo(ops.repo, packagedir=ops.repo_package_dir) if ops.in_repo: for p in ops.in_repo: @@ -1113,15 +1149,10 @@ if __name__ == '__main__': print if ops.list_platforms: - platforms = [] - for pg in pm.package_groups: - for p in pg.packages: - (name, arch) = OnlPackage.idparse(p.id()) - m = re.match(r'onl-platform-config-(?P.*)', name) - if m: - if ops.arch in [ arch, "all", None ]: - platforms.append(m.groups('platform')[0]) - + if not ops.arch: + logger.error("missing --arch with --list-platforms") + sys.exit(1) + platforms = pm.list_platforms(ops.arch) if ops.csv: print ','.join(platforms) else: diff --git a/tools/scripts/sfx.sh.in b/tools/scripts/sfx.sh.in index bd1598bc..43d4463f 100644 --- a/tools/scripts/sfx.sh.in +++ b/tools/scripts/sfx.sh.in @@ -4,7 +4,7 @@ set -e CMD=${0##*/} -UNZIP=/usr/bin/unzip +UNZIP=${UNZIP-"/usr/bin/unzip"} UNZIPOPTS= UNZIPARGS= @@ -29,9 +29,10 @@ SFX_INSTALL=install ## internal script in the payload to run ################# SFX_PERMS= ## internal script to correct file permissions #################### SFX_PAD= ## pad file (this payload) to skip during unpack #################### SFX_LAZY= ## set to '1' to defer extraction to SFX_INSTALL ################## -SFX_UNZIP=1 ## set to '' if this unzip cannot parse SFX headers ############# -SFX_LOOP=1 ## set to '' if this unzip cannot read from a loopback/block #### -SFX_PIPE=1 ## set to '' if this unzip cannot read from a pipe ############## +SFX_UNZIP=1 ## set to '' if this unzip cannot parse SFX headers ############## +SFX_LOOP=1 ## set to '' if this unzip cannot read from a loopback/block ###### +SFX_PIPE=1 ## set to '' if this unzip cannot read from a pipe ################ +SFX_INPLACE= ## set to '1' if this zip file can be modified in place########## if test "$SFX_PAD"; then UNZIPARGS=$UNZIPARGS${UNZIPARGS:+" "}"-x $SFX_PAD" @@ -100,6 +101,23 @@ do_cleanup() } trap "do_cleanup" 0 1 +echo "$CMD: computing checksum of original archive" +{ + dd if="$SHARABS" bs=$SFX_BLOCKSIZE count=$SFX_BLOCKS 2>/dev/null | sed -e "/^SFX_CHECKSUM=/d"; + dd if="$SHARABS" bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS 2>/dev/null +} | md5sum > "$workdir/ck" + +set dummy `cat "$workdir/ck"` +newck=$2 +rm -f "$workdir/ck" + +if test "$SFX_CHECKSUM" = "$newck"; then + echo "$CMD: checksum is OK" +else + echo "$CMD: *** checksum mismatch" 1>&2 + exit 1 +fi + _t() { local c z @@ -144,11 +162,19 @@ case "$SFX_PAD:$SFX_UNZIP:$SFX_LOOP:$SFX_PIPE" in esac if test "$SFX_PAD"; then - echo "$CMD: copying file and resetting pad" - cp "$SHARABS" $workdir/onie-installer.zip - dd if="$SHARABS" of=$workdir/onie-installer.zip bs=512 skip=$(($SFX_BLOCKS-1)) count=1 conv=notrunc - _CAT=":" - _ZIP="$workdir/onie-installer.zip" + echo "$CMD: extracting pad" + dd if="$SHARABS" of=$workdir/zip.bin bs=512 skip=$(($SFX_BLOCKS-1)) count=1 + if test "$SFX_INPLACE"; then + _CAT=":" + _ZIP="$SHARABS" + else + echo "$CMD: copying file before resetting pad" + cp "$SHARABS" $workdir/onie-installer.zip + _CAT=":" + _ZIP="$workdir/onie-installer.zip" + fi + echo "$CMD: resetting pad" + dd if="$workdir/zip.bin" of="$_ZIP" bs=512 count=1 conv=notrunc elif test "$SFX_UNZIP"; then echo "$CMD: processing SFX with unzip" _CAT=":" @@ -209,24 +235,6 @@ case "$banner" in ;; esac -echo "$CMD: computing checksum" -{ - dd if="$SHARABS" bs=$SFX_BLOCKSIZE count=$SFX_BLOCKS 2>/dev/null | sed -e "/^SFX_CHECKSUM=/d"; - dd if="$SHARABS" bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS 2>/dev/null -} | md5sum > "$UNZIPDIR/ck" - -set dummy `cat "$UNZIPDIR/ck"` -newck=$2 - -rm -f "$UNZIPDIR/ck" - -if test "$SFX_CHECKSUM" = "$newck"; then - echo "$CMD: checksum is OK" -else - echo "$CMD: *** checksum mismatch" 1>&2 - exit 1 -fi - shardir=`dirname $0` shardir=`cd $shardir && pwd`