Skip to content

Instantly share code, notes, and snippets.

@smoser

smoser/.gitignore

Last active Sep 30, 2020
Embed
What would you like to do?
cloud-init ubuntu nocloud example with network config
*.img
*.raw

Boot Ubuntu providing it network config in NoCloud Datasource

Ubuntu images can be booted with an additional disk that provides user-data, meta-data and network configuration. This is described some in the upstream documentation.

But this example allows for more easily tinkering.

See a screencast of this on asciinema.org.

What is here

A brief description of files here.

Usage

Heres an example walking through how to use this.

  • Verify dependencies

    $ ./check-dependencies
    All good. (cloud-localds genisoimage qemu-img qemu-system-x86_64 wget)
    
  • Download an image

    Original images are compressed qcow2. get-image downloads an original image and converts it to 'raw' format. This is not strictly necessary, but booting a compressed qcow2 directly means that all disk reads go through decompression.

    $ ./get-image xenial
    downloading <...>
    dist: xenial-server-cloudimg-amd64-disk1.img
    raw:  xenial-server-cloudimg-amd64.raw
    
  • Build a seed image from provided files

    cloud-localds creates an iso9660 filesystem with expected content. Specifically the filesystem label is 'cidata', and the top level directory has 'meta-data', 'user-data', and 'network-config' files. network-config is optional.

    $ cloud-localds -v --network-config=network-config-v1.yaml \
        seed.img user-data.yaml meta-data.yaml
    wrote seed.img with filesystem=iso9660 and diskformat=raw
    
  • Create a qcow2 disk named 'disk.img' backed by the raw image

    By using a qcow image backed by the raw, we can avoid modifying the original.

    $ qemu-img create -f qcow2 -b xenial-server-cloudimg-amd64.raw disk.img
    
  • Boot the image

    By default, boot will run qemu in vga graphics mode, and create a window on your system. It will boot to a login prompt and you can login with username ubuntu and password passw0rd.

    I like to boot with qemu's -nographic option, which will display the full boot in a terminal which is better for demos. See a full boot log here

    Some things to look at once you've logged in

    $ cat /run/cloud-init/result.json
    {
     "v1": {
      "datasource": "DataSourceNoCloud [seed=/dev/sr0][dsmode=net]",
      "errors": []
     }
    }
    
    $ grep -v '^[#]' /etc/network/interfaces.d/50-cloud-init.cfg
    auto lo
    iface lo inet loopback
    
    auto interface0
    iface interface0 inet static
        address 192.168.1.10/24
        gateway 192.168.1.254
    
    $ grep INFO /var/log/cloud-init.log 
    2017-11-03 18:10:18,785 - stages.py[INFO]: Loaded datasource DataSourceNoCloud - DataSourceNoCloud [seed=/dev/sr0][dsmode=net]
    2017-11-03 18:10:18,964 - stages.py[INFO]: Applying network configuration from ds bringup=False: {'config': [{'type': 'physical', 'mac_address': '52:54:00:12:34:00', 'name': 'interface0', 'subnets': [{'netmask': '255.255.255.0', 'gateway': '192.168.1.254', 'type': 'static', 'address': '192.168.1.10'}]}], 'version': 1}
    2017-11-03 18:10:22,954 - cc_apt_configure.py[INFO]: No custom template provided, fall back to builtin
    
#!/bin/sh
Usage() {
cat <<EOF
Usage: ${0##*/} disk.img seed.img [qemu [additional [args]]]
EOF
}
fail() { echo "$@" 1>&2; exit 1; }
set -f
[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit; }
[ $# -ge 2 ] || { Usage 1>&2; exit 1; }
disk=$1
seed=$2
shift 2
[ "$1" = "--" ] && shift
[ -f "$disk" ] || fail "$disk: not a file"
# provide 'format=' correctly so qemu doesnt complain. (qcow2 or raw)
fmt=$(qemu-img info "$disk" | awk -F: '$1 == "file format" { print $2 }')
fmt=${fmt# }
# add -m 768 if no '-m' present.
args=" $* "
[ "${args##* -m}" = "-m" ] || set -- "$@" -m 768
MAC=${MAC:-52:54:00:12:34:00}
set -- \
qemu-system-x86_64 -enable-kvm \
-netdev type=user,id=net00 \
-device virtio-net-pci,netdev=net00,mac=${MAC} \
-drive file=$disk,id=bootdisk,if=none,format=$fmt,index=0 \
-device virtio-blk,drive=bootdisk \
-drive file=$seed,id=seed,if=none,format=raw,index=1 \
-device virtio-blk,drive=seed \
"$@"
echo "$@" 1>&2
"$@"
# vi: ts=4 expandtab
#!/bin/sh
MISSING=""
FOUND=""
checkdep() {
local exe="$1" package="$2" upstream="$3"
if command -v "$1" >/dev/null 2>&1; then
FOUND="${FOUND:+${FOUND} }$exe"
return "0"
fi
MISSING=${MISSING:+${MISSING }$package}
echo "missing $exe."
echo " It can be installed in package: $package"
[ -n "$upstream" ] &&
echo " Upstream project url: $upstream"
return 1
}
checkdep cloud-localds cloud-image-utils http://launchpad.net/cloud-utils
checkdep genisoimage genisoimage
checkdep qemu-img qemu-utils http://qemu.org/
checkdep qemu-system-x86_64 qemu-system-x86 http://qemu.org/
checkdep wget wget
if [ -n "$MISSING" ]; then
echo
[ -n "${FOUND}" ] && echo "found: ${FOUND}"
echo "install missing deps with:"
echo " apt-get update && apt-get install ${MISSING}"
else
echo "All good. (${FOUND})"
fi
# vi: ts=4 expandtab
#!/bin/sh
Usage() {
cat <<EOF
Usage: ${0##*/} release
release is like 'xenial' or 'artful'.
- Downloads an image from cloud-image.ubuntu.com
- converts it to raw format (from qcow2)
EOF
}
fail() { echo "$@" 1>&2; exit 1; }
rel="$1"
[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit 0; }
[ -n "$rel" ] || { Usage 1>&2; fail "Must give release"; }
arch="amd64"
burl="${_PROTO:-https}://cloud-images.ubuntu.com/daily/server"
fname=$rel-server-cloudimg-amd64.img
ofname="$fname"
# trusty and xenial have a '-disk1.img' while
# other releases have just 'disk.img'
case "$rel" in
precise|trusty|xenial) ofname="$rel-server-cloudimg-amd64-disk1.img";;
esac
raw="${fname%.img}.raw"
if [ ! -f "$fname" ]; then
url="$burl/$rel/current/$ofname"
echo "downloading $url to $fname"
wget "$url" -O "$fname.tmp" &&
mv "$fname.tmp" "$fname" || exit
rm -f "$raw"
fi
if [ ! -f "$raw" ]; then
echo "converting $fname to raw in $raw"
qemu-img convert -O raw "$fname" "$raw.tmp" &&
mv "$raw.tmp" "$raw" || exit
rm -f "$pfname"
fi
echo "dist: $ofname"
echo "raw: $raw"
# vi: ts=4 expandtab
instance-id: 506f2788-9741-4ed8-af53-c6a21383d09a
version: 1
config:
- type: physical
name: interface0
mac_address: "52:54:00:12:34:00"
subnets:
- type: static
address: 192.168.1.10
netmask: 255.255.255.0
gateway: 192.168.1.254
version: 2
ethernets:
interface0:
match:
mac_address: "52:54:00:12:34:00"
set-name: interface0
addresses:
- 192.168.1.10/255.255.255.0
gateway4: 192.168.1.254
#cloud-config
password: passw0rd
chpasswd: { expire: False }
ssh_pwauth: True
@jamesbeedy

This comment has been minimized.

Copy link

@jamesbeedy jamesbeedy commented Nov 4, 2017

@smoser this is great! thank you!

@brennancheung

This comment has been minimized.

Copy link

@brennancheung brennancheung commented Oct 13, 2019

Thank you! I wish I found this earlier. It's been nearly 2 days trying to get cloud-init working on my local Ubuntu box with kvm. It finally works. I didn't know I had to put networking in a different section and when building the seed.img.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.