123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- #!/bin/bash
- VERBOSITY=0
- TEMP_D=""
- DEF_DISK_FORMAT="raw"
- DEF_FILESYSTEM="iso9660"
- CR="
- "
- error() { echo "$@" 1>&2; }
- fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
- Usage() {
- cat <<EOF
- Usage: ${0##*/} [ options ] output user-data [meta-data]
- Create a disk for cloud-init to utilize nocloud
- options:
- -h | --help show usage
- -d | --disk-format D disk format to output. default: raw
- can be anything supported by qemu-img or
- tar, tar-seed-local, tar-seed-net
- -H | --hostname H set hostname in metadata to H
- -f | --filesystem F filesystem format (vfat or iso), default: iso9660
- -i | --interfaces F write network interfaces file into metadata
- -N | --network-config F write network config file to local datasource
- -m | --dsmode M add 'dsmode' ('local' or 'net') to the metadata
- default in cloud-init is 'net', meaning network is
- required.
- -V | --vendor-data F vendor-data file
- -v | --verbose increase verbosity
- Note, --dsmode, --hostname, and --interfaces are incompatible
- with metadata.
- Example:
- * cat my-user-data
- #cloud-config
- password: passw0rd
- chpasswd: { expire: False }
- ssh_pwauth: True
- * echo "instance-id: \$(uuidgen || echo i-abcdefg)" > my-meta-data
- * ${0##*/} my-seed.img my-user-data my-meta-data
- * kvm -net nic -net user,hostfwd=tcp::2222-:22 \\
- -drive file=disk1.img,if=virtio -drive file=my-seed.img,if=virtio
- * ssh -p 2222 ubuntu@localhost
- EOF
- }
- bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
- cleanup() {
- [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
- }
- debug() {
- local level=${1}; shift;
- [ "${level}" -gt "${VERBOSITY}" ] && return
- error "${@}"
- }
- has_cmd() {
- command -v "$1" >/dev/null 2>&1
- }
- short_opts="hH:i:d:f:m:N:o:V:v"
- long_opts="disk-format:,dsmode:,filesystem:,help,hostname:,interfaces:,"
- long_opts="${long_opts}network-config:,output:,vendor-data:,verbose"
- getopt_out=$(getopt -n "${0##*/}" \
- -o "${short_opts}" -l "${long_opts}" -- "$@") &&
- eval set -- "${getopt_out}" ||
- bad_Usage
- ## <<insert default variables here>>
- output=""
- userdata=""
- metadata=""
- vendordata=""
- filesystem=""
- diskformat=$DEF_DISK_FORMAT
- interfaces=_unset
- dsmode=""
- hostname=""
- ncname="network-config"
- while [ $# -ne 0 ]; do
- cur=${1}; next=${2};
- case "$cur" in
- -h|--help) Usage ; exit 0;;
- -d|--disk-format) diskformat=$next; shift;;
- -f|--filesystem) filesystem=$next; shift;;
- -H|--hostname) hostname=$next; shift;;
- -i|--interfaces) interfaces=$next; shift;;
- -N|--network-config) netcfg=$next; shift;;
- -m|--dsmode) dsmode=$next; shift;;
- -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
- -V|--vendor-data) vendordata="$next";;
- --) shift; break;;
- esac
- shift;
- done
- ## check arguments here
- ## how many args do you expect?
- echo $1
- echo $2
- echo $3
- [ $# -ge 2 ] || bad_Usage "must provide output, userdata"
- [ $# -le 3 ] || bad_Usage "confused by additional args"
- output=$1
- userdata=$2
- metadata=$3
- if [ -n "$metadata" ]; then
- [ "$interfaces" = "_unset" -a -z "$dsmode" -a -z "$hostname" ] ||
- fail "metadata is incompatible with:" \
- "--interfaces, --hostname, --dsmode"
- fi
- case "$diskformat" in
- tar|tar-seed-local|tar-seed-net)
- if [ "${filesystem:-tar}" != "tar" ]; then
- fail "diskformat=tar is incompatible with filesystem"
- fi
- filesystem="$diskformat"
- ;;
- tar*)
- fail "supported 'tar' formats are tar, tar-seed-local, tar-seed-net"
- esac
- if [ -z "$filesystem" ]; then
- filesystem="$DEF_FILESYSTEM"
- fi
- if [ "$filesystem" = "iso" ]; then
- filesystem="iso9660"
- fi
- case "$filesystem" in
- tar*)
- has_cmd tar ||
- fail "missing 'tar'. Required for --filesystem=$filesystem";;
- vfat)
- has_cmd mkfs.vfat ||
- fail "missing 'mkfs.vfat'. Required for --filesystem=vfat."
- has_cmd mcopy ||
- fail "missing 'mcopy'. Required for --filesystem=vfat."
- ;;
- iso9660)
- has_cmd mkisofs ||
- fail "missing 'mkisofs'. Required for --filesystem=iso9660."
- ;;
- *) fail "unknown filesystem $filesystem";;
- esac
- case "$diskformat" in
- tar*|raw) :;;
- *) has_cmd "qemu-img" ||
- fail "missing 'qemu-img'. Required for --disk-format=$diskformat."
- esac
- [ "$interfaces" = "_unset" -o -r "$interfaces" ] ||
- fail "$interfaces: not a readable file"
- TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
- fail "failed to make tempdir"
- trap cleanup EXIT
- files=( "${TEMP_D}/user-data" "${TEMP_D}/meta-data" )
- if [ -n "$metadata" ]; then
- cp "$metadata" "$TEMP_D/meta-data" || fail "$metadata: failed to copy"
- else
- instance_id="iid-local01"
- iface_data=""
- [ "$interfaces" != "_unset" ] &&
- iface_data=$(sed ':a;N;$!ba;s/\n/\\n/g' "$interfaces")
- # write json formatted user-data (json is a subset of yaml)
- mdata=""
- for kv in "instance-id:$instance_id" "local-hostname:$hostname" \
- "interfaces:${iface_data}" "dsmode:$dsmode"; do
- key=${kv%%:*}
- val=${kv#*:}
- [ -n "$val" ] || continue
- mdata="${mdata:+${mdata},${CR}}\"$key\": \"$val\""
- done
- printf "{\n%s\n}\n" "$mdata" > "${TEMP_D}/meta-data"
- fi
- if [ -n "$netcfg" ]; then
- cp "$netcfg" "${TEMP_D}/$ncname" ||
- fail "failed to copy network config"
- files[${#files[@]}]="$TEMP_D/$ncname"
- fi
- if [ -n "$vendordata" ]; then
- cp "$vendordata" "${TEMP_D}/vendor-data" ||
- fail "failed to copy vendor data"
- files[${#files[@]}]="$TEMP_D/vendor-data"
- fi
- files_rel=( )
- for f in "${files[@]}"; do
- files_rel[${#files_rel[@]}]="${f#${TEMP_D}/}"
- done
- if [ "$userdata" = "-" ]; then
- cat > "$TEMP_D/user-data" || fail "failed to read from stdin"
- else
- cp "$userdata" "$TEMP_D/user-data" || fail "$userdata: failed to copy"
- fi
- ## alternatively, create a vfat filesystem with same files
- img="$TEMP_D/seed-data"
- tar_opts=( --owner=root --group=root )
- case "$filesystem" in
- tar)
- tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${files_rel[@]}" ||
- fail "failed to create tarball of ${files_rel[*]}"
- ;;
- tar-seed-local|tar-seed-net)
- if [ "$filesystem" = "tar-seed-local" ]; then
- path="var/lib/cloud/seed/nocloud"
- else
- path="var/lib/cloud/seed/nocloud-net"
- fi
- mkdir -p "${TEMP_D}/${path}" ||
- fail "failed making path for seed files"
- mv "${files[@]}" "${TEMP_D}/$path" ||
- fail "failed moving files"
- tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${path}" ||
- fail "failed to create tarball with $path"
- ;;
- iso9660)
- mkisofs -output "$img" -volid cidata \
- -joliet -rock "${files[@]}" > "$TEMP_D/err" 2>&1 ||
- { cat "$TEMP_D/err" 1>&2; fail "failed to mkisofs"; }
- ;;
- vfat)
- truncate -s 128K "$img" || fail "failed truncate image"
- out=$(mkfs.vfat -n cidata "$img" 2>&1) ||
- { error "failed: mkfs.vfat -n cidata $img"; error "$out"; }
- mcopy -oi "$img" "${files[@]}" :: ||
- fail "failed to copy user-data, meta-data to img"
- ;;
- esac
- [ "$output" = "-" ] && output="$TEMP_D/final"
- if [ "${diskformat#tar}" != "$diskformat" -o "$diskformat" = "raw" ]; then
- cp "$img" "$output" ||
- fail "failed to copy image to $output"
- else
- qemu-img convert -f raw -O "$diskformat" "$img" "$output" ||
- fail "failed to convert to disk format $diskformat"
- fi
- [ "$output" != "$TEMP_D/final" ] || { cat "$output" && output="-"; } ||
- fail "failed to write to -"
- debug 1 "wrote ${output} with filesystem=$filesystem and diskformat=$diskformat"
- # vi: ts=4 noexpandtab
|