Created
July 10, 2017 00:14
-
-
Save jirutka/872389ca4858f65844e02a0f4d176827 to your computer and use it in GitHub Desktop.
This is a wrapper for oneimage for uploading image from local system to OpenNebula server.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# vim: set ts=4: | |
# | |
# Install dependencies on Alpine: | |
# apk add ruby ruby-io-console ruby-json ruby-nokogiri ruby-xmlrpc bind-tools | |
# gem install opennebula-cli | |
# | |
#---help--- | |
# Usage: oneimage-upload [options] | |
# | |
# This is a wrapper for oneimage for uploading image from local system | |
# to OpenNebula server. It starts a web server that listens on port 80 and | |
# runs oneimage with URL of the given image served by this web server. | |
# | |
# Options: | |
# --name IMAGE_NAME Name of the new image (required). | |
# --endpoint ONE_XMLRPC URL of OpenNebula XMLRPC frontend (required). | |
# --password ONE_PASSWORD Password to authenticate with OpenNebula (required*). | |
# --path IMAGE_FILE Path of the disk image on local filesystem (required). | |
# --perms IMAGE_PERMS The image permissions (see oneimage chmod). | |
# --timeout TIMEOUT How long to wait (in seconds) until ONe server download | |
# the image and change its state to READY. | |
# Default is 300s. | |
# --user ONE_USER User name used to connect to OpenNebula (required*). | |
# ...and all options accepted by "oneimage create" | |
#---help--- | |
readonly PROGNAME='oneimage-upload' | |
die() { | |
printf '\033[1;31mERROR:\033[0m %s: %s\n' $PROGNAME "$@" >&2 # bold red | |
exit 1 | |
} | |
_oneimage() { | |
oneimage "$@" \ | |
${ONE_USER:+--user "$ONE_USER"} \ | |
${ONE_PASSWORD:+--password "$ONE_PASSWORD"} \ | |
--endpoint "$ONE_XMLRPC" | |
} | |
# Prints IP address of interface that can reach host with the given IP. | |
my_ip() { | |
local reach_ip="$1" | |
ip route get "$reach_ip" 2>/dev/null \ | |
| grep -v unreachable \ | |
| sed -En 's/.* src ([0-9a-f.:]+) .*/\1/p' \ | |
| grep . | |
} | |
# Parses image state from XML response of "oneimage show". | |
parse_image_state() { | |
local xml="${1:-$(cat)}" | |
local state=$(echo "$xml" | sed -En 's|.*<STATE>([^>]+)</STATE>.*|\1|p') | |
if [ -z "$state" ]; then | |
printf 'Invalid response:\n%s\n' "$xml" >&2 | |
return 1 | |
fi | |
# See https://docs.opennebula.org/5.0/integration/system_interfaces/api.html. | |
case "$state" in | |
0) echo INIT;; | |
1) echo READY;; | |
3) echo DISABLED;; | |
4) echo LOCKED;; | |
5) echo ERROR;; | |
*) echo $state;; | |
esac | |
} | |
# Parses host part of the given URL address. | |
url_parse_host() { | |
echo "$1" | sed -E 's|^\w+://||; s|^\w+:?\w*@||; s|/.*$||; s|:\d+$||' | |
} | |
usage() { | |
sed -En '/^#---help---/,/^#---help---/p' "$0" | sed -E 's/^# ?//; 1d;$d;' | |
exit ${1:-0} | |
} | |
cleanup() { | |
trap '' EXIT HUP INT TERM | |
test -z "${httpd_pid:-}" || kill $httpd_pid || kill -9 $httpd_pid | |
test -d "${temp_dir:-}" && rm -Rf "$temp_dir" || : | |
} | |
#============================ M a i n ============================# | |
other_opts='' | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
--*=*) opt="${1%%=*}" val="${1#*=}" n=1;; | |
*) opt="$1" val="$2" n=2;; | |
esac | |
case "$opt" in | |
-d | --datastore) ONE_DATASTORE="$val";; | |
--description) IMAGE_DESCRIPTION="$val";; | |
--endpoint) ONE_XMLRPC="$val";; | |
--name) IMAGE_NAME="$val";; | |
--password) ONE_PASSWORD="$val";; | |
--path) IMAGE_FILE="$val";; | |
--user) ONE_USER="$val";; | |
--perms) IMAGE_PERMS="$val";; | |
--timeout) TIMEOUT="$val";; | |
-h | --help) usage 0;; | |
-V | --version) usage 1 >&2;; | |
*) other_opts="$other_opts $1" n=1;; | |
esac | |
shift $n | |
done | |
: ${TIMEOUT:=300} | |
test -n "${IMAGE_FILE:-}" || die 'Either --path or $IMAGE_FILE must be defined!' | |
test -r "$IMAGE_FILE" || die "File '$IMAGE_FILE' does not exist or not readable!" | |
test -n "${ONE_XMLRPC:-}" || die 'Either --endpoint or $ONE_XMLRPC must be defined!' | |
test -n "${IMAGE_NAME:-}" || die 'Either --name or $IMAGE_NAME must be defined!' | |
trap cleanup EXIT HUP INT TERM | |
temp_dir=$(mktemp -d /tmp/$PROGNAME.XXXXXX) | |
ln -s "$(realpath "$IMAGE_FILE")" "$temp_dir"/ | |
one_host=$(url_parse_host "$ONE_XMLRPC") | |
one_ip=$(dig +short $one_host) || die 'Failed to resolve IP of ONe server!' | |
server_ip=$(my_ip $one_ip) || die "$one_host ($one_ip) is not reachable!" | |
path="http://$server_ip/${IMAGE_FILE##*/}" | |
echo "Serving image at $path" >&2 | |
busybox httpd -f -h "$temp_dir" & | |
httpd_pid="$!" | |
echo "Creating image $IMAGE_NAME" >&2 | |
out=$(_oneimage create \ | |
${ONE_DATASTORE:+--datastore "$ONE_DATASTORE"} \ | |
${IMAGE_DESCRIPTION:+--description "$IMAGE_DESCRIPTION"} \ | |
--name="$IMAGE_NAME" \ | |
--path="$path" \ | |
$other_opts) | |
image_id=$(echo "$out" | sed -En 's/^ID: (\d+)/\1/p' | grep .) \ | |
|| die "$out" | |
echo "Image created with ID $image_id, please wait until it's uploaded..." >&2 | |
elapsed=0 | |
while true; do | |
[ $elapsed -lt $TIMEOUT ] || die "Timeout ${TIMEOUT}s exceeded!" | |
state=$(_oneimage show --xml $image_id \ | |
| parse_image_state) || exit 1 | |
case "$state" in | |
READY) break;; | |
INIT | LOCKED) ;; | |
*) die "Something went wrong, image state is: $state";; | |
esac | |
sleep 2 | |
elapsed=$(( elapsed + 2 )) | |
done | |
if [ -n "${IMAGE_PERMS:-}" ]; then | |
echo "Changing permissions to $IMAGE_PERMS" >&2 | |
_oneimage chmod $image_id $IMAGE_PERMS | |
fi | |
echo 'Completed' >&2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment