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:
parent
66c3c7a419
commit
fa80b0cabe
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -44,3 +44,5 @@ class WelcomeController(ControllerPolicy):
|
||||||
return self.ui.next_controller()
|
return self.ui.next_controller()
|
||||||
|
|
||||||
__controller_class__ = WelcomeController
|
__controller_class__ = WelcomeController
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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']))
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
Loading…
Reference in New Issue