Add probert to subiquity

Update subiquity welcome controller to call probert to extract
host information and present it to the user briefly before
invokin the curtin installer.

Add the install_src to git clone probert.  Pack this as a tarball
in the user-data like subiquity.

Update user-data package installs for new probert deps.

Modify boot parameters to handle systemd persistent nic naming;
we may not always have eth0 as a nic.  Subsequently update
user-data to use bootcmd to automatically attempt to bring up
nics during the boot before the installer.

Finally fix commandline parsing/output in geninstaller.
Implement version parameter and better caching of previous
maas ephemeral images.

Fix issue with removing cache after unpacking rootfs which is
now owned by root (add sudo to rm of cache).

Signed-off-by: Ryan Harper <ryan.harper@canonical.com>
This commit is contained in:
Ryan Harper 2015-06-25 16:50:13 -05:00
parent 66c3c7a419
commit fa80b0cabe
9 changed files with 100 additions and 23 deletions

View File

@ -31,6 +31,7 @@ syslinux-common
grub2-common" grub2-common"
SRC_DEPS=( SRC_DEPS=(
"bzr" "lp:curtin" "bzr" "lp:curtin"
"git" "https://github.com/CanonicalLtd/probert.git"
) )
CACHEDIR="" CACHEDIR=""
@ -101,6 +102,8 @@ usage: $PROG [PARAMS] [ARGS]
-b, --bootloader=TYPE For TYPE in [syslinux, grub2, uboot] -b, --bootloader=TYPE For TYPE in [syslinux, grub2, uboot]
-h, --help This output. -h, --help This output.
-r, --release=RELEASE For RELEASE in [trusty, utopic, vivid, wily] -r, --release=RELEASE For RELEASE in [trusty, utopic, vivid, wily]
-s, --stream=STREAM For STREAM in [daily, released]
-V, --version=VERSION VERSION=YYYYMMDD , 20150623
-v, --verbose -v, --verbose
@ -194,7 +197,7 @@ acquire_image() {
local version=${4}; local version=${4};
# run a query unless they specify all params # run a query unless they specify all params
if [ $# -lt 5 ]; then if [ $# -le 4 -a -z "${version}" ]; then
log "Querying simplestreams for latest image: $label $release $arch" log "Querying simplestreams for latest image: $label $release $arch"
case "$label" in case "$label" in
daily) daily)
@ -226,12 +229,17 @@ acquire_image() {
# and re-assemble roottar and rootfs which was based on ephimg # and re-assemble roottar and rootfs which was based on ephimg
[ -r "$ephimg" ] && CACHE_SUM=( `sha256sum $ephimg 2>/dev/null` ) [ -r "$ephimg" ] && CACHE_SUM=( `sha256sum $ephimg 2>/dev/null` )
if [ -z "${sha256}" -a -f "${ephimg}.sha256" ]; then
log "Using sha256sum from cached file";
sha256=`cat ${ephimg}.sha256 | cut -d' ' -f1`
fi
if [ "${sha256}" != "${CACHE_SUM[0]}" ]; then if [ "${sha256}" != "${CACHE_SUM[0]}" ]; then
log "WARNING: sha256 csum mismatch" log "WARNING: sha256 csum mismatch"
log "WARNING: expected: [$sha256]" log "WARNING: expected: [$sha256]"
log " found: [${CACHE_SUM[0]}]" log " found: [${CACHE_SUM[0]}]"
# didn't match so nuke the cache # didn't match so nuke the cache
rm -fr "${cachedir}" || { log "WARNING: removing old cache"
sudo rm -fr "${cachedir}" || {
log "ERROR: failed to remove stale cachedir: $cachedir"; log "ERROR: failed to remove stale cachedir: $cachedir";
return 1; return 1;
} }
@ -245,6 +253,8 @@ acquire_image() {
log "ERROR: failed to download: ${item_url}"; log "ERROR: failed to download: ${item_url}";
return 1; return 1;
} }
(cd `dirname ${ephimg}` &&
sha256sum `basename ${ephimg}` > ${ephimg}.sha256)
else else
log " Using cached $label $release $arch $item_name:" log " Using cached $label $release $arch $item_name:"
log " $ephimg" log " $ephimg"
@ -328,7 +338,19 @@ generate_seed() {
log "Failed to subiquity into $seed"; log "Failed to subiquity into $seed";
return 1; return 1;
} }
log "Writing seed user-data (probert)"
local probert_tar=$dldir/probert.tar
(cd ${dldir}/probert.git &&
tar -cpf $probert_tar bin probert) || {
log "ERROR: Failed to package probert installer";
return 1;
}
userdata_write_file "/tmp/probert.tar" \
"root:root" "0644" "b64" \
"$probert_tar" >> $seed/user-data || {
log "Failed to subiquity into $seed";
return 1;
}
return 0 return 0
} }
@ -437,8 +459,8 @@ parse_args() {
# args: # args:
[ $# -lt 1 ] && { usage; exit 0; } [ $# -lt 1 ] && { usage; exit 0; }
OPTS_LONG="arch:bootloader:,download:help,release:,stream:verbose" OPTS_LONG="arch:,bootloader:,download:,help,release:,stream:,verbose,version:"
OPTS="a:b:d:h,r:s:v" OPTS="a:b:d:hr:s:vV:"
ARGS=`getopt --name "$PROG" --long $OPTS_LONG --options $OPTS -- "$@"` ARGS=`getopt --name "$PROG" --long $OPTS_LONG --options $OPTS -- "$@"`
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "$PROG: usage error (use -h for help)" >&2 echo "$PROG: usage error (use -h for help)" >&2
@ -452,6 +474,7 @@ parse_args() {
RELEASE="wily" RELEASE="wily"
STREAM="daily" STREAM="daily"
VERBOSE="no" VERBOSE="no"
VERSION=""
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case "$1" in
-a | --arch) ARCH="$2"; shift;; -a | --arch) ARCH="$2"; shift;;
@ -460,6 +483,7 @@ parse_args() {
-h | --help) usage; exit 0;; -h | --help) usage; exit 0;;
-r | --release) RELEASE="$2"; shift;; -r | --release) RELEASE="$2"; shift;;
-s | --stream) STREAM="$2"; shift;; -s | --stream) STREAM="$2"; shift;;
-V | --version) VERSION="$2"; shift;;
-v | --verbose) VERBOSE="yes";; -v | --verbose) VERBOSE="yes";;
--) shift; break;; # end of options --) shift; break;; # end of options
esac esac
@ -488,7 +512,7 @@ main() {
return 1; return 1;
} }
acquire_image ${DLDIR} "$STREAM" "$RELEASE" "$ARCH" || { acquire_image ${DLDIR} "$STREAM" "$RELEASE" "$ARCH" "$VERSION" || {
return 1; return 1;
} }
CACHEDIR=${_RETVAL} CACHEDIR=${_RETVAL}

View File

@ -9,9 +9,9 @@ label text-installer
menu label ^Ubuntu Server Installer (text) menu label ^Ubuntu Server Installer (text)
menu default menu default
linux /vmlinuz linux /vmlinuz
append initrd=/initrd.img ip=::::myhostname:BOOTIF ro root=LABEL=cloudimg-rootfs overlayroot=tmpfs BOOTIF_DEFAULT=eth0 console=ttyS0 console=tty0 splash append initrd=/initrd.img ip=dhcp ro root=LABEL=cloudimg-rootfs overlayroot=tmpfs console=ttyS0 console=tty0 splash
label serial-installer label serial-installer
menu label ^Ubuntu Server Installer (serial) menu label ^Ubuntu Server Installer (serial)
linux /vmlinuz linux /vmlinuz
append initrd=/initrd.img ip=::::myhostname:BOOTIF ro root=LABEL=cloudimg-rootfs overlayroot=tmpfs BOOTIF_DEFAULT=eth0 console=tty0 console=ttyS0 splash append initrd=/initrd.img ip=dhcp ro root=LABEL=cloudimg-rootfs overlayroot=tmpfs console=tty0 console=ttyS0 splash

View File

@ -1,13 +1,21 @@
#cloud-config #cloud-config
#http_proxy: http://my-proxy:3129/ #http_proxy: http://my-proxy:3129/
bootcmd:
- rm -f /etc/network/interfaces
- echo -e "auto lo\niface lo inet loopback\n" > /etc/network/interfaces
- /bin/ls -1 /sys/class/net | grep -v ^lo$ | xargs -i bash -c 'D={}; echo -e "auto $D\niface $D inet dhcp"' | tee /etc/network/interfaces
- /bin/ls -1 /sys/class/net | grep -v ^lo$ | xargs -i ifup {}
password: passw0rd password: passw0rd
chpasswd: { expire: False } chpasswd: { expire: False }
output: {all: '| tee -a /var/log/cloud-init-output.log'} output: {all: '| tee -a /var/log/cloud-init-output.log'}
packages: packages:
- python-urwid - python-urwid
- python3-urwid - python3-urwid
- python3-pyudev
- python3-netifaces
runcmd: runcmd:
- tar -C /usr/local -xf /tmp/subiquity.tar - tar -C /usr/local -xf /tmp/subiquity.tar
- tar -C /usr/local -xf /tmp/probert.tar
- (cd /usr/local && bin/curtin-archive extract --no-execute) - (cd /usr/local && bin/curtin-archive extract --no-execute)
- /tmp/installer.sh - /tmp/installer.sh
write_files: write_files:

View File

@ -41,7 +41,11 @@ fi
qemu-img create -f raw ${TARGET} 10G || exit 1 qemu-img create -f raw ${TARGET} 10G || exit 1
} }
# TODO, curses should work, but my xmonad setup blocks using it # TODO, curses should work, but my xmonad setup blocks using it
sudo qemu-system-$ARCH -m $MEM -enable-kvm -hda $INSTALLER -hdb $TARGET \ sudo qemu-system-$ARCH -smp 2 -m $MEM -enable-kvm -hda $INSTALLER -hdb $TARGET \
-net nic,model=e1000 \
-net nic,model=virtio \
-net nic,model=i82559er \
-net user \
-monitor telnet:127.0.0.1:2446,server,nowait -serial stdio -monitor telnet:127.0.0.1:2446,server,nowait -serial stdio
#sudo qemu-system-$ARCH -m $MEM -enable-kvm -hda $INSTALLER -hdb $TARGET \ #sudo qemu-system-$ARCH -m $MEM -enable-kvm -hda $INSTALLER -hdb $TARGET \
# -monitor telnet:127.0.0.1:2446,server,nowait -curses # -monitor telnet:127.0.0.1:2446,server,nowait -curses

View File

@ -44,3 +44,5 @@ class WelcomeController(ControllerPolicy):
return self.ui.next_controller() return self.ui.next_controller()
__controller_class__ = WelcomeController __controller_class__ = WelcomeController

View File

@ -21,20 +21,42 @@ configuration.
""" """
from subiquity import models from subiquity import models
import argparse
import math
import time
from probert import prober
class FilesystemModel(models.Model): class FilesystemModel(models.Model):
""" Model representing storage options """ Model representing storage options
""" """
available_disks = ['/dev/sda',
'/dev/sdb',
'/dev/sdc',
'/dev/sdd',
'/dev/sde']
additional_options = ['Connecti iSCSI network disk', additional_options = ['Connecti iSCSI network disk',
'Connect Ceph network disk', 'Connect Ceph network disk',
'Create volume group (LVM2)', 'Create volume group (LVM2)',
'Create software RAID (MD)', 'Create software RAID (MD)',
'Setup hierarchichal storage (bcache)'] 'Setup hierarchichal storage (bcache)']
def __init__(self):
self.storage= {}
self.options = argparse.Namespace(probe_storage=True, probe_network=False)
self.prober = prober.Prober(self.options)
def probe_storage(self):
self.prober.probe()
self.storage = self.prober.get_results().get('storage'}
def get_available_disks(self):
return [disk for disk in self.storage.keys()
if self.storage[disk]['DEVTYPE'] == 'disk' and
self.storage[disk]['MAJOR'] == '8']
def _humanize_size(self, size):
size = abs(size)
if (size==0):
return "0B"
units = ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']
p = math.floor(math.log(size, 2)/10)
return "%.3f %s" % (size/math.pow(1024,p),units[int(p)])
def get_disk_size(self, disk):
return self._humanize_size(int(self.storage[disk]['attrs']['size']))

View File

@ -20,16 +20,29 @@ Provides network device listings and extended network information
""" """
from subiquity import models from subiquity import models
import argparse
import math
import time
from probert import prober
class NetworkModel(models.Model): class NetworkModel(models.Model):
""" Model representing network interfaces """ Model representing network interfaces
""" """
interfaces = ['em1',
'em2',
'bond0']
additional_options = ['Set default route', additional_options = ['Set default route',
'Bond interfaces', 'Bond interfaces',
'Install network driver'] 'Install network driver']
def __init__(self):
self.hwdata = {}
self.options = argparse.Namespace(probe_storage=False, probe_network=True)
self.prober = prober.Prober(self.options)
def probe_network(self):
self.prober.probe()
self.hwdata = self.prober.get_results()
def get_interfaces(self):
return [n for n in self.hwdata['network'].keys()
if self.hwdata['network'][n]['type'] == 'eth']

View File

@ -46,11 +46,14 @@ class FilesystemView(WidgetWrap):
def _build_model_inputs(self): def _build_model_inputs(self):
sl = [] sl = []
for iface in self.model.available_disks: self.model.probe_storage()
sl.append(Color.button_primary(confirm_btn(label=iface, for disk in self.model.get_available_disks():
sl.append(Color.button_primary(confirm_btn(label=disk,
on_press=self.confirm), on_press=self.confirm),
focus_map='button_primary focus')) focus_map='button_primary focus'))
sl.append(Padding.push_10(Text("2 tb free"))) disk_sz = self.model.get_disk_size(disk)
sl.append(Padding.push_10(Text(disk_sz)))
return BoxAdapter(SimpleList(sl), return BoxAdapter(SimpleList(sl),
height=len(sl)) height=len(sl))

View File

@ -46,7 +46,8 @@ class NetworkView(WidgetWrap):
def _build_model_inputs(self): def _build_model_inputs(self):
sl = [] sl = []
for iface in self.model.interfaces: self.model.probe_network()
for iface in self.model.get_interfaces():
sl.append(Color.button_primary(confirm_btn(label=iface, sl.append(Color.button_primary(confirm_btn(label=iface,
on_press=self.confirm), on_press=self.confirm),
focus_map='button_primary focus')) focus_map='button_primary focus'))