From ec98a539b4a251266afdbbc6947ebd800762d89b Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 28 Jan 2011 17:42:27 -0800 Subject: [PATCH] merge premium xenserver scripts to oss --- .../hypervisor/xenserver/check_heartbeat.sh | 58 ++ .../copy_vhd_from_secondarystorage.sh | 118 +++ .../xenserver/copy_vhd_to_secondarystorage.sh | 106 +++ scripts/vm/hypervisor/xenserver/fsimage.so | Bin 0 -> 11220 bytes scripts/vm/hypervisor/xenserver/launch_hb.sh | 30 + .../vm/hypervisor/xenserver/setupXenServer.sh | 13 + .../xenserver/setup_heartbeat_file.sh | 72 ++ .../xenserver/setup_heartbeat_sr.sh | 88 ++ scripts/vm/hypervisor/xenserver/vmopspremium | 101 ++ .../vm/hypervisor/xenserver/xenheartbeat.sh | 64 ++ .../xenserver56/InterfaceReconfigure.py | 870 ++++++++++++++++++ .../vm/hypervisor/xenserver/xenserver56/patch | 11 + .../hypervisor/xenserver/xenserver56fp1/patch | 9 + scripts/vm/hypervisor/xenserver/xs_cleanup.sh | 54 ++ 14 files changed, 1594 insertions(+) create mode 100755 scripts/vm/hypervisor/xenserver/check_heartbeat.sh create mode 100755 scripts/vm/hypervisor/xenserver/copy_vhd_from_secondarystorage.sh create mode 100755 scripts/vm/hypervisor/xenserver/copy_vhd_to_secondarystorage.sh create mode 100755 scripts/vm/hypervisor/xenserver/fsimage.so create mode 100755 scripts/vm/hypervisor/xenserver/launch_hb.sh create mode 100755 scripts/vm/hypervisor/xenserver/setupXenServer.sh create mode 100755 scripts/vm/hypervisor/xenserver/setup_heartbeat_file.sh create mode 100755 scripts/vm/hypervisor/xenserver/setup_heartbeat_sr.sh create mode 100755 scripts/vm/hypervisor/xenserver/vmopspremium create mode 100755 scripts/vm/hypervisor/xenserver/xenheartbeat.sh create mode 100755 scripts/vm/hypervisor/xenserver/xenserver56/InterfaceReconfigure.py create mode 100755 scripts/vm/hypervisor/xenserver/xs_cleanup.sh diff --git a/scripts/vm/hypervisor/xenserver/check_heartbeat.sh b/scripts/vm/hypervisor/xenserver/check_heartbeat.sh new file mode 100755 index 00000000000..2f5b40022c3 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/check_heartbeat.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s [uuid of this host] [interval in seconds]\n" $(basename $0) >&2 + +} + +if [ -z $1 ]; then + usage + exit 2 +fi + +if [ -z $2 ]; then + usage + exit 3 +fi + + +date=`date +%s` +hbs=`lvscan | grep hb-$1 | awk '{print $2}'` +for hb in $hbs +do + hb=${hb:1:`expr ${#hb} - 2`} + active=`lvscan | grep $hb | awk '{print $1}'` + if [ "$active" == "inactive" ]; then + lvchange -ay $hb + if [ ! -L $hb ]; then + continue; + fi + fi + ping=`dd if=$hb bs=1 count=100` + if [ $? -ne 0 ]; then + continue; + fi + diff=`expr $date - $ping` + if [ $diff -lt $2 ]; then + echo "=====> ALIVE <=====" + exit 0; + fi +done + +hbs=`ls -l /var/run/sr-mount/*/hb-$1 | awk '{print $9}'` +for hb in $hbs +do + ping=`cat $hb` + if [ $? -ne 0 ]; then + continue; + fi + diff=`expr $date - $ping` + if [ $diff -lt $2 ]; then + echo "=====> ALIVE <=====" + exit 0; + fi +done + +echo "=====> DEAD <======" diff --git a/scripts/vm/hypervisor/xenserver/copy_vhd_from_secondarystorage.sh b/scripts/vm/hypervisor/xenserver/copy_vhd_from_secondarystorage.sh new file mode 100755 index 00000000000..5760d5dffe3 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/copy_vhd_from_secondarystorage.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s [mountpoint in secondary storage] [uuid of the source sr]\n" $(basename $0) +} + +cleanup() +{ + if [ ! -z $localmp ]; then + umount $localmp + if [ $? -eq 0 ]; then + rm $localmp -rf + fi + fi +} + +if [ -z $1 ]; then + usage + echo "2#no mountpoint" + exit 0 +else + mountpoint=$1 +fi + +if [ -z $2 ]; then + usage + echo "3#no uuid of the source sr" + exit 0 +else + sruuid=$2 +fi + +type=$(xe sr-param-get uuid=$sruuid param-name=type) +if [ $? -ne 0 ]; then + echo "4#sr $sruuid doesn't exist" + exit 0 +fi + +localmp=/var/run/cloud_mount/$(uuidgen -r) + +mkdir -p $localmp +if [ $? -ne 0 ]; then + echo "5#cann't make dir $localmp" + exit 0 +fi + +mount $mountpoint $localmp +if [ $? -ne 0 ]; then + echo "6#cann't mounbt $mountpoint to $localmp" + exit 0 +fi + +vhdfile=$(ls $localmp/*.vhd) +if [ $? -ne 0 ]; then + echo "7#There is no vhd file under $mountpoint" + cleanup + exit 0 +fi + +if [ $type == "nfs" ]; then + uuid=$(uuidgen -r) + dd if=$vhdfile of=/var/run/sr-mount/$sruuid/$uuid bs=2M + if [ $? -ne 0 ]; then + echo "8#failed ot copy vhdfile to /var/run/sr-mount/sruuid/$uuid" + cleanup + exit 0 + fi + mv /var/run/sr-mount/$sruuid/$uuid /var/run/sr-mount/$sruuid/${uuid}.vhd + xe sr-scan uuid=$sruuid +elif [ $type == "lvmoiscsi" -o $type == "lvm" ]; then + size=$(vhd-util query -v -n $vhdfile) + uuid=$(xe vdi-create sr-uuid=$sruuid virtual-size=${size}MiB type=user name-label="cloud") + if [ $? -ne 0 ]; then + echo "9#can not create vdi in sr $sruuid" + cleanup + exit 0 + fi + lvchange -ay /dev/VG_XenStorage-$sruuid/VHD-$uuid + if [ $? -ne 0 ]; then + echo "10#lvm can not make VDI $uuid visiable" + cleanup + exit 0 + fi + dd if=$vhdfile of=/dev/VG_XenStorage-$sruuid/VHD-$uuid bs=2M + if [ $? -ne 0 ]; then + echo "11#failed to dd to sr $sruuid" + cleanup + exit 0 + fi + lvsize=$(xe vdi-param-get uuid=$uuid param-name=physical-utilisation) + if [ $? -ne 0 ]; then + echo "12#failed to get physical size of vdi $uuid" + cleanup + exit 0 + fi + vhd-util modify -s $lvsize -n /dev/VG_XenStorage-$sruuid/VHD-$uuid + if [ $? -ne 0 ]; then + echo "13#failed to set new vhd physical size for vdi vdi $uuid" + cleanup + exit 0 + fi + xe sr-scan uuid=$sruuid + if [ $? -ne 0 ]; then + echo "14#failed to scan sr $sruuid" + cleanup + exit 0 + fi +else + echo "15#doesn't support sr type $type" + cleanup + exit 0 +fi + +echo "0#$uuid" +cleanup +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/copy_vhd_to_secondarystorage.sh b/scripts/vm/hypervisor/xenserver/copy_vhd_to_secondarystorage.sh new file mode 100755 index 00000000000..65800dbe994 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/copy_vhd_to_secondarystorage.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s [mountpoint in secondary storage] [uuid of the source vdi] [uuid of the source sr]\n" $(basename $0) +} + +cleanup() +{ + if [ ! -z $localmp ]; then + umount $localmp + if [ $? -eq 0 ]; then + rm $localmp -rf + fi + fi +} + +if [ -z $1 ]; then + usage + echo "1#no mountpoint" + exit 0 +else + mountpoint=$1 +fi + +if [ -z $2 ]; then + usage + echo "2#no uuid of the source sr" + exit 0 +else + vdiuuid=$2 +fi + + +if [ -z $3 ]; then + usage + echo "3#no uuid of the source sr" + exit 0 +else + sruuid=$3 +fi + +type=$(xe sr-param-get uuid=$sruuid param-name=type) +if [ $? -ne 0 ]; then + echo "4#sr $sruuid doesn't exist" + exit 0 +fi + +localmp=/var/run/cloud_mount/$(uuidgen -r) + +mkdir -p $localmp +if [ $? -ne 0 ]; then + echo "5#cann't make dir $localmp" + exit 0 +fi + +mount $mountpoint $localmp +if [ $? -ne 0 ]; then + echo "6#cann't mounbt $mountpoint to $localmp" + exit 0 +fi + +vhdfile=$localmp/${vdiuuid}.vhd + +if [ $type == "nfs" ]; then + dd if=/var/run/sr-mount/$sruuid/${vdiuuid}.vhd of=$vhdfile bs=2M + if [ $? -ne 0 ]; then + echo "8#failed to copy /var/run/sr-mount/$sruuid/${vdiuuid}.vhd to secondarystorage" + cleanup + exit 0 + fi +elif [ $type == "lvmoiscsi" -o $type == "lvm" ]; then + lvchange -ay /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid + if [ $? -ne 0 ]; then + echo "9#lvm can not make VDI $vdiuuid visiable" + cleanup + exit 0 + fi + size=$(vhd-util query -s -n /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid) + if [ $? -ne 0 ]; then + echo "10#can not get physical size of /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid" + cleanup + exit 0 + fi +#in 2M unit + size=$((size>>21)) + size=$((size+1)) + dd if=/dev/VG_XenStorage-$sruuid/VHD-$vdiuuid of=$vhdfile bs=2M count=$size +#in byte unit + size=$((size<<21)) + vhd-util modify -s $size -n $vhdfile + if [ $? -ne 0 ]; then + echo "11#failed to change $vhdfile physical size" + cleanup + exit 0 + fi +else + echo "15#doesn't support sr type $type" + cleanup + exit 0 +fi + +echo "0#$vdiuuid" +cleanup +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/fsimage.so b/scripts/vm/hypervisor/xenserver/fsimage.so new file mode 100755 index 0000000000000000000000000000000000000000..05e0d041d916d32db1949d763de6e13395127ea4 GIT binary patch literal 11220 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 E0LMj!Y5)KL literal 0 HcmV?d00001 diff --git a/scripts/vm/hypervisor/xenserver/launch_hb.sh b/scripts/vm/hypervisor/xenserver/launch_hb.sh new file mode 100755 index 00000000000..b2b4b994528 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/launch_hb.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s [uuid of this host] [interval in seconds]\n" $(basename $0) + +} + +if [ -z $1 ]; then + usage + exit 2 +fi + +if [ -z $2 ]; then + usage + exit 3 +fi + +if [ ! -f /opt/xensource/bin/xenheartbeat.sh ]; then + printf "Error: Unable to find xenheartbeat.sh to launch\n" + exit 4 +fi + +for psid in `ps -ef | grep xenheartbeat | grep -v grep | awk '{print $2}'`; do + kill $psid +done + +nohup /opt/xensource/bin/xenheartbeat.sh $1 $2 >/dev/null 2>/dev/null & +echo "======> DONE <======" diff --git a/scripts/vm/hypervisor/xenserver/setupXenServer.sh b/scripts/vm/hypervisor/xenserver/setupXenServer.sh new file mode 100755 index 00000000000..78743d644ca --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/setupXenServer.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# avoid disk full +mv /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 + +# more aio thread +echo 1048576 >/proc/sys/fs/aio-max-nr + +# empty heartbeat +cat /dev/null > /opt/xensource/bin/heartbeat + +echo "success" + diff --git a/scripts/vm/hypervisor/xenserver/setup_heartbeat_file.sh b/scripts/vm/hypervisor/xenserver/setup_heartbeat_file.sh new file mode 100755 index 00000000000..42ff965e5be --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/setup_heartbeat_file.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s [uuid of this host] [uuid of the sr to place the heartbeat]\n" $(basename $0) + +} + + +if [ -z $1 ]; then + usage + echo "1# no uuid of host" + exit 0 +fi + +if [ -z $2 ]; then + usage + echo "2# no uuid of sr" + exit 0 +fi + +if [ `xe host-list | grep $1 | wc -l` -ne 1 ]; then + echo "3# Unable to find the host uuid: $1" + exit 0 +fi + +if [ `xe sr-list uuid=$2 | wc -l` -eq 0 ]; then + echo "4# Unable to find SR with uuid: $2" + exit 0 +fi + +if [ `xe pbd-list sr-uuid=$2 | grep -B 1 $1 | wc -l` -eq 0 ]; then + echo "5# Unable to find a pbd for the SR: $2" + exit 0 +fi + +srtype=`xe sr-param-get param-name=type uuid=$2` + + +if [ "$srtype" = "nfs" ];then + dir=/var/run/sr-mount/$2 + filename=$dir/hb-$1 + if [ ! -f "$filename" ]; then + echo "6# heartbeat file $filename doesn't exist" + exit 0 + fi +else + dir=/dev/VG_XenStorage-$2 + link=$dir/hb-$1 + lvchange -ay $link + if [ $? -ne 0 ]; then + echo "7# Unable to make the heartbeat $link active" + exit 0 + fi +fi + +hbfile=/opt/xensource/bin/heartbeat + +if [ -f $hbfile ]; then + grep $dir $hbfile >/dev/null + if [ $? -gt 0 ] + then + echo $dir >> $hbfile + fi +else + echo $dir >> $hbfile +fi + +echo "0#DONE" + +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/setup_heartbeat_sr.sh b/scripts/vm/hypervisor/xenserver/setup_heartbeat_sr.sh new file mode 100755 index 00000000000..0408c417e90 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/setup_heartbeat_sr.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +#set -x + +usage() { + echo "Usage: $(basename $0) [uuid of this host] [uuid of the sr to place the heartbeat]" + +} + +if [ -z $1 ]; then + usage + echo "1#no host uuid" + exit 0 +fi + +if [ -z $2 ]; then + usage + echo "2#no sr uuid" + exit 0 +fi + +if [ `xe host-list | grep $1 | wc -l` -ne 1 ]; then + echo "3# Unable to find the host uuid: $1" + exit 0 +fi + +if [ `xe sr-list uuid=$2 | wc -l` -eq 0 ]; then + echo "4# Unable to find SR with uuid: $2" + exit 0 +fi + +if [ `xe pbd-list sr-uuid=$2 | grep -B 1 $1 | wc -l` -eq 0 ]; then + echo "5# Unable to find a pbd for the SR: $2" + exit 0 +fi + +srtype=`xe sr-param-get param-name=type uuid=$2` + +if [ "$srtype" == "nfs" ];then + dir=/var/run/sr-mount/$2 + filename=$dir/hb-$1 + files=`ls $dir | grep "hb-$1"` + if [ -z "$files" ]; then + date=`date +%s` + echo "$date" > $filename + fi +else + dir=/dev/VG_XenStorage-$2 + link=$dir/hb-$1 + lv=`lvscan | grep $link` + if [ -z "$lv" ]; then + if [ -e $link ]; then + devmapper=$(ls $link -l | awk '{print $NF}') + if [ -e $devmapper ]; then + dmsetup remove -f $devmapper + fi + rm $link -f + fi + lvcreate VG_XenStorage-$2 -n hb-$1 --size 4M + if [ $? -ne 0 ]; then + echo "6# Unable to create heartbeat volume hb-$1" + exit 0 + fi + lv=`lvscan | grep $link` + if [ -z "$lv" ]; then + echo "7# volume hb-$1 is not created" + exit 0 + fi + fi + + if [ `echo $lv | awk '{print $1}'` == "inactive" ]; then + lvchange -ay $link + if [ $? -ne 0 ]; then + echo "8# Unable to make $link active" + exit 0 + fi + fi + + if [ ! -L $link ]; then + echo "9# Unable to find the soft link $link" + exit 0 + fi + dd if=/dev/zero of=$link bs=1 count=100 +fi + +echo "0#DONE" + +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/vmopspremium b/scripts/vm/hypervisor/xenserver/vmopspremium new file mode 100755 index 00000000000..5fada9eb5f9 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/vmopspremium @@ -0,0 +1,101 @@ +#!/usr/bin/python +# +# A plugin for executing script needed by vmops cloud + +import os, sys, time +import XenAPIPlugin +sys.path.append("/opt/xensource/sm/") +import util +import socket + +def echo(fn): + def wrapped(*v, **k): + name = fn.__name__ + util.SMlog("#### VMOPS enter %s ####" % name ) + res = fn(*v, **k) + util.SMlog("#### VMOPS exit %s ####" % name ) + return res + return wrapped + +@echo +def copy_vhd_to_secondarystorage(session, args): + mountpoint = args['mountpoint'] + vdiuuid = args['vdiuuid'] + sruuid = args['sruuid'] + try: + cmd = ["bash", "/opt/xensource/bin/copy_vhd_to_secondarystorage.sh", mountpoint, vdiuuid, sruuid] + txt = util.pread2(cmd) + except: + txt = '10#failed' + return txt + +@echo +def copy_vhd_from_secondarystorage(session, args): + mountpoint = args['mountpoint'] + sruuid = args['sruuid'] + try: + cmd = ["bash", "/opt/xensource/bin/copy_vhd_from_secondarystorage.sh", mountpoint, sruuid] + txt = util.pread2(cmd) + except: + txt = '10#failed' + return txt + +@echo +def setup_heartbeat_sr(session, args): + host = args['host'] + sr = args['sr'] + try: + cmd = ["bash", "/opt/xensource/bin/setup_heartbeat_sr.sh", host, sr] + txt = util.pread2(cmd) + except: + txt = '' + return txt + +@echo +def setup_heartbeat_file(session, args): + host = args['host'] + sr = args['sr'] + try: + cmd = ["bash", "/opt/xensource/bin/setup_heartbeat_file.sh", host, sr] + txt = util.pread2(cmd) + except: + txt = '' + return txt + +@echo +def check_heartbeat(session, args): + host = args['host'] + interval = args['interval'] + try: + cmd = ["bash", "/opt/xensource/bin/check_heartbeat.sh", host, interval] + txt = util.pread2(cmd) + except: + txt='' + return txt + + +@echo +def heartbeat(session, args): + host = args['host'] + interval = args['interval'] + try: + cmd = ["/bin/bash", "/opt/xensource/bin/launch_hb.sh", host, interval] + txt = util.pread2(cmd) + except: + txt='fail' + return txt + + +@echo +def setupXenServer(session, args): + try: + cmd = ["/bin/bash", "/opt/xensource/bin/setupXenServer.sh"] + txt = util.pread2(cmd) + return txt + except: + raise xs_errors.XenError('setupXenServer.sh execution failed.') + + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"copy_vhd_to_secondarystorage":copy_vhd_to_secondarystorage, "copy_vhd_from_secondarystorage":copy_vhd_from_secondarystorage, "setup_heartbeat_sr":setup_heartbeat_sr, "setup_heartbeat_file":setup_heartbeat_file, "check_heartbeat":check_heartbeat, "heartbeat": heartbeat, "setupXenServer":setupXenServer}) + diff --git a/scripts/vm/hypervisor/xenserver/xenheartbeat.sh b/scripts/vm/hypervisor/xenserver/xenheartbeat.sh new file mode 100755 index 00000000000..2ba79a21b12 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenheartbeat.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Version @VERSION@ + +#set -x + +usage() { + printf "Usage: %s [uuid of this host] [interval in seconds]\n" $(basename $0) >&2 + +} + +if [ -z $1 ]; then + usage + exit 2 +fi + +if [ -z $2 ]; then + usage + exit 3 +fi + +file=/opt/xensource/bin/heartbeat +while true +do + sleep $2 + + if [ ! -f $file ] + then + continue + fi + + # for iscsi + dirs=$(cat $file | grep VG_XenStorage) + for dir in $dirs + do + if [ -d $dir ]; then + hb=$dir/hb-$1 + date +%s | dd of=$hb count=100 bs=1 2>/dev/null + if [ $? -ne 0 ]; then + /usr/bin/logger -t heartbeat "Problem with $hb" + reboot -f + fi + else + sed -i /${dir##/*/}/d $file + fi + done + # for nfs + dirs=$(cat $file | grep sr-mount) + for dir in $dirs + do + mp=`mount | grep $dir` + if [ -n "$mp" ]; then + hb=$dir/hb-$1 + date +%s | dd of=$hb count=100 bs=1 2>/dev/null + if [ $? -ne 0 ]; then + /usr/bin/logger -t heartbeat "Problem with $hb" + reboot -f + fi + else + sed -i /${dir##/*/}/d $file + fi + done + +done + diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/InterfaceReconfigure.py b/scripts/vm/hypervisor/xenserver/xenserver56/InterfaceReconfigure.py new file mode 100755 index 00000000000..9723c6617eb --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenserver56/InterfaceReconfigure.py @@ -0,0 +1,870 @@ +# Copyright (c) 2008,2009 Citrix Systems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; version 2.1 only. with the special +# exception on linking described in file LICENSE. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +import sys +import syslog +import os + +from xml.dom.minidom import getDOMImplementation +from xml.dom.minidom import parse as parseXML + +the_root_prefix = "" +def root_prefix(): + """Returns a string to prefix to all file name references, which + is useful for testing.""" + return the_root_prefix +def set_root_prefix(prefix): + global the_root_prefix + the_root_prefix = prefix + +log_destination = "syslog" +def get_log_destination(): + """Returns the current log destination. + 'syslog' means "log to syslog". + 'stderr' means "log to stderr".""" + return log_destination +def set_log_destination(dest): + global log_destination + log_destination = dest + +# +# Logging. +# + +def log(s): + if get_log_destination() == 'syslog': + syslog.syslog(s) + else: + print >>sys.stderr, s + +# +# Exceptions. +# + +class Error(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.msg = msg + +# +# Run external utilities +# + +def run_command(command): + log("Running command: " + ' '.join(command)) + rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command) + if rc != 0: + log("Command failed %d: " % rc + ' '.join(command)) + return False + return True + +# +# Configuration File Handling. +# + +class ConfigurationFile(object): + """Write a file, tracking old and new versions. + + Supports writing a new version of a file and applying and + reverting those changes. + """ + + __STATE = {"OPEN":"OPEN", + "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED", + "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"} + + def __init__(self, path): + dirname,basename = os.path.split(path) + + self.__state = self.__STATE['OPEN'] + self.__children = [] + + self.__path = os.path.join(dirname, basename) + self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old") + self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new") + + self.__f = open(self.__newpath, "w") + + def attach_child(self, child): + self.__children.append(child) + + def path(self): + return self.__path + + def readlines(self): + try: + return open(self.path()).readlines() + except: + return "" + + def write(self, args): + if self.__state != self.__STATE['OPEN']: + raise Error("Attempt to write to file in state %s" % self.__state) + self.__f.write(args) + + def close(self): + if self.__state != self.__STATE['OPEN']: + raise Error("Attempt to close file in state %s" % self.__state) + + self.__f.close() + self.__state = self.__STATE['NOT-APPLIED'] + + def changed(self): + if self.__state != self.__STATE['NOT-APPLIED']: + raise Error("Attempt to compare file in state %s" % self.__state) + + return True + + def apply(self): + if self.__state != self.__STATE['NOT-APPLIED']: + raise Error("Attempt to apply configuration from state %s" % self.__state) + + for child in self.__children: + child.apply() + + log("Applying changes to %s configuration" % self.__path) + + # Remove previous backup. + if os.access(self.__oldpath, os.F_OK): + os.unlink(self.__oldpath) + + # Save current configuration. + if os.access(self.__path, os.F_OK): + os.link(self.__path, self.__oldpath) + os.unlink(self.__path) + + # Apply new configuration. + assert(os.path.exists(self.__newpath)) + os.link(self.__newpath, self.__path) + + # Remove temporary file. + os.unlink(self.__newpath) + + self.__state = self.__STATE['APPLIED'] + + def revert(self): + if self.__state != self.__STATE['APPLIED']: + raise Error("Attempt to revert configuration from state %s" % self.__state) + + for child in self.__children: + child.revert() + + log("Reverting changes to %s configuration" % self.__path) + + # Remove existing new configuration + if os.access(self.__newpath, os.F_OK): + os.unlink(self.__newpath) + + # Revert new configuration. + if os.access(self.__path, os.F_OK): + os.link(self.__path, self.__newpath) + os.unlink(self.__path) + + # Revert to old configuration. + if os.access(self.__oldpath, os.F_OK): + os.link(self.__oldpath, self.__path) + os.unlink(self.__oldpath) + + # Leave .*.xapi-new as an aid to debugging. + + self.__state = self.__STATE['REVERTED'] + + def commit(self): + if self.__state != self.__STATE['APPLIED']: + raise Error("Attempt to commit configuration from state %s" % self.__state) + + for child in self.__children: + child.commit() + + log("Committing changes to %s configuration" % self.__path) + + if os.access(self.__oldpath, os.F_OK): + os.unlink(self.__oldpath) + if os.access(self.__newpath, os.F_OK): + os.unlink(self.__newpath) + + self.__state = self.__STATE['COMMITTED'] + +# +# Helper functions for encoding/decoding database attributes to/from XML. +# + +def _str_to_xml(xml, parent, tag, val): + e = xml.createElement(tag) + parent.appendChild(e) + v = xml.createTextNode(val) + e.appendChild(v) +def _str_from_xml(n): + def getText(nodelist): + rc = "" + for node in nodelist: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc + return getText(n.childNodes).strip() + +def _bool_to_xml(xml, parent, tag, val): + if val: + _str_to_xml(xml, parent, tag, "True") + else: + _str_to_xml(xml, parent, tag, "False") +def _bool_from_xml(n): + s = _str_from_xml(n) + if s == "True": + return True + elif s == "False": + return False + else: + raise Error("Unknown boolean value %s" % s) + +def _strlist_to_xml(xml, parent, ltag, itag, val): + e = xml.createElement(ltag) + parent.appendChild(e) + for v in val: + c = xml.createElement(itag) + e.appendChild(c) + cv = xml.createTextNode(v) + c.appendChild(cv) +def _strlist_from_xml(n, ltag, itag): + ret = [] + for n in n.childNodes: + if n.nodeName == itag: + ret.append(_str_from_xml(n)) + return ret + +def _otherconfig_to_xml(xml, parent, val, attrs): + otherconfig = xml.createElement("other_config") + parent.appendChild(otherconfig) + for n,v in val.items(): + if not n in attrs: + raise Error("Unknown other-config attribute: %s" % n) + _str_to_xml(xml, otherconfig, n, v) +def _otherconfig_from_xml(n, attrs): + ret = {} + for n in n.childNodes: + if n.nodeName in attrs: + ret[n.nodeName] = _str_from_xml(n) + return ret + +# +# Definitions of the database objects (and their attributes) used by interface-reconfigure. +# +# Each object is defined by a dictionary mapping an attribute name in +# the xapi database to a tuple containing two items: +# - a function which takes this attribute and encodes it as XML. +# - a function which takes XML and decocdes it into a value. +# +# other-config attributes are specified as a simple array of strings + +_PIF_XML_TAG = "pif" +_VLAN_XML_TAG = "vlan" +_BOND_XML_TAG = "bond" +_NETWORK_XML_TAG = "network" + +_ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ] + +_PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \ + [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \ + _ETHTOOL_OTHERCONFIG_ATTRS + +_PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), + 'management': (_bool_to_xml,_bool_from_xml), + 'network': (_str_to_xml,_str_from_xml), + 'device': (_str_to_xml,_str_from_xml), + 'bond_master_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'bond_master_of', 'slave', v), + lambda n: _strlist_from_xml(n, 'bond_master_of', 'slave')), + 'bond_slave_of': (_str_to_xml,_str_from_xml), + 'VLAN': (_str_to_xml,_str_from_xml), + 'VLAN_master_of': (_str_to_xml,_str_from_xml), + 'VLAN_slave_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v), + lambda n: _strlist_from_xml(n, 'VLAN_slave_Of', 'master')), + 'ip_configuration_mode': (_str_to_xml,_str_from_xml), + 'IP': (_str_to_xml,_str_from_xml), + 'netmask': (_str_to_xml,_str_from_xml), + 'gateway': (_str_to_xml,_str_from_xml), + 'DNS': (_str_to_xml,_str_from_xml), + 'MAC': (_str_to_xml,_str_from_xml), + 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _PIF_OTHERCONFIG_ATTRS), + lambda n: _otherconfig_from_xml(n, _PIF_OTHERCONFIG_ATTRS)), + + # Special case: We write the current value + # PIF.currently-attached to the cache but since it will + # not be valid when we come to use the cache later + # (i.e. after a reboot) we always read it as False. + 'currently_attached': (_bool_to_xml, lambda n: False), + } + +_VLAN_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), + 'tagged_PIF': (_str_to_xml,_str_from_xml), + 'untagged_PIF': (_str_to_xml,_str_from_xml), + } + +_BOND_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), + 'master': (_str_to_xml,_str_from_xml), + 'slaves': (lambda x, p, t, v: _strlist_to_xml(x, p, 'slaves', 'slave', v), + lambda n: _strlist_from_xml(n, 'slaves', 'slave')), + } + +_NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + _ETHTOOL_OTHERCONFIG_ATTRS + +_NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), + 'bridge': (_str_to_xml,_str_from_xml), + 'MTU': (_str_to_xml,_str_from_xml), + 'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v), + lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')), + 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS), + lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)), + } + +# +# Database Cache object +# + +_db = None + +def db(): + assert(_db is not None) + return _db + +def db_init_from_cache(cache): + global _db + assert(_db is None) + _db = DatabaseCache(cache_file=cache) + +def db_init_from_xenapi(session): + global _db + assert(_db is None) + _db = DatabaseCache(session_ref=session) + +class DatabaseCache(object): + def __read_xensource_inventory(self): + filename = root_prefix() + "/etc/xensource-inventory" + f = open(filename, "r") + lines = [x.strip("\n") for x in f.readlines()] + f.close() + + defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ] + defs = [ (a, b.strip("'")) for (a,b) in defs ] + + return dict(defs) + + def __pif_on_host(self,pif): + return self.__pifs.has_key(pif) + + def __get_pif_records_from_xapi(self, session, host): + self.__pifs = {} + for (p,rec) in session.xenapi.PIF.get_all_records().items(): + if rec['host'] != host: + continue + self.__pifs[p] = {} + for f in _PIF_ATTRS: + self.__pifs[p][f] = rec[f] + self.__pifs[p]['other_config'] = {} + for f in _PIF_OTHERCONFIG_ATTRS: + if not rec['other_config'].has_key(f): continue + self.__pifs[p]['other_config'][f] = rec['other_config'][f] + + def __get_vlan_records_from_xapi(self, session): + self.__vlans = {} + for v in session.xenapi.VLAN.get_all(): + rec = session.xenapi.VLAN.get_record(v) + if not self.__pif_on_host(rec['untagged_PIF']): + continue + self.__vlans[v] = {} + for f in _VLAN_ATTRS: + self.__vlans[v][f] = rec[f] + + def __get_bond_records_from_xapi(self, session): + self.__bonds = {} + for b in session.xenapi.Bond.get_all(): + rec = session.xenapi.Bond.get_record(b) + if not self.__pif_on_host(rec['master']): + continue + self.__bonds[b] = {} + for f in _BOND_ATTRS: + self.__bonds[b][f] = rec[f] + + def __get_network_records_from_xapi(self, session): + self.__networks = {} + for n in session.xenapi.network.get_all(): + rec = session.xenapi.network.get_record(n) + self.__networks[n] = {} + for f in _NETWORK_ATTRS: + if f == "PIFs": + # drop PIFs on other hosts + self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)] + elif f == "MTU" and f not in rec: + # XenServer 5.5 network records did not have an + # MTU field, so allow this to be missing. + pass + else: + self.__networks[n][f] = rec[f] + self.__networks[n]['other_config'] = {} + for f in _NETWORK_OTHERCONFIG_ATTRS: + if not rec['other_config'].has_key(f): continue + self.__networks[n]['other_config'][f] = rec['other_config'][f] + + def __to_xml(self, xml, parent, key, ref, rec, attrs): + """Encode a database object as XML""" + e = xml.createElement(key) + parent.appendChild(e) + if ref: + e.setAttribute('ref', ref) + + for n,v in rec.items(): + if attrs.has_key(n): + h,_ = attrs[n] + h(xml, e, n, v) + else: + raise Error("Unknown attribute %s" % n) + def __from_xml(self, e, attrs): + """Decode a database object from XML""" + ref = e.attributes['ref'].value + rec = {} + for n in e.childNodes: + if n.nodeName in attrs: + _,h = attrs[n.nodeName] + rec[n.nodeName] = h(n) + return (ref,rec) + + def __init__(self, session_ref=None, cache_file=None): + if session_ref and cache_file: + raise Error("can't specify session reference and cache file") + if cache_file == None: + import XenAPI + session = XenAPI.xapi_local() + + if not session_ref: + log("No session ref given on command line, logging in.") + session.xenapi.login_with_password("root", "") + else: + session._session = session_ref + + try: + + inventory = self.__read_xensource_inventory() + assert(inventory.has_key('INSTALLATION_UUID')) + log("host uuid is %s" % inventory['INSTALLATION_UUID']) + + host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID']) + + self.__get_pif_records_from_xapi(session, host) + + self.__get_vlan_records_from_xapi(session) + self.__get_bond_records_from_xapi(session) + self.__get_network_records_from_xapi(session) + finally: + if not session_ref: + session.xenapi.session.logout() + else: + log("Loading xapi database cache from %s" % cache_file) + + xml = parseXML(root_prefix() + cache_file) + + self.__pifs = {} + self.__bonds = {} + self.__vlans = {} + self.__networks = {} + + assert(len(xml.childNodes) == 1) + toplevel = xml.childNodes[0] + + assert(toplevel.nodeName == "xenserver-network-configuration") + + for n in toplevel.childNodes: + if n.nodeName == "#text": + pass + elif n.nodeName == _PIF_XML_TAG: + (ref,rec) = self.__from_xml(n, _PIF_ATTRS) + self.__pifs[ref] = rec + elif n.nodeName == _BOND_XML_TAG: + (ref,rec) = self.__from_xml(n, _BOND_ATTRS) + self.__bonds[ref] = rec + elif n.nodeName == _VLAN_XML_TAG: + (ref,rec) = self.__from_xml(n, _VLAN_ATTRS) + self.__vlans[ref] = rec + elif n.nodeName == _NETWORK_XML_TAG: + (ref,rec) = self.__from_xml(n, _NETWORK_ATTRS) + self.__networks[ref] = rec + else: + raise Error("Unknown XML element %s" % n.nodeName) + + def save(self, cache_file): + + xml = getDOMImplementation().createDocument( + None, "xenserver-network-configuration", None) + for (ref,rec) in self.__pifs.items(): + self.__to_xml(xml, xml.documentElement, _PIF_XML_TAG, ref, rec, _PIF_ATTRS) + for (ref,rec) in self.__bonds.items(): + self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS) + for (ref,rec) in self.__vlans.items(): + self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS) + for (ref,rec) in self.__networks.items(): + self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec, + _NETWORK_ATTRS) + + f = open(cache_file, 'w') + f.write(xml.toprettyxml()) + f.close() + + def get_pif_by_uuid(self, uuid): + pifs = map(lambda (ref,rec): ref, + filter(lambda (ref,rec): uuid == rec['uuid'], + self.__pifs.items())) + if len(pifs) == 0: + raise Error("Unknown PIF \"%s\"" % uuid) + elif len(pifs) > 1: + raise Error("Non-unique PIF \"%s\"" % uuid) + + return pifs[0] + + def get_pifs_by_device(self, device): + return map(lambda (ref,rec): ref, + filter(lambda (ref,rec): rec['device'] == device, + self.__pifs.items())) + + def get_pif_by_bridge(self, bridge): + networks = map(lambda (ref,rec): ref, + filter(lambda (ref,rec): rec['bridge'] == bridge, + self.__networks.items())) + if len(networks) == 0: + raise Error("No matching network \"%s\"" % bridge) + + answer = None + for network in networks: + nwrec = self.get_network_record(network) + for pif in nwrec['PIFs']: + pifrec = self.get_pif_record(pif) + if answer: + raise Error("Multiple PIFs on host for network %s" % (bridge)) + answer = pif + if not answer: + raise Error("No PIF on host for network %s" % (bridge)) + return answer + + def get_pif_record(self, pif): + if self.__pifs.has_key(pif): + return self.__pifs[pif] + raise Error("Unknown PIF \"%s\"" % pif) + def get_all_pifs(self): + return self.__pifs + def pif_exists(self, pif): + return self.__pifs.has_key(pif) + + def get_management_pif(self): + """ Returns the management pif on host + """ + all = self.get_all_pifs() + for pif in all: + pifrec = self.get_pif_record(pif) + if pifrec['management']: return pif + return None + + def get_network_record(self, network): + if self.__networks.has_key(network): + return self.__networks[network] + raise Error("Unknown network \"%s\"" % network) + + def get_bond_record(self, bond): + if self.__bonds.has_key(bond): + return self.__bonds[bond] + else: + return None + + def get_vlan_record(self, vlan): + if self.__vlans.has_key(vlan): + return self.__vlans[vlan] + else: + return None + +# +# +# + +def ethtool_settings(oc): + settings = [] + if oc.has_key('ethtool-speed'): + val = oc['ethtool-speed'] + if val in ["10", "100", "1000"]: + settings += ['speed', val] + else: + log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val) + if oc.has_key('ethtool-duplex'): + val = oc['ethtool-duplex'] + if val in ["10", "100", "1000"]: + settings += ['duplex', 'val'] + else: + log("Invalid value for ethtool-duplex = %s. Must be half|full." % val) + if oc.has_key('ethtool-autoneg'): + val = oc['ethtool-autoneg'] + if val in ["true", "on"]: + settings += ['autoneg', 'on'] + elif val in ["false", "off"]: + settings += ['autoneg', 'off'] + else: + log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val) + offload = [] + for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"): + if oc.has_key("ethtool-" + opt): + val = oc["ethtool-" + opt] + if val in ["true", "on"]: + offload += [opt, 'on'] + elif val in ["false", "off"]: + offload += [opt, 'off'] + else: + log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val)) + return settings,offload + +# By default the MTU is taken from the Network.MTU setting for VIF, +# PIF and Bridge. However it is possible to override this by using +# {VIF,PIF,Network}.other-config:mtu. +# +# type parameter is a string describing the object that the oc parameter +# is from. e.g. "PIF", "Network" +def mtu_setting(nw, type, oc): + mtu = None + + nwrec = db().get_network_record(nw) + if nwrec.has_key('MTU'): + mtu = nwrec['MTU'] + else: + mtu = "1500" + + if oc.has_key('mtu'): + log("Override Network.MTU setting on bridge %s from %s.MTU is %s" % \ + (nwrec['bridge'], type, mtu)) + mtu = oc['mtu'] + + if mtu is not None: + try: + int(mtu) # Check that the value is an integer + return mtu + except ValueError, x: + log("Invalid value for mtu = %s" % mtu) + + return None + +# +# IP Network Devices -- network devices with IP configuration +# +def pif_ipdev_name(pif): + """Return the ipdev name associated with pif""" + pifrec = db().get_pif_record(pif) + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + # TODO: sanity check that nwrec['bridgeless'] != 'true' + return nwrec['bridge'] + else: + # TODO: sanity check that nwrec['bridgeless'] == 'true' + return pif_netdev_name(pif) + +# +# Bare Network Devices -- network devices without IP configuration +# + +def netdev_exists(netdev): + return os.path.exists(root_prefix() + "/sys/class/net/" + netdev) + +def pif_netdev_name(pif): + """Get the netdev name for a PIF.""" + + pifrec = db().get_pif_record(pif) + + if pif_is_vlan(pif): + return "%(device)s.%(VLAN)s" % pifrec + else: + return pifrec['device'] + +# +# Bridges +# + +def pif_is_bridged(pif): + pifrec = db().get_pif_record(pif) + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + # TODO: sanity check that nwrec['bridgeless'] != 'true' + return True + else: + # TODO: sanity check that nwrec['bridgeless'] == 'true' + return False + +def pif_bridge_name(pif): + """Return the bridge name of a pif. + + PIF must be a bridged PIF.""" + pifrec = db().get_pif_record(pif) + + nwrec = db().get_network_record(pifrec['network']) + + if nwrec['bridge']: + return nwrec['bridge'] + else: + raise Error("PIF %(uuid)s does not have a bridge name" % pifrec) + +# +# Bonded PIFs +# +def pif_is_bond(pif): + pifrec = db().get_pif_record(pif) + + return len(pifrec['bond_master_of']) > 0 + +def pif_get_bond_masters(pif): + """Returns a list of PIFs which are bond masters of this PIF""" + + pifrec = db().get_pif_record(pif) + + bso = pifrec['bond_slave_of'] + + # bond-slave-of is currently a single reference but in principle a + # PIF could be a member of several bonds which are not + # concurrently attached. Be robust to this possibility. + if not bso or bso == "OpaqueRef:NULL": + bso = [] + elif not type(bso) == list: + bso = [bso] + + bondrecs = [db().get_bond_record(bond) for bond in bso] + bondrecs = [rec for rec in bondrecs if rec] + + return [bond['master'] for bond in bondrecs] + +def pif_get_bond_slaves(pif): + """Returns a list of PIFs which make up the given bonded pif.""" + + pifrec = db().get_pif_record(pif) + + bmo = pifrec['bond_master_of'] + if len(bmo) > 1: + raise Error("Bond-master-of contains too many elements") + + if len(bmo) == 0: + return [] + + bondrec = db().get_bond_record(bmo[0]) + if not bondrec: + raise Error("No bond record for bond master PIF") + + return bondrec['slaves'] + +# +# VLAN PIFs +# + +def pif_is_vlan(pif): + return db().get_pif_record(pif)['VLAN'] != '-1' + +def pif_get_vlan_slave(pif): + """Find the PIF which is the VLAN slave of pif. + +Returns the 'physical' PIF underneath the a VLAN PIF @pif.""" + + pifrec = db().get_pif_record(pif) + + vlan = pifrec['VLAN_master_of'] + if not vlan or vlan == "OpaqueRef:NULL": + raise Error("PIF is not a VLAN master") + + vlanrec = db().get_vlan_record(vlan) + if not vlanrec: + raise Error("No VLAN record found for PIF") + + return vlanrec['tagged_PIF'] + +def pif_get_vlan_masters(pif): + """Returns a list of PIFs which are VLANs on top of the given pif.""" + + pifrec = db().get_pif_record(pif) + vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']] + return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])] + +# +# Datapath base class +# + +class Datapath(object): + """Object encapsulating the actions necessary to (de)configure the + datapath for a given PIF. Does not include configuration of the + IP address on the ipdev. + """ + + def __init__(self, pif): + self._pif = pif + + def configure_ipdev(self, cfg): + """Write ifcfg TYPE field for an IPdev, plus any type specific + fields to cfg + """ + raise NotImplementedError + + def preconfigure(self, parent): + """Prepare datapath configuration for PIF, but do not actually + apply any changes. + + Any configuration files should be attached to parent. + """ + raise NotImplementedError + + def bring_down_existing(self): + """Tear down any existing network device configuration which + needs to be undone in order to bring this PIF up. + """ + raise NotImplementedError + + def configure(self): + """Apply the configuration prepared in the preconfigure stage. + + Should assume any configuration files changed attached in + the preconfigure stage are applied and bring up the + necesary devices to provide the datapath for the + PIF. + + Should not bring up the IPdev. + """ + raise NotImplementedError + + def post(self): + """Called after the IPdev has been brought up. + + Should do any final setup, including reinstating any + devices which were taken down in the bring_down_existing + hook. + """ + raise NotImplementedError + + def bring_down(self): + """Tear down and deconfigure the datapath. Should assume the + IPdev has already been brought down. + """ + raise NotImplementedError + +def DatapathFactory(pif): + # XXX Need a datapath object for bridgeless PIFs + + try: + network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r') + network_backend = network_conf.readline().strip() + network_conf.close() + except Exception, e: + raise Error("failed to determine network backend:" + e) + + if network_backend == "bridge": + from InterfaceReconfigureBridge import DatapathBridge + return DatapathBridge(pif) + elif network_backend in ["openvswitch", "vswitch"]: + from InterfaceReconfigureVswitch import DatapathVswitch + return DatapathVswitch(pif) + else: + raise Error("unknown network backend %s" % network_backend) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch index 944962b6b37..dba8a9b8a36 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch @@ -31,3 +31,14 @@ networkUsage.sh=../../../../network/domr/,0755,/opt/xensource/bin call_firewall.sh=../../../../network/domr/,0755,/opt/xensource/bin call_loadbalancer.sh=../../../../network/domr/,0755,/opt/xensource/bin l2tp_vpn.sh=../../../../network/domr/,0755,/opt/xensource/bin +copy_vhd_to_secondarystorage.sh=..,0755,/opt/xensource/bin +copy_vhd_from_secondarystorage.sh=..,0755,/opt/xensource/bin +setup_heartbeat_sr.sh=..,0755,/opt/xensource/bin +setup_heartbeat_file.sh=..,0755,/opt/xensource/bin +check_heartbeat.sh=..,0755,/opt/xensource/bin +xenheartbeat.sh=..,0755,/opt/xensource/bin +launch_hb.sh=..,0755,/opt/xensource/bin +setupXenServer.sh=..,0755,/opt/xensource/bin +vmopspremium=..,0755,/etc/xapi.d/plugins +InterfaceReconfigure.py=.,0755,/opt/xensource/bin +fsimage.so=..,0755,/usr/lib/fs/ext2fs-lib diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index 9f7c0d0d193..bc1d2c8ed12 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -33,3 +33,12 @@ call_firewall.sh=../../../../network/domr/,0755,/opt/xensource/bin call_loadbalancer.sh=../../../../network/domr/,0755,/opt/xensource/bin l2tp_vpn.sh=../../../../network/domr/,0755,/opt/xensource/bin cloud-setup-bonding.sh=..,0755,/opt/xensource/bin +copy_vhd_to_secondarystorage.sh=..,0755,/opt/xensource/bin +copy_vhd_from_secondarystorage.sh=..,0755,/opt/xensource/bin +setup_heartbeat_sr.sh=..,0755,/opt/xensource/bin +setup_heartbeat_file.sh=..,0755,/opt/xensource/bin +check_heartbeat.sh=..,0755,/opt/xensource/bin +xenheartbeat.sh=..,0755,/opt/xensource/bin +launch_hb.sh=..,0755,/opt/xensource/bin +setupXenServer.sh=..,0755,/opt/xensource/bin +vmopspremium=..,0755,/etc/xapi.d/plugins diff --git a/scripts/vm/hypervisor/xenserver/xs_cleanup.sh b/scripts/vm/hypervisor/xenserver/xs_cleanup.sh new file mode 100755 index 00000000000..cb81a3f16ef --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xs_cleanup.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +#set -x + +usage() { + printf "Usage: %s \n" $(basename $0) >&2 + +} + +# remove device which is in xenstore but not in xapi +remove_device() { + be=$1 + xenstore-write /local/domain/0/backend/tap/0/$be/online 0 &>/dev/null + xenstore-write /local/domain/0/backend/tap/0/$be/shutdown-request normal &>/dev/null + for i in $(seq 20) + do + sleep 1 + xenstore-exists /local/domain/0/backend/tap/0/$be/shutdown-done &>/dev/null + if [ $? -eq 0 ] ; then + xenstore-rm /local/domain/0/device/vbd/$be &>/dev/null + xenstore-rm /local/domain/0/backend/tap/0/$be &>/dev/null + xenstore-rm /local/domain/0/error/backend/tap/0/$be &>/dev/null + xenstore-rm /local/domain/0/error/device/vbd/$be &>/dev/null + return + fi + xenstore-exists /local/domain/0/backend/tap/0/$be &>/dev/null + if [ $? -ne 0 ] ; then + return + fi + done + + echo "unplug device $be failed" + exit 2 +} + +bes=`xenstore-list /local/domain/0/backend/tap/0` + +if [ -z "$bes" ]; then + exit 0 +fi + +for be in $bes +do + device=`xenstore-read /local/domain/0/backend/tap/0/$be/dev` + ls $device >/dev/null 2>&1 + if [ $? -ne 0 ]; then + remove_device $be + fi +done + + +echo "======> DONE <======" +exit 0 +