mirror of https://github.com/aya-rs/aya
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			265 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Bash
		
	
			
		
		
	
	
			265 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Bash
		
	
| #!/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
 |