From 2462eab6f02105b15f4686a6183eb043d7b1a5e3 Mon Sep 17 00:00:00 2001 From: Antony Messerli Date: Mon, 20 Dec 2010 10:56:10 -0600 Subject: initial commit of xenserver host protections --- plugins/xenserver/networking/etc/init.d/host-rules | 83 ++++++++++++++++++++++ .../etc/xensource/scripts/vif_5.6-fp1.patch | 22 ++++++ .../networking/etc/xensource/scripts/vif_rules.py | 72 +++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100755 plugins/xenserver/networking/etc/init.d/host-rules create mode 100644 plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch create mode 100755 plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py (limited to 'plugins') diff --git a/plugins/xenserver/networking/etc/init.d/host-rules b/plugins/xenserver/networking/etc/init.d/host-rules new file mode 100755 index 000000000..980396bae --- /dev/null +++ b/plugins/xenserver/networking/etc/init.d/host-rules @@ -0,0 +1,83 @@ +#!/bin/bash +# +# host-rules Start/Stop the networking host rules +# +# chkconfig: 2345 85 15 +# description: Networking Host Rules for Multi Tenancy Protections + +iptables-up() +{ + iptables -P FORWARD DROP + iptables -A FORWARD -m physdev --physdev-in eth0 -j ACCEPT + iptables -A FORWARD -m physdev --physdev-in eth1 -j ACCEPT +} + +ebtables-up() +{ + ebtables -P FORWARD DROP + ebtables -A FORWARD -o eth0 -j ACCEPT + ebtables -A FORWARD -o eth1 -j ACCEPT +} + +arptables-up() +{ + arptables -P FORWARD DROP + arptables -A FORWARD --opcode Request --in-interface eth0 -j ACCEPT + arptables -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + arptables -A FORWARD --opcode Request --in-interface eth1 -j ACCEPT + arptables -A FORWARD --opcode Reply --in-interface eth1 -j ACCEPT +} + +iptables-down() +{ + iptables -P FORWARD ACCEPT + iptables -D FORWARD -m physdev --physdev-in eth0 -j ACCEPT + iptables -D FORWARD -m physdev --physdev-in eth1 -j ACCEPT +} + +ebtables-down() +{ + ebtables -P FORWARD ACCEPT + ebtables -D FORWARD -o eth0 -j ACCEPT + ebtables -D FORWARD -o eth1 -j ACCEPT +} + +arptables-down() +{ + arptables -P FORWARD ACCEPT + arptables -D FORWARD --opcode Request --in-interface eth0 -j ACCEPT + arptables -D FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + arptables -D FORWARD --opcode Request --in-interface eth1 -j ACCEPT + arptables -D FORWARD --opcode Reply --in-interface eth1 -j ACCEPT +} + +start() +{ + iptables-up + ebtables-up + arptables-up +} + +stop() +{ + iptables-down + ebtables-down + arptables-down +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + *) + echo $"Usage: $0 {start|stop|restart}" + exit 1 +esac +exit 0 diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch b/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch new file mode 100644 index 000000000..142096ff1 --- /dev/null +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch @@ -0,0 +1,22 @@ +--- vif 2010-12-20 16:39:46.000000000 +0000 ++++ vif_modified 2010-11-19 23:24:37.000000000 +0000 +@@ -213,6 +213,7 @@ + + # xs-xen.pq.hq:91e986b8e49f netback-wait-for-hotplug + xenstore-write "/local/domain/0/backend/vif/${DOMID}/${DEVID}/hotplug-status" "connected" ++ python /etc/xensource/scripts/vif_rules.py ${DOMID} online 2>&1 >> /dev/null + fi + ;; + +@@ -224,9 +225,11 @@ + + remove) + if [ "${TYPE}" = "vif" ] ;then ++ python /etc/xensource/scripts/vif_rules.py ${DOMID} offline 2>&1 >> /dev/null + xenstore-rm "${HOTPLUG}/hotplug" + fi + logger -t scripts-vif "${dev} has been removed" + remove_from_bridge + ;; + esac ++ diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py new file mode 100755 index 000000000..05141630b --- /dev/null +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +from os import system, popen4 +import sys +import simplejson as json +from itertools import chain + +# order is important, mmmkay? 1 is domid, 2 command, 3 is vif +# when we add rules, we delete first, to make sure we only keep the one rule we need + +def main(): + fin,fout = popen4("/usr/bin/xenstore-ls /local/domain/%s/vm-data/networking" % sys.argv[1] ) + macs = fout.read().split("\n")[0:-1] + + for mac in macs: + m = mac.split("=")[0].strip() + fin,fout = popen4("/usr/bin/xenstore-read /local/domain/%s/vm-data/networking/%s" % (sys.argv[1],m)) + mjson = json.loads(fout.read()) + for ip in mjson['ips']: + if mjson["label"] == "public": + label = 0 + else: + label = 1 + + VIF = "vif%s.%s" % (sys.argv[1],label) + + if (len(sys.argv) == 4 and sys.argv[3] == VIF) or (len(sys.argv) == 3): + run_rules( + IP = ip['ip'], + VIF = VIF, + MAC = mjson['mac'], + STATUS = (sys.argv[2] == 'online') and '-A' or '-D' + ) + +def run_rules(**kwargs): + map(system, chain(ebtables(**kwargs), arptables(**kwargs), iptables(**kwargs) )) + +def iptables(**kwargs): + return [ + "/sbin/iptables -D FORWARD -m physdev --physdev-in %s -s %s -j ACCEPT 2>&1 > /dev/null" % ( kwargs['VIF'], kwargs['IP']), + "/sbin/iptables %s FORWARD -m physdev --physdev-in %s -s %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']) + ] + +def arptables(**kwargs): + return [ + "/sbin/arptables -D FORWARD --opcode Request --in-interface %s --source-ip %s --source-mac %s -j ACCEPT 2>&1 > /dev/null" % (kwargs['VIF'], kwargs['IP'], kwargs['MAC']), + "/sbin/arptables -D FORWARD --opcode Reply --in-interface %s --source-ip %s --source-mac %s -j ACCEPT 2>&1 > /dev/null" % (kwargs['VIF'], kwargs['IP'], kwargs['MAC']), + "/sbin/arptables %s FORWARD --opcode Request --in-interface %s --source-ip %s --source-mac %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP'], kwargs['MAC']), + "/sbin/arptables %s FORWARD --opcode Reply --in-interface %s --source-ip %s --source-mac %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP'], kwargs['MAC']) + ] + +def ebtables(**kwargs): + cmds = [ + "/sbin/ebtables -D FORWARD -p 0806 -o %s --arp-ip-dst %s -j ACCEPT 2>&1 >> /dev/null" % (kwargs['VIF'], kwargs['IP']), + "/sbin/ebtables -D FORWARD -p 0800 -o %s --ip-dst %s -j ACCEPT 2>&1 >> /dev/null" % (kwargs['VIF'], kwargs['IP']), + "/sbin/ebtables %s FORWARD -p 0806 -o %s --arp-ip-dst %s -j ACCEPT 2>&1 " % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']), + "/sbin/ebtables %s FORWARD -p 0800 -o %s --ip-dst %s -j ACCEPT 2>&1 " % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']) + ] + if kwargs['STATUS'] == "-A": + cmds.append("/sbin/ebtables -D FORWARD -s ! %s -i %s -j DROP 2>&1 > /dev/null" % (kwargs['MAC'], kwargs['VIF'])) + cmds.append("/sbin/ebtables -I FORWARD 1 -s ! %s -i %s -j DROP" % (kwargs['MAC'], kwargs['VIF'])) + else: + cmds.append("/sbin/ebtables %s FORWARD -s ! %s -i %s -j DROP" % (kwargs['STATUS'], kwargs['MAC'], kwargs['VIF'])) + return cmds + +def usage(): + print "Usage: slice_vifs.py optional: " + +if __name__ == "__main__": + if len(sys.argv) < 3: + usage() + else: + main() -- cgit From ab0cba603d96e25ee151222bb5fcf550459cfc7a Mon Sep 17 00:00:00 2001 From: Cory Wright Date: Mon, 20 Dec 2010 17:24:08 -0500 Subject: Rewrite of vif_rules.py to meet coding standards and be more pythonic in general. Use absolute paths for iptables/ebtables/arptables in host-rules. --- plugins/xenserver/networking/etc/init.d/host-rules | 54 ++++--- .../etc/xensource/scripts/vif_5.6-fp1.patch | 4 +- .../networking/etc/xensource/scripts/vif_rules.py | 180 +++++++++++++-------- 3 files changed, 146 insertions(+), 92 deletions(-) (limited to 'plugins') diff --git a/plugins/xenserver/networking/etc/init.d/host-rules b/plugins/xenserver/networking/etc/init.d/host-rules index 980396bae..385c59629 100755 --- a/plugins/xenserver/networking/etc/init.d/host-rules +++ b/plugins/xenserver/networking/etc/init.d/host-rules @@ -5,50 +5,54 @@ # chkconfig: 2345 85 15 # description: Networking Host Rules for Multi Tenancy Protections +IPTABLES=/sbin/iptables +EBTABLES=/sbin/ebtables +ARPTABLES=/sbin/arptables + iptables-up() { - iptables -P FORWARD DROP - iptables -A FORWARD -m physdev --physdev-in eth0 -j ACCEPT - iptables -A FORWARD -m physdev --physdev-in eth1 -j ACCEPT + $IPTABLES -P FORWARD DROP + $IPTABLES -A FORWARD -m physdev --physdev-in eth0 -j ACCEPT + $IPTABLES -A FORWARD -m physdev --physdev-in eth1 -j ACCEPT } ebtables-up() { - ebtables -P FORWARD DROP - ebtables -A FORWARD -o eth0 -j ACCEPT - ebtables -A FORWARD -o eth1 -j ACCEPT + $EBTABLES -P FORWARD DROP + $EBTABLES -A FORWARD -o eth0 -j ACCEPT + $EBTABLES -A FORWARD -o eth1 -j ACCEPT } arptables-up() { - arptables -P FORWARD DROP - arptables -A FORWARD --opcode Request --in-interface eth0 -j ACCEPT - arptables -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT - arptables -A FORWARD --opcode Request --in-interface eth1 -j ACCEPT - arptables -A FORWARD --opcode Reply --in-interface eth1 -j ACCEPT + $ARPTABLES -P FORWARD DROP + $ARPTABLES -A FORWARD --opcode Request --in-interface eth0 -j ACCEPT + $ARPTABLES -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + $ARPTABLES -A FORWARD --opcode Request --in-interface eth1 -j ACCEPT + $ARPTABLES -A FORWARD --opcode Reply --in-interface eth1 -j ACCEPT } iptables-down() { - iptables -P FORWARD ACCEPT - iptables -D FORWARD -m physdev --physdev-in eth0 -j ACCEPT - iptables -D FORWARD -m physdev --physdev-in eth1 -j ACCEPT + $IPTABLES -P FORWARD ACCEPT + $IPTABLES -D FORWARD -m physdev --physdev-in eth0 -j ACCEPT + $IPTABLES -D FORWARD -m physdev --physdev-in eth1 -j ACCEPT } ebtables-down() { - ebtables -P FORWARD ACCEPT - ebtables -D FORWARD -o eth0 -j ACCEPT - ebtables -D FORWARD -o eth1 -j ACCEPT + $EBTABLES -P FORWARD ACCEPT + $EBTABLES -D FORWARD -o eth0 -j ACCEPT + $EBTABLES -D FORWARD -o eth1 -j ACCEPT } arptables-down() { - arptables -P FORWARD ACCEPT - arptables -D FORWARD --opcode Request --in-interface eth0 -j ACCEPT - arptables -D FORWARD --opcode Reply --in-interface eth0 -j ACCEPT - arptables -D FORWARD --opcode Request --in-interface eth1 -j ACCEPT - arptables -D FORWARD --opcode Reply --in-interface eth1 -j ACCEPT + $ARPTABLES -P FORWARD ACCEPT + $ARPTABLES -D FORWARD --opcode Request --in-interface eth0 -j ACCEPT + $ARPTABLES -D FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + $ARPTABLES -D FORWARD --opcode Request --in-interface eth1 -j ACCEPT + $ARPTABLES -D FORWARD --opcode Reply --in-interface eth1 -j ACCEPT } start() @@ -68,16 +72,20 @@ stop() case "$1" in start) start + RETVAL=$? ;; stop) stop + RETVAL=$? ;; restart) stop start + RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart}" exit 1 + ;; esac -exit 0 +exit $RETVAL diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch b/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch index 142096ff1..feaf1312d 100644 --- a/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_5.6-fp1.patch @@ -4,7 +4,7 @@ # xs-xen.pq.hq:91e986b8e49f netback-wait-for-hotplug xenstore-write "/local/domain/0/backend/vif/${DOMID}/${DEVID}/hotplug-status" "connected" -+ python /etc/xensource/scripts/vif_rules.py ${DOMID} online 2>&1 >> /dev/null ++ python /etc/xensource/scripts/vif_rules.py ${DOMID} online 2>&1 > /dev/null fi ;; @@ -12,7 +12,7 @@ remove) if [ "${TYPE}" = "vif" ] ;then -+ python /etc/xensource/scripts/vif_rules.py ${DOMID} offline 2>&1 >> /dev/null ++ python /etc/xensource/scripts/vif_rules.py ${DOMID} offline 2>&1 > /dev/null xenstore-rm "${HOTPLUG}/hotplug" fi logger -t scripts-vif "${dev} has been removed" diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py index 05141630b..dd27d3c6b 100755 --- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -1,72 +1,118 @@ #!/usr/bin/env python -from os import system, popen4 +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +This script is used to configure iptables, ebtables, and arptables rules for +XenServer instances. +""" + +import os +import subprocess import sys + +# This is written to Python 2.4, since that is what is available on XenServer import simplejson as json -from itertools import chain - -# order is important, mmmkay? 1 is domid, 2 command, 3 is vif -# when we add rules, we delete first, to make sure we only keep the one rule we need - -def main(): - fin,fout = popen4("/usr/bin/xenstore-ls /local/domain/%s/vm-data/networking" % sys.argv[1] ) - macs = fout.read().split("\n")[0:-1] - - for mac in macs: - m = mac.split("=")[0].strip() - fin,fout = popen4("/usr/bin/xenstore-read /local/domain/%s/vm-data/networking/%s" % (sys.argv[1],m)) - mjson = json.loads(fout.read()) - for ip in mjson['ips']: - if mjson["label"] == "public": - label = 0 - else: - label = 1 - - VIF = "vif%s.%s" % (sys.argv[1],label) - - if (len(sys.argv) == 4 and sys.argv[3] == VIF) or (len(sys.argv) == 3): - run_rules( - IP = ip['ip'], - VIF = VIF, - MAC = mjson['mac'], - STATUS = (sys.argv[2] == 'online') and '-A' or '-D' - ) - -def run_rules(**kwargs): - map(system, chain(ebtables(**kwargs), arptables(**kwargs), iptables(**kwargs) )) - -def iptables(**kwargs): - return [ - "/sbin/iptables -D FORWARD -m physdev --physdev-in %s -s %s -j ACCEPT 2>&1 > /dev/null" % ( kwargs['VIF'], kwargs['IP']), - "/sbin/iptables %s FORWARD -m physdev --physdev-in %s -s %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']) - ] - -def arptables(**kwargs): - return [ - "/sbin/arptables -D FORWARD --opcode Request --in-interface %s --source-ip %s --source-mac %s -j ACCEPT 2>&1 > /dev/null" % (kwargs['VIF'], kwargs['IP'], kwargs['MAC']), - "/sbin/arptables -D FORWARD --opcode Reply --in-interface %s --source-ip %s --source-mac %s -j ACCEPT 2>&1 > /dev/null" % (kwargs['VIF'], kwargs['IP'], kwargs['MAC']), - "/sbin/arptables %s FORWARD --opcode Request --in-interface %s --source-ip %s --source-mac %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP'], kwargs['MAC']), - "/sbin/arptables %s FORWARD --opcode Reply --in-interface %s --source-ip %s --source-mac %s -j ACCEPT" % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP'], kwargs['MAC']) - ] - -def ebtables(**kwargs): - cmds = [ - "/sbin/ebtables -D FORWARD -p 0806 -o %s --arp-ip-dst %s -j ACCEPT 2>&1 >> /dev/null" % (kwargs['VIF'], kwargs['IP']), - "/sbin/ebtables -D FORWARD -p 0800 -o %s --ip-dst %s -j ACCEPT 2>&1 >> /dev/null" % (kwargs['VIF'], kwargs['IP']), - "/sbin/ebtables %s FORWARD -p 0806 -o %s --arp-ip-dst %s -j ACCEPT 2>&1 " % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']), - "/sbin/ebtables %s FORWARD -p 0800 -o %s --ip-dst %s -j ACCEPT 2>&1 " % (kwargs['STATUS'], kwargs['VIF'], kwargs['IP']) - ] - if kwargs['STATUS'] == "-A": - cmds.append("/sbin/ebtables -D FORWARD -s ! %s -i %s -j DROP 2>&1 > /dev/null" % (kwargs['MAC'], kwargs['VIF'])) - cmds.append("/sbin/ebtables -I FORWARD 1 -s ! %s -i %s -j DROP" % (kwargs['MAC'], kwargs['VIF'])) - else: - cmds.append("/sbin/ebtables %s FORWARD -s ! %s -i %s -j DROP" % (kwargs['STATUS'], kwargs['MAC'], kwargs['VIF'])) - return cmds - -def usage(): - print "Usage: slice_vifs.py optional: " + + +def main(dom_id, command, only_this_vif=None): + xsls = execute("/usr/bin/xenstore-ls /local/domain/%s/vm-data/networking" \ + % dom_id, True) + macs = [line.split("=")[0].strip() for line in xsls.splitlines()] + + for mac in macs: + xsr = "/usr/bin/xenstore-read /local/domain/%s/vm-data/networking/%s" + xsread = execute(xsr % (dom_id, mac), True) + data = json.loads(xsread) + for ip in data['ips']: + if data["label"] == "public": + vif = "vif%s.0" % dom_id + else: + vif = "vif%s.1" % dom_id + + if (only_this_vif is None) or (vif == only_this_vif): + params = dict(IP=ip['ip'], VIF=vif, MAC=data['mac']) + apply_ebtables_rules(command, params) + apply_arptables_rules(command, params) + apply_iptables_rules(command, params) + + +def execute(command, return_stdout=False): + devnull = open(os.devnull, 'w') + proc = subprocess.Popen(command, shell=True, close_fds=True, + stdout=subprocess.PIPE, stderr=devnull) + if return_stdout: + return proc.stdout.read() + else: + return None + +# A note about adding rules: +# Whenever we add any rule to iptables, arptables or ebtables we first +# delete the same rule to ensure the rule only exists once. + + +def apply_iptables_rules(command, params): + iptables = lambda rule: execute("/sbin/iptables %s" % rule) + + iptables("-D FORWARD -m physdev --physdev-in %(VIF)s -s %(IP)s \ + -j ACCEPT" % params) + if command == 'online': + iptables("-A FORWARD -m physdev --physdev-in %(VIF)s -s %(IP)s \ + -j ACCEPT" % params) + + +def apply_arptables_rules(command, params): + arptables = lambda rule: execute("/sbin/arptables %s" % rule) + + arptables("-D FORWARD --opcode Request --in-interface %(VIF)s \ + --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + arptables("-D FORWARD --opcode Reply --in-interface %(VIF)s \ + --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + if command == 'online': + arptables("-A FORWARD --opcode Request --in-interface %(VIF)s \ + --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + arptables("-A FORWARD --opcode Reply --in-interface %(VIF)s \ + --source-ip %(IP)s --source-mac %(MAC)s -j ACCEPT" % params) + + +def apply_ebtables_rules(command, params): + ebtables = lambda rule: execute("/sbin/ebtables %s" % rule) + + ebtables("-D FORWARD -p 0806 -o %(VIF)s --arp-ip-dst %(IP)s -j ACCEPT" % + params) + ebtables("-D FORWARD -p 0800 -o %(VIF)s --ip-dst %(IP)s -j ACCEPT" % + params) + if command == 'online': + ebtables("-A FORWARD -p 0806 -o %(VIF)s --arp-ip-dst %(IP)s \ + -j ACCEPT" % params) + ebtables("-A FORWARD -p 0800 -o %(VIF)s --ip-dst %(IP)s \ + -j ACCEPT" % params) + + ebtables("-D FORWARD -s ! %(MAC)s -i %(VIF)s -j DROP" % params) + if command == 'online': + ebtables("-I FORWARD 1 -s ! %(MAC)s -i %(VIF)s -j DROP" % params) + if __name__ == "__main__": - if len(sys.argv) < 3: - usage() - else: - main() + if len(sys.argv) < 3: + print "usage: %s dom_id online|offline [vif]" % \ + os.path.basename(sys.argv[0]) + sys.exit(1) + else: + dom_id, command = sys.argv[1:3] + vif = len(sys.argv) == 4 and sys.argv[3] or None + main(dom_id, command, vif) -- cgit From 1b47ef95fff4d8419e27a7cc247178806cc065ff Mon Sep 17 00:00:00 2001 From: Cory Wright Date: Mon, 20 Dec 2010 18:15:40 -0500 Subject: Close devnull filehandle --- plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py index dd27d3c6b..d60816ce7 100755 --- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -17,8 +17,8 @@ # under the License. """ -This script is used to configure iptables, ebtables, and arptables rules for -XenServer instances. +This script is used to configure iptables, ebtables, and arptables rules on +XenServer hosts. """ import os @@ -55,6 +55,7 @@ def execute(command, return_stdout=False): devnull = open(os.devnull, 'w') proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=devnull) + devnull.close() if return_stdout: return proc.stdout.read() else: -- cgit From 130d75a8b240068a6251188da68296496c2c1564 Mon Sep 17 00:00:00 2001 From: Antony Messerli Date: Wed, 22 Dec 2010 11:27:23 -0600 Subject: Moved xenapi into xenserver specific directory --- plugins/xenapi/README | 6 - plugins/xenapi/etc/xapi.d/plugins/objectstore | 231 --------------------- .../xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 216 ------------------- plugins/xenserver/xenapi/README | 6 + .../xenapi/etc/xapi.d/plugins/objectstore | 231 +++++++++++++++++++++ .../xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 216 +++++++++++++++++++ 6 files changed, 453 insertions(+), 453 deletions(-) delete mode 100644 plugins/xenapi/README delete mode 100644 plugins/xenapi/etc/xapi.d/plugins/objectstore delete mode 100755 plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py create mode 100644 plugins/xenserver/xenapi/README create mode 100644 plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore create mode 100755 plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py (limited to 'plugins') diff --git a/plugins/xenapi/README b/plugins/xenapi/README deleted file mode 100644 index fbd471035..000000000 --- a/plugins/xenapi/README +++ /dev/null @@ -1,6 +0,0 @@ -This directory contains files that are required for the XenAPI support. They -should be installed in the XenServer / Xen Cloud Platform domain 0. - -Also, you need to - -chmod u+x /etc/xapi.d/plugins/objectstore diff --git a/plugins/xenapi/etc/xapi.d/plugins/objectstore b/plugins/xenapi/etc/xapi.d/plugins/objectstore deleted file mode 100644 index 271e7337f..000000000 --- a/plugins/xenapi/etc/xapi.d/plugins/objectstore +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2010 Citrix Systems, Inc. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# -# XenAPI plugin for fetching images from nova-objectstore. -# - -import base64 -import errno -import hmac -import os -import os.path -import sha -import time -import urlparse - -import XenAPIPlugin - -from pluginlib_nova import * -configure_logging('objectstore') - - -KERNEL_DIR = '/boot/guest' - -DOWNLOAD_CHUNK_SIZE = 2 * 1024 * 1024 -SECTOR_SIZE = 512 -MBR_SIZE_SECTORS = 63 -MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE - - -def get_vdi(session, args): - src_url = exists(args, 'src_url') - username = exists(args, 'username') - password = exists(args, 'password') - add_partition = validate_bool(args, 'add_partition', 'false') - - (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) - - sr = find_sr(session) - if sr is None: - raise Exception('Cannot find SR to write VDI to') - - virtual_size = \ - get_content_length(proto, netloc, url_path, username, password) - if virtual_size < 0: - raise Exception('Cannot get VDI size') - - vdi_size = virtual_size - if add_partition: - # Make room for MBR. - vdi_size += MBR_SIZE_BYTES - - vdi = create_vdi(session, sr, src_url, vdi_size, False) - with_vdi_in_dom0(session, vdi, False, - lambda dev: get_vdi_(proto, netloc, url_path, - username, password, add_partition, - virtual_size, '/dev/%s' % dev)) - return session.xenapi.VDI.get_uuid(vdi) - - -def get_vdi_(proto, netloc, url_path, username, password, add_partition, - virtual_size, dest): - - if add_partition: - write_partition(virtual_size, dest) - - offset = add_partition and MBR_SIZE_BYTES or 0 - get(proto, netloc, url_path, username, password, dest, offset) - - -def write_partition(virtual_size, dest): - mbr_last = MBR_SIZE_SECTORS - 1 - primary_first = MBR_SIZE_SECTORS - primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1 - - logging.debug('Writing partition table %d %d to %s...', - primary_first, primary_last, dest) - - result = os.system('parted --script %s mklabel msdos' % dest) - if result != 0: - raise Exception('Failed to mklabel') - result = os.system('parted --script %s mkpart primary %ds %ds' % - (dest, primary_first, primary_last)) - if result != 0: - raise Exception('Failed to mkpart') - - logging.debug('Writing partition table %s done.', dest) - - -def find_sr(session): - host = get_this_host(session) - srs = session.xenapi.SR.get_all() - for sr in srs: - sr_rec = session.xenapi.SR.get_record(sr) - if not ('i18n-key' in sr_rec['other_config'] and - sr_rec['other_config']['i18n-key'] == 'local-storage'): - continue - for pbd in sr_rec['PBDs']: - pbd_rec = session.xenapi.PBD.get_record(pbd) - if pbd_rec['host'] == host: - return sr - return None - - -def get_kernel(session, args): - src_url = exists(args, 'src_url') - username = exists(args, 'username') - password = exists(args, 'password') - - (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) - - dest = os.path.join(KERNEL_DIR, url_path[1:]) - - # Paranoid check against people using ../ to do rude things. - if os.path.commonprefix([KERNEL_DIR, dest]) != KERNEL_DIR: - raise Exception('Illegal destination %s %s', (url_path, dest)) - - dirname = os.path.dirname(dest) - try: - os.makedirs(dirname) - except os.error, e: - if e.errno != errno.EEXIST: - raise - if not os.path.isdir(dirname): - raise Exception('Cannot make directory %s', dirname) - - try: - os.remove(dest) - except: - pass - - get(proto, netloc, url_path, username, password, dest, 0) - - return dest - - -def get_content_length(proto, netloc, url_path, username, password): - headers = make_headers('HEAD', url_path, username, password) - return with_http_connection( - proto, netloc, - lambda conn: get_content_length_(url_path, headers, conn)) - - -def get_content_length_(url_path, headers, conn): - conn.request('HEAD', url_path, None, headers) - response = conn.getresponse() - if response.status != 200: - raise Exception('%d %s' % (response.status, response.reason)) - - return long(response.getheader('Content-Length', -1)) - - -def get(proto, netloc, url_path, username, password, dest, offset): - headers = make_headers('GET', url_path, username, password) - download(proto, netloc, url_path, headers, dest, offset) - - -def make_headers(verb, url_path, username, password): - headers = {} - headers['Date'] = \ - time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - headers['Authorization'] = \ - 'AWS %s:%s' % (username, - s3_authorization(verb, url_path, password, headers)) - return headers - - -def s3_authorization(verb, path, password, headers): - sha1 = hmac.new(password, digestmod=sha) - sha1.update(plaintext(verb, path, headers)) - return base64.encodestring(sha1.digest()).strip() - - -def plaintext(verb, path, headers): - return '%s\n\n\n%s\n%s' % (verb, - "\n".join([headers[h] for h in headers]), - path) - - -def download(proto, netloc, url_path, headers, dest, offset): - with_http_connection( - proto, netloc, - lambda conn: download_(url_path, dest, offset, headers, conn)) - - -def download_(url_path, dest, offset, headers, conn): - conn.request('GET', url_path, None, headers) - response = conn.getresponse() - if response.status != 200: - raise Exception('%d %s' % (response.status, response.reason)) - - length = response.getheader('Content-Length', -1) - - with_file( - dest, 'a', - lambda dest_file: download_all(response, length, dest_file, offset)) - - -def download_all(response, length, dest_file, offset): - dest_file.seek(offset) - i = 0 - while True: - buf = response.read(DOWNLOAD_CHUNK_SIZE) - if buf: - dest_file.write(buf) - else: - return - i += len(buf) - if length != -1 and i >= length: - return - - -if __name__ == '__main__': - XenAPIPlugin.dispatch({'get_vdi': get_vdi, - 'get_kernel': get_kernel}) diff --git a/plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py deleted file mode 100755 index 2d323a016..000000000 --- a/plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright (c) 2010 Citrix Systems, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# -# Helper functions for the Nova xapi plugins. In time, this will merge -# with the pluginlib.py shipped with xapi, but for now, that file is not -# very stable, so it's easiest just to have a copy of all the functions -# that we need. -# - -import httplib -import logging -import logging.handlers -import re -import time - - -##### Logging setup - -def configure_logging(name): - log = logging.getLogger() - log.setLevel(logging.DEBUG) - sysh = logging.handlers.SysLogHandler('/dev/log') - sysh.setLevel(logging.DEBUG) - formatter = logging.Formatter('%s: %%(levelname)-8s %%(message)s' % name) - sysh.setFormatter(formatter) - log.addHandler(sysh) - - -##### Exceptions - -class PluginError(Exception): - """Base Exception class for all plugin errors.""" - def __init__(self, *args): - Exception.__init__(self, *args) - -class ArgumentError(PluginError): - """Raised when required arguments are missing, argument values are invalid, - or incompatible arguments are given. - """ - def __init__(self, *args): - PluginError.__init__(self, *args) - - -##### Helpers - -def ignore_failure(func, *args, **kwargs): - try: - return func(*args, **kwargs) - except XenAPI.Failure, e: - logging.error('Ignoring XenAPI.Failure %s', e) - return None - - -##### Argument validation - -ARGUMENT_PATTERN = re.compile(r'^[a-zA-Z0-9_:\.\-,]+$') - -def validate_exists(args, key, default=None): - """Validates that a string argument to a RPC method call is given, and - matches the shell-safe regex, with an optional default value in case it - does not exist. - - Returns the string. - """ - if key in args: - if len(args[key]) == 0: - raise ArgumentError('Argument %r value %r is too short.' % (key, args[key])) - if not ARGUMENT_PATTERN.match(args[key]): - raise ArgumentError('Argument %r value %r contains invalid characters.' % (key, args[key])) - if args[key][0] == '-': - raise ArgumentError('Argument %r value %r starts with a hyphen.' % (key, args[key])) - return args[key] - elif default is not None: - return default - else: - raise ArgumentError('Argument %s is required.' % key) - -def validate_bool(args, key, default=None): - """Validates that a string argument to a RPC method call is a boolean string, - with an optional default value in case it does not exist. - - Returns the python boolean value. - """ - value = validate_exists(args, key, default) - if value.lower() == 'true': - return True - elif value.lower() == 'false': - return False - else: - raise ArgumentError("Argument %s may not take value %r. Valid values are ['true', 'false']." % (key, value)) - -def exists(args, key): - """Validates that a freeform string argument to a RPC method call is given. - Returns the string. - """ - if key in args: - return args[key] - else: - raise ArgumentError('Argument %s is required.' % key) - -def optional(args, key): - """If the given key is in args, return the corresponding value, otherwise - return None""" - return key in args and args[key] or None - - -def get_this_host(session): - return session.xenapi.session.get_this_host(session.handle) - - -def get_domain_0(session): - this_host_ref = get_this_host(session) - expr = 'field "is_control_domain" = "true" and field "resident_on" = "%s"' % this_host_ref - return session.xenapi.VM.get_all_records_where(expr).keys()[0] - - -def create_vdi(session, sr_ref, name_label, virtual_size, read_only): - vdi_ref = session.xenapi.VDI.create( - { 'name_label': name_label, - 'name_description': '', - 'SR': sr_ref, - 'virtual_size': str(virtual_size), - 'type': 'User', - 'sharable': False, - 'read_only': read_only, - 'xenstore_data': {}, - 'other_config': {}, - 'sm_config': {}, - 'tags': [] }) - logging.debug('Created VDI %s (%s, %s, %s) on %s.', vdi_ref, name_label, - virtual_size, read_only, sr_ref) - return vdi_ref - - -def with_vdi_in_dom0(session, vdi, read_only, f): - dom0 = get_domain_0(session) - vbd_rec = {} - vbd_rec['VM'] = dom0 - vbd_rec['VDI'] = vdi - vbd_rec['userdevice'] = 'autodetect' - vbd_rec['bootable'] = False - vbd_rec['mode'] = read_only and 'RO' or 'RW' - vbd_rec['type'] = 'disk' - vbd_rec['unpluggable'] = True - vbd_rec['empty'] = False - vbd_rec['other_config'] = {} - vbd_rec['qos_algorithm_type'] = '' - vbd_rec['qos_algorithm_params'] = {} - vbd_rec['qos_supported_algorithms'] = [] - logging.debug('Creating VBD for VDI %s ... ', vdi) - vbd = session.xenapi.VBD.create(vbd_rec) - logging.debug('Creating VBD for VDI %s done.', vdi) - try: - logging.debug('Plugging VBD %s ... ', vbd) - session.xenapi.VBD.plug(vbd) - logging.debug('Plugging VBD %s done.', vbd) - return f(session.xenapi.VBD.get_device(vbd)) - finally: - logging.debug('Destroying VBD for VDI %s ... ', vdi) - vbd_unplug_with_retry(session, vbd) - ignore_failure(session.xenapi.VBD.destroy, vbd) - logging.debug('Destroying VBD for VDI %s done.', vdi) - - -def vbd_unplug_with_retry(session, vbd): - """Call VBD.unplug on the given VBD, with a retry if we get - DEVICE_DETACH_REJECTED. For reasons which I don't understand, we're - seeing the device still in use, even when all processes using the device - should be dead.""" - while True: - try: - session.xenapi.VBD.unplug(vbd) - logging.debug('VBD.unplug successful first time.') - return - except XenAPI.Failure, e: - if (len(e.details) > 0 and - e.details[0] == 'DEVICE_DETACH_REJECTED'): - logging.debug('VBD.unplug rejected: retrying...') - time.sleep(1) - elif (len(e.details) > 0 and - e.details[0] == 'DEVICE_ALREADY_DETACHED'): - logging.debug('VBD.unplug successful eventually.') - return - else: - logging.error('Ignoring XenAPI.Failure in VBD.unplug: %s', e) - return - - -def with_http_connection(proto, netloc, f): - conn = (proto == 'https' and - httplib.HTTPSConnection(netloc) or - httplib.HTTPConnection(netloc)) - try: - return f(conn) - finally: - conn.close() - - -def with_file(dest_path, mode, f): - dest = open(dest_path, mode) - try: - return f(dest) - finally: - dest.close() diff --git a/plugins/xenserver/xenapi/README b/plugins/xenserver/xenapi/README new file mode 100644 index 000000000..fbd471035 --- /dev/null +++ b/plugins/xenserver/xenapi/README @@ -0,0 +1,6 @@ +This directory contains files that are required for the XenAPI support. They +should be installed in the XenServer / Xen Cloud Platform domain 0. + +Also, you need to + +chmod u+x /etc/xapi.d/plugins/objectstore diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore new file mode 100644 index 000000000..271e7337f --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore @@ -0,0 +1,231 @@ +#!/usr/bin/env python + +# Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# +# XenAPI plugin for fetching images from nova-objectstore. +# + +import base64 +import errno +import hmac +import os +import os.path +import sha +import time +import urlparse + +import XenAPIPlugin + +from pluginlib_nova import * +configure_logging('objectstore') + + +KERNEL_DIR = '/boot/guest' + +DOWNLOAD_CHUNK_SIZE = 2 * 1024 * 1024 +SECTOR_SIZE = 512 +MBR_SIZE_SECTORS = 63 +MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE + + +def get_vdi(session, args): + src_url = exists(args, 'src_url') + username = exists(args, 'username') + password = exists(args, 'password') + add_partition = validate_bool(args, 'add_partition', 'false') + + (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) + + sr = find_sr(session) + if sr is None: + raise Exception('Cannot find SR to write VDI to') + + virtual_size = \ + get_content_length(proto, netloc, url_path, username, password) + if virtual_size < 0: + raise Exception('Cannot get VDI size') + + vdi_size = virtual_size + if add_partition: + # Make room for MBR. + vdi_size += MBR_SIZE_BYTES + + vdi = create_vdi(session, sr, src_url, vdi_size, False) + with_vdi_in_dom0(session, vdi, False, + lambda dev: get_vdi_(proto, netloc, url_path, + username, password, add_partition, + virtual_size, '/dev/%s' % dev)) + return session.xenapi.VDI.get_uuid(vdi) + + +def get_vdi_(proto, netloc, url_path, username, password, add_partition, + virtual_size, dest): + + if add_partition: + write_partition(virtual_size, dest) + + offset = add_partition and MBR_SIZE_BYTES or 0 + get(proto, netloc, url_path, username, password, dest, offset) + + +def write_partition(virtual_size, dest): + mbr_last = MBR_SIZE_SECTORS - 1 + primary_first = MBR_SIZE_SECTORS + primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1 + + logging.debug('Writing partition table %d %d to %s...', + primary_first, primary_last, dest) + + result = os.system('parted --script %s mklabel msdos' % dest) + if result != 0: + raise Exception('Failed to mklabel') + result = os.system('parted --script %s mkpart primary %ds %ds' % + (dest, primary_first, primary_last)) + if result != 0: + raise Exception('Failed to mkpart') + + logging.debug('Writing partition table %s done.', dest) + + +def find_sr(session): + host = get_this_host(session) + srs = session.xenapi.SR.get_all() + for sr in srs: + sr_rec = session.xenapi.SR.get_record(sr) + if not ('i18n-key' in sr_rec['other_config'] and + sr_rec['other_config']['i18n-key'] == 'local-storage'): + continue + for pbd in sr_rec['PBDs']: + pbd_rec = session.xenapi.PBD.get_record(pbd) + if pbd_rec['host'] == host: + return sr + return None + + +def get_kernel(session, args): + src_url = exists(args, 'src_url') + username = exists(args, 'username') + password = exists(args, 'password') + + (proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url) + + dest = os.path.join(KERNEL_DIR, url_path[1:]) + + # Paranoid check against people using ../ to do rude things. + if os.path.commonprefix([KERNEL_DIR, dest]) != KERNEL_DIR: + raise Exception('Illegal destination %s %s', (url_path, dest)) + + dirname = os.path.dirname(dest) + try: + os.makedirs(dirname) + except os.error, e: + if e.errno != errno.EEXIST: + raise + if not os.path.isdir(dirname): + raise Exception('Cannot make directory %s', dirname) + + try: + os.remove(dest) + except: + pass + + get(proto, netloc, url_path, username, password, dest, 0) + + return dest + + +def get_content_length(proto, netloc, url_path, username, password): + headers = make_headers('HEAD', url_path, username, password) + return with_http_connection( + proto, netloc, + lambda conn: get_content_length_(url_path, headers, conn)) + + +def get_content_length_(url_path, headers, conn): + conn.request('HEAD', url_path, None, headers) + response = conn.getresponse() + if response.status != 200: + raise Exception('%d %s' % (response.status, response.reason)) + + return long(response.getheader('Content-Length', -1)) + + +def get(proto, netloc, url_path, username, password, dest, offset): + headers = make_headers('GET', url_path, username, password) + download(proto, netloc, url_path, headers, dest, offset) + + +def make_headers(verb, url_path, username, password): + headers = {} + headers['Date'] = \ + time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) + headers['Authorization'] = \ + 'AWS %s:%s' % (username, + s3_authorization(verb, url_path, password, headers)) + return headers + + +def s3_authorization(verb, path, password, headers): + sha1 = hmac.new(password, digestmod=sha) + sha1.update(plaintext(verb, path, headers)) + return base64.encodestring(sha1.digest()).strip() + + +def plaintext(verb, path, headers): + return '%s\n\n\n%s\n%s' % (verb, + "\n".join([headers[h] for h in headers]), + path) + + +def download(proto, netloc, url_path, headers, dest, offset): + with_http_connection( + proto, netloc, + lambda conn: download_(url_path, dest, offset, headers, conn)) + + +def download_(url_path, dest, offset, headers, conn): + conn.request('GET', url_path, None, headers) + response = conn.getresponse() + if response.status != 200: + raise Exception('%d %s' % (response.status, response.reason)) + + length = response.getheader('Content-Length', -1) + + with_file( + dest, 'a', + lambda dest_file: download_all(response, length, dest_file, offset)) + + +def download_all(response, length, dest_file, offset): + dest_file.seek(offset) + i = 0 + while True: + buf = response.read(DOWNLOAD_CHUNK_SIZE) + if buf: + dest_file.write(buf) + else: + return + i += len(buf) + if length != -1 and i >= length: + return + + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'get_vdi': get_vdi, + 'get_kernel': get_kernel}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py new file mode 100755 index 000000000..2d323a016 --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py @@ -0,0 +1,216 @@ +# Copyright (c) 2010 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# +# Helper functions for the Nova xapi plugins. In time, this will merge +# with the pluginlib.py shipped with xapi, but for now, that file is not +# very stable, so it's easiest just to have a copy of all the functions +# that we need. +# + +import httplib +import logging +import logging.handlers +import re +import time + + +##### Logging setup + +def configure_logging(name): + log = logging.getLogger() + log.setLevel(logging.DEBUG) + sysh = logging.handlers.SysLogHandler('/dev/log') + sysh.setLevel(logging.DEBUG) + formatter = logging.Formatter('%s: %%(levelname)-8s %%(message)s' % name) + sysh.setFormatter(formatter) + log.addHandler(sysh) + + +##### Exceptions + +class PluginError(Exception): + """Base Exception class for all plugin errors.""" + def __init__(self, *args): + Exception.__init__(self, *args) + +class ArgumentError(PluginError): + """Raised when required arguments are missing, argument values are invalid, + or incompatible arguments are given. + """ + def __init__(self, *args): + PluginError.__init__(self, *args) + + +##### Helpers + +def ignore_failure(func, *args, **kwargs): + try: + return func(*args, **kwargs) + except XenAPI.Failure, e: + logging.error('Ignoring XenAPI.Failure %s', e) + return None + + +##### Argument validation + +ARGUMENT_PATTERN = re.compile(r'^[a-zA-Z0-9_:\.\-,]+$') + +def validate_exists(args, key, default=None): + """Validates that a string argument to a RPC method call is given, and + matches the shell-safe regex, with an optional default value in case it + does not exist. + + Returns the string. + """ + if key in args: + if len(args[key]) == 0: + raise ArgumentError('Argument %r value %r is too short.' % (key, args[key])) + if not ARGUMENT_PATTERN.match(args[key]): + raise ArgumentError('Argument %r value %r contains invalid characters.' % (key, args[key])) + if args[key][0] == '-': + raise ArgumentError('Argument %r value %r starts with a hyphen.' % (key, args[key])) + return args[key] + elif default is not None: + return default + else: + raise ArgumentError('Argument %s is required.' % key) + +def validate_bool(args, key, default=None): + """Validates that a string argument to a RPC method call is a boolean string, + with an optional default value in case it does not exist. + + Returns the python boolean value. + """ + value = validate_exists(args, key, default) + if value.lower() == 'true': + return True + elif value.lower() == 'false': + return False + else: + raise ArgumentError("Argument %s may not take value %r. Valid values are ['true', 'false']." % (key, value)) + +def exists(args, key): + """Validates that a freeform string argument to a RPC method call is given. + Returns the string. + """ + if key in args: + return args[key] + else: + raise ArgumentError('Argument %s is required.' % key) + +def optional(args, key): + """If the given key is in args, return the corresponding value, otherwise + return None""" + return key in args and args[key] or None + + +def get_this_host(session): + return session.xenapi.session.get_this_host(session.handle) + + +def get_domain_0(session): + this_host_ref = get_this_host(session) + expr = 'field "is_control_domain" = "true" and field "resident_on" = "%s"' % this_host_ref + return session.xenapi.VM.get_all_records_where(expr).keys()[0] + + +def create_vdi(session, sr_ref, name_label, virtual_size, read_only): + vdi_ref = session.xenapi.VDI.create( + { 'name_label': name_label, + 'name_description': '', + 'SR': sr_ref, + 'virtual_size': str(virtual_size), + 'type': 'User', + 'sharable': False, + 'read_only': read_only, + 'xenstore_data': {}, + 'other_config': {}, + 'sm_config': {}, + 'tags': [] }) + logging.debug('Created VDI %s (%s, %s, %s) on %s.', vdi_ref, name_label, + virtual_size, read_only, sr_ref) + return vdi_ref + + +def with_vdi_in_dom0(session, vdi, read_only, f): + dom0 = get_domain_0(session) + vbd_rec = {} + vbd_rec['VM'] = dom0 + vbd_rec['VDI'] = vdi + vbd_rec['userdevice'] = 'autodetect' + vbd_rec['bootable'] = False + vbd_rec['mode'] = read_only and 'RO' or 'RW' + vbd_rec['type'] = 'disk' + vbd_rec['unpluggable'] = True + vbd_rec['empty'] = False + vbd_rec['other_config'] = {} + vbd_rec['qos_algorithm_type'] = '' + vbd_rec['qos_algorithm_params'] = {} + vbd_rec['qos_supported_algorithms'] = [] + logging.debug('Creating VBD for VDI %s ... ', vdi) + vbd = session.xenapi.VBD.create(vbd_rec) + logging.debug('Creating VBD for VDI %s done.', vdi) + try: + logging.debug('Plugging VBD %s ... ', vbd) + session.xenapi.VBD.plug(vbd) + logging.debug('Plugging VBD %s done.', vbd) + return f(session.xenapi.VBD.get_device(vbd)) + finally: + logging.debug('Destroying VBD for VDI %s ... ', vdi) + vbd_unplug_with_retry(session, vbd) + ignore_failure(session.xenapi.VBD.destroy, vbd) + logging.debug('Destroying VBD for VDI %s done.', vdi) + + +def vbd_unplug_with_retry(session, vbd): + """Call VBD.unplug on the given VBD, with a retry if we get + DEVICE_DETACH_REJECTED. For reasons which I don't understand, we're + seeing the device still in use, even when all processes using the device + should be dead.""" + while True: + try: + session.xenapi.VBD.unplug(vbd) + logging.debug('VBD.unplug successful first time.') + return + except XenAPI.Failure, e: + if (len(e.details) > 0 and + e.details[0] == 'DEVICE_DETACH_REJECTED'): + logging.debug('VBD.unplug rejected: retrying...') + time.sleep(1) + elif (len(e.details) > 0 and + e.details[0] == 'DEVICE_ALREADY_DETACHED'): + logging.debug('VBD.unplug successful eventually.') + return + else: + logging.error('Ignoring XenAPI.Failure in VBD.unplug: %s', e) + return + + +def with_http_connection(proto, netloc, f): + conn = (proto == 'https' and + httplib.HTTPSConnection(netloc) or + httplib.HTTPConnection(netloc)) + try: + return f(conn) + finally: + conn.close() + + +def with_file(dest_path, mode, f): + dest = open(dest_path, mode) + try: + return f(dest) + finally: + dest.close() -- cgit From a653173c75fdd3810ce75c3d5de5ea491d5d6922 Mon Sep 17 00:00:00 2001 From: Antony Messerli Date: Wed, 22 Dec 2010 11:28:08 -0600 Subject: Added networking protections readme --- plugins/xenserver/networking/README | 126 ++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 plugins/xenserver/networking/README (limited to 'plugins') diff --git a/plugins/xenserver/networking/README b/plugins/xenserver/networking/README new file mode 100644 index 000000000..b59de16b8 --- /dev/null +++ b/plugins/xenserver/networking/README @@ -0,0 +1,126 @@ +Multi Tenancy Networking Protections in XenServer +================================================= + +The purpose of the vif_rules script is to allow multi-tenancy on a XenServer +host. In a multi-tenant cloud environment a host machine needs to be able to +enforce network isolation amongst guest instances, at both layer two and layer +three. The rules prevent guests from taking and using unauthorized IP addresses, +sniffing other guests traffic, and prevents ARP poisoning attacks. This current +revision only supports IPv4, but will support IPv6 in the future. + +Kernel Requirements +=================== + +- physdev module +- arptables support +- ebtables support +- iptables support + +If the kernel doesn't support these, you will need to obtain the Source RPMS for +the proper version of XenServer to recompile the dom0 kernel. + +XenServer Requirements (32-bit dom0) +==================================== + +- arptables 32-bit rpm +- ebtables 32-bit rpm +- python-simplejson + +XenServer Environment Specific Notes +==================================== + +- XenServer 5.5 U1 based on the 2.6.18 kernel didn't include physdev module + support. Support for this had to be recompiled into the kernel. +- XenServer 5.6 based on the 2.6.27 kernel didn't include physdev, ebtables, or + arptables. +- XenServer 5.6 FP1 didn't include physdev, ebtables, or arptables but they do + have a Cloud Supplemental pack available to partners which swaps out the + kernels for kernels that support the networking rules. + +How it works - tl;dr +==================== + +iptables, ebtables, and arptables drop rules are applied to all forward chains +on the host. These are applied at boot time with an init script. They ensure +all forwarded packets are dropped by default. Allow rules are then applied to +the instances to ensure they have permission to talk on the internet. + +How it works - Long +=================== + +Any time an underprivileged domain or domU is started or stopped, it gets a +unique domain id (dom_id). This dom_id is utilized in a number of places, one +of which is it's assigned to the virtual interface (vif). The vifs are attached +to the bridge that is attached to the physical network. For instance, if you +had a public bridge attached to eth0 and your domain id was 5, your vif would be +vif5.0. + +The networking rules are applied to the VIF directly so they apply at the lowest +level of the networking stack. Because the VIF changes along with the domain id +on any start, stop, or reboot of the instance, the rules need to be removed and +re-added any time that occurs. + +Because the dom_id can change often, the vif_rules script is hooked into the +/etc/xensource/scripts/vif script that gets called anytime an instance is +started, or stopped, which includes pauses and resumes. + +Examples of the rules ran for the host on boot: + +iptables -P FORWARD DROP iptables -A FORWARD -m physdev --physdev-in eth0 -j +ACCEPT ebtables -P FORWARD DROP ebtables -A FORWARD -o eth0 -j ACCEPT arptables +-P FORWARD DROP arptables -A FORWARD --opcode Request --in-interface eth0 -j +ACCEPT arptables -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + +Examples of the rules that are ran per instance state change: + +iptables -A FORWARD -m physdev --physdev-in vif1.0 -s 10.1.135.22/32 -j ACCEPT +arptables -A FORWARD --opcode Request --in-interface "vif1.0" --source-ip +10.1.135.22 -j ACCEPT arptables -A FORWARD --opcode Reply --in-interface +"vif1.0" --source-ip 10.1.135.22 --source-mac 9e:6e:cc:19:7f:fe -j ACCEPT +ebtables -A FORWARD -p 0806 -o vif1.0 --arp-ip-dst 10.1.135.22 -j ACCEPT +ebtables -A FORWARD -p 0800 -o vif1.0 --ip-dst 10.1.135.22 -j ACCEPT ebtables -I +FORWARD 1 -s ! 9e:6e:cc:19:7f:fe -i vif1.0 -j DROP + +Typically when you see a vif, it'll look like vif.. +vif2.1 for example would be domain 2 on the second interface. + +The vif_rules.py script needs to pull information about the IPs and MAC +addresses assigned to the instance. The current implementation assumes that +information is put into the VM Record into the xenstore-data key in a JSON +string. The vif_rules.py script reads out of the JSON string to determine the +IPs, and MAC addresses to protect. + +An example format is given below: + +xe vm-param-get uuid= param-name=xenstore-data xenstore-data (MRW): +vm-data/networking/4040fa7292e4: +{"label":"public","ips":[{"netmask":"255.255.255.0", "enabled":"1", +"ip":"173.200.100.10"}], "mac":"40:40:fa:72:92:e4", "gateway":"173.200.100.1", +"vm_id":"123456", "dns":["72.3.128.240","72.3.128.241"]}; +vm-data/networking/40402321c9b8: +{"label":"private","ips":[{"netmask":"255.255.224.0","enabled":"1", +"ip":"10.177.10.10"}], "routes":[{"route":"10.176.0.0","netmask":"255.248.0.0", +"gateway":"10.177.10.1"}, {"route":"10.191.192.0", "netmask":"255.255.192.0", +"gateway":"10.177.10.1"}], "mac":"40:40:23:21:c9:b8"} + +The key is used for two purposes. One, the vif_rules.py script will read from +it to apply the rules needed after parsing the JSON. The second is that because +it's put into the xenstore-data field, the xenstore will be populated with this +data on boot. This allows a guest agent the ability to read out data about the +instance and apply configurations as needed. + +Installation +============ + +- Copy host-rules into /etc/init.d/ and make sure to chmod +x host-rules. +- Run 'chkconfig host-rules on' to add the init script to start up. +- Copy vif_rules.py into /etc/xensource/scripts +- Patch /etc/xensource/scripts/vif using the supplied patch file. It may vary + for different versions of XenServer but it should be pretty self explanitory. + It calls the vif_rules.py script on domain creation and tear down. +- Run '/etc/init.d/host-rules start' to start up the host based rules. +- The instance rules will then fire on creation of the VM as long as the correct + JSON is in place. +- You can check to see if the rules are in place with: iptables --list, + arptables --list, or ebtables --list + -- cgit From 002bbfa7a648a1117e14713eab3ee3ee4b2b6d8e Mon Sep 17 00:00:00 2001 From: Cory Wright Date: Mon, 27 Dec 2010 12:06:36 -0500 Subject: Moving README to doc/networking.rst per recommendation from Jay Pipes --- plugins/xenserver/doc/networking.rst | 144 +++++++++++++++++++++++++++++++++++ plugins/xenserver/networking/README | 144 ----------------------------------- 2 files changed, 144 insertions(+), 144 deletions(-) create mode 100644 plugins/xenserver/doc/networking.rst delete mode 100644 plugins/xenserver/networking/README (limited to 'plugins') diff --git a/plugins/xenserver/doc/networking.rst b/plugins/xenserver/doc/networking.rst new file mode 100644 index 000000000..67f2d9af3 --- /dev/null +++ b/plugins/xenserver/doc/networking.rst @@ -0,0 +1,144 @@ +Multi Tenancy Networking Protections in XenServer +================================================= + +The purpose of the vif_rules script is to allow multi-tenancy on a XenServer +host. In a multi-tenant cloud environment a host machine needs to be able to +enforce network isolation amongst guest instances, at both layer two and layer +three. The rules prevent guests from taking and using unauthorized IP addresses, +sniffing other guests traffic, and prevents ARP poisoning attacks. This current +revision only supports IPv4, but will support IPv6 in the future. + +Kernel Requirements +=================== + +- physdev module +- arptables support +- ebtables support +- iptables support + +If the kernel doesn't support these, you will need to obtain the Source RPMS for +the proper version of XenServer to recompile the dom0 kernel. + +XenServer Requirements (32-bit dom0) +==================================== + +- arptables 32-bit rpm +- ebtables 32-bit rpm +- python-simplejson + +XenServer Environment Specific Notes +==================================== + +- XenServer 5.5 U1 based on the 2.6.18 kernel didn't include physdev module + support. Support for this had to be recompiled into the kernel. +- XenServer 5.6 based on the 2.6.27 kernel didn't include physdev, ebtables, or + arptables. +- XenServer 5.6 FP1 didn't include physdev, ebtables, or arptables but they do + have a Cloud Supplemental pack available to partners which swaps out the + kernels for kernels that support the networking rules. + +How it works - tl;dr +==================== + +iptables, ebtables, and arptables drop rules are applied to all forward chains +on the host. These are applied at boot time with an init script. They ensure +all forwarded packets are dropped by default. Allow rules are then applied to +the instances to ensure they have permission to talk on the internet. + +How it works - Long +=================== + +Any time an underprivileged domain or domU is started or stopped, it gets a +unique domain id (dom_id). This dom_id is utilized in a number of places, one +of which is it's assigned to the virtual interface (vif). The vifs are attached +to the bridge that is attached to the physical network. For instance, if you +had a public bridge attached to eth0 and your domain id was 5, your vif would be +vif5.0. + +The networking rules are applied to the VIF directly so they apply at the lowest +level of the networking stack. Because the VIF changes along with the domain id +on any start, stop, or reboot of the instance, the rules need to be removed and +re-added any time that occurs. + +Because the dom_id can change often, the vif_rules script is hooked into the +/etc/xensource/scripts/vif script that gets called anytime an instance is +started, or stopped, which includes pauses and resumes. + +Examples of the rules ran for the host on boot: + +iptables -P FORWARD DROP +iptables -A FORWARD -m physdev --physdev-in eth0 -j ACCEPT +ebtables -P FORWARD DROP +ebtables -A FORWARD -o eth0 -j ACCEPT +arptables -P FORWARD DROP +arptables -A FORWARD --opcode Request --in-interface eth0 -j ACCEPT +arptables -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT + +Examples of the rules that are ran per instance state change: + +iptables -A FORWARD -m physdev --physdev-in vif1.0 -s 10.1.135.22/32 -j ACCEPT +arptables -A FORWARD --opcode Request --in-interface "vif1.0" \ + --source-ip 10.1.135.22 -j ACCEPT +arptables -A FORWARD --opcode Reply --in-interface "vif1.0" \ + --source-ip 10.1.135.22 --source-mac 9e:6e:cc:19:7f:fe -j ACCEPT +ebtables -A FORWARD -p 0806 -o vif1.0 --arp-ip-dst 10.1.135.22 -j ACCEPT +ebtables -A FORWARD -p 0800 -o vif1.0 --ip-dst 10.1.135.22 -j ACCEPT +ebtables -I FORWARD 1 -s ! 9e:6e:cc:19:7f:fe -i vif1.0 -j DROP + +Typically when you see a vif, it'll look like vif.. +vif2.1 for example would be domain 2 on the second interface. + +The vif_rules.py script needs to pull information about the IPs and MAC +addresses assigned to the instance. The current implementation assumes that +information is put into the VM Record into the xenstore-data key in a JSON +string. The vif_rules.py script reads out of the JSON string to determine the +IPs, and MAC addresses to protect. + +An example format is given below: + +# xe vm-param-get uuid= param-name=xenstore-data +xenstore-data (MRW): +vm-data/networking/4040fa7292e4: +{"label": "public", + "ips": [{"netmask":"255.255.255.0", + "enabled":"1", + "ip":"173.200.100.10"}], + "mac":"40:40:fa:72:92:e4", + "gateway":"173.200.100.1", + "vm_id":"123456", + "dns":["72.3.128.240","72.3.128.241"]}; + +vm-data/networking/40402321c9b8: +{"label":"private", + "ips":[{"netmask":"255.255.224.0", + "enabled":"1", + "ip":"10.177.10.10"}], + "routes":[{"route":"10.176.0.0", + "netmask":"255.248.0.0", + "gateway":"10.177.10.1"}, + {"route":"10.191.192.0", + "netmask":"255.255.192.0", + "gateway":"10.177.10.1"}], + "mac":"40:40:23:21:c9:b8"} + +The key is used for two purposes. One, the vif_rules.py script will read from +it to apply the rules needed after parsing the JSON. The second is that because +it's put into the xenstore-data field, the xenstore will be populated with this +data on boot. This allows a guest agent the ability to read out data about the +instance and apply configurations as needed. + +Installation +============ + +- Copy host-rules into /etc/init.d/ and make sure to chmod +x host-rules. +- Run 'chkconfig host-rules on' to add the init script to start up. +- Copy vif_rules.py into /etc/xensource/scripts +- Patch /etc/xensource/scripts/vif using the supplied patch file. It may vary + for different versions of XenServer but it should be pretty self explanatory. + It calls the vif_rules.py script on domain creation and tear down. +- Run '/etc/init.d/host-rules start' to start up the host based rules. +- The instance rules will then fire on creation of the VM as long as the correct + JSON is in place. +- You can check to see if the rules are in place with: iptables --list, + arptables --list, or ebtables --list + diff --git a/plugins/xenserver/networking/README b/plugins/xenserver/networking/README deleted file mode 100644 index 67f2d9af3..000000000 --- a/plugins/xenserver/networking/README +++ /dev/null @@ -1,144 +0,0 @@ -Multi Tenancy Networking Protections in XenServer -================================================= - -The purpose of the vif_rules script is to allow multi-tenancy on a XenServer -host. In a multi-tenant cloud environment a host machine needs to be able to -enforce network isolation amongst guest instances, at both layer two and layer -three. The rules prevent guests from taking and using unauthorized IP addresses, -sniffing other guests traffic, and prevents ARP poisoning attacks. This current -revision only supports IPv4, but will support IPv6 in the future. - -Kernel Requirements -=================== - -- physdev module -- arptables support -- ebtables support -- iptables support - -If the kernel doesn't support these, you will need to obtain the Source RPMS for -the proper version of XenServer to recompile the dom0 kernel. - -XenServer Requirements (32-bit dom0) -==================================== - -- arptables 32-bit rpm -- ebtables 32-bit rpm -- python-simplejson - -XenServer Environment Specific Notes -==================================== - -- XenServer 5.5 U1 based on the 2.6.18 kernel didn't include physdev module - support. Support for this had to be recompiled into the kernel. -- XenServer 5.6 based on the 2.6.27 kernel didn't include physdev, ebtables, or - arptables. -- XenServer 5.6 FP1 didn't include physdev, ebtables, or arptables but they do - have a Cloud Supplemental pack available to partners which swaps out the - kernels for kernels that support the networking rules. - -How it works - tl;dr -==================== - -iptables, ebtables, and arptables drop rules are applied to all forward chains -on the host. These are applied at boot time with an init script. They ensure -all forwarded packets are dropped by default. Allow rules are then applied to -the instances to ensure they have permission to talk on the internet. - -How it works - Long -=================== - -Any time an underprivileged domain or domU is started or stopped, it gets a -unique domain id (dom_id). This dom_id is utilized in a number of places, one -of which is it's assigned to the virtual interface (vif). The vifs are attached -to the bridge that is attached to the physical network. For instance, if you -had a public bridge attached to eth0 and your domain id was 5, your vif would be -vif5.0. - -The networking rules are applied to the VIF directly so they apply at the lowest -level of the networking stack. Because the VIF changes along with the domain id -on any start, stop, or reboot of the instance, the rules need to be removed and -re-added any time that occurs. - -Because the dom_id can change often, the vif_rules script is hooked into the -/etc/xensource/scripts/vif script that gets called anytime an instance is -started, or stopped, which includes pauses and resumes. - -Examples of the rules ran for the host on boot: - -iptables -P FORWARD DROP -iptables -A FORWARD -m physdev --physdev-in eth0 -j ACCEPT -ebtables -P FORWARD DROP -ebtables -A FORWARD -o eth0 -j ACCEPT -arptables -P FORWARD DROP -arptables -A FORWARD --opcode Request --in-interface eth0 -j ACCEPT -arptables -A FORWARD --opcode Reply --in-interface eth0 -j ACCEPT - -Examples of the rules that are ran per instance state change: - -iptables -A FORWARD -m physdev --physdev-in vif1.0 -s 10.1.135.22/32 -j ACCEPT -arptables -A FORWARD --opcode Request --in-interface "vif1.0" \ - --source-ip 10.1.135.22 -j ACCEPT -arptables -A FORWARD --opcode Reply --in-interface "vif1.0" \ - --source-ip 10.1.135.22 --source-mac 9e:6e:cc:19:7f:fe -j ACCEPT -ebtables -A FORWARD -p 0806 -o vif1.0 --arp-ip-dst 10.1.135.22 -j ACCEPT -ebtables -A FORWARD -p 0800 -o vif1.0 --ip-dst 10.1.135.22 -j ACCEPT -ebtables -I FORWARD 1 -s ! 9e:6e:cc:19:7f:fe -i vif1.0 -j DROP - -Typically when you see a vif, it'll look like vif.. -vif2.1 for example would be domain 2 on the second interface. - -The vif_rules.py script needs to pull information about the IPs and MAC -addresses assigned to the instance. The current implementation assumes that -information is put into the VM Record into the xenstore-data key in a JSON -string. The vif_rules.py script reads out of the JSON string to determine the -IPs, and MAC addresses to protect. - -An example format is given below: - -# xe vm-param-get uuid= param-name=xenstore-data -xenstore-data (MRW): -vm-data/networking/4040fa7292e4: -{"label": "public", - "ips": [{"netmask":"255.255.255.0", - "enabled":"1", - "ip":"173.200.100.10"}], - "mac":"40:40:fa:72:92:e4", - "gateway":"173.200.100.1", - "vm_id":"123456", - "dns":["72.3.128.240","72.3.128.241"]}; - -vm-data/networking/40402321c9b8: -{"label":"private", - "ips":[{"netmask":"255.255.224.0", - "enabled":"1", - "ip":"10.177.10.10"}], - "routes":[{"route":"10.176.0.0", - "netmask":"255.248.0.0", - "gateway":"10.177.10.1"}, - {"route":"10.191.192.0", - "netmask":"255.255.192.0", - "gateway":"10.177.10.1"}], - "mac":"40:40:23:21:c9:b8"} - -The key is used for two purposes. One, the vif_rules.py script will read from -it to apply the rules needed after parsing the JSON. The second is that because -it's put into the xenstore-data field, the xenstore will be populated with this -data on boot. This allows a guest agent the ability to read out data about the -instance and apply configurations as needed. - -Installation -============ - -- Copy host-rules into /etc/init.d/ and make sure to chmod +x host-rules. -- Run 'chkconfig host-rules on' to add the init script to start up. -- Copy vif_rules.py into /etc/xensource/scripts -- Patch /etc/xensource/scripts/vif using the supplied patch file. It may vary - for different versions of XenServer but it should be pretty self explanatory. - It calls the vif_rules.py script on domain creation and tear down. -- Run '/etc/init.d/host-rules start' to start up the host based rules. -- The instance rules will then fire on creation of the VM as long as the correct - JSON is in place. -- You can check to see if the rules are in place with: iptables --list, - arptables --list, or ebtables --list - -- cgit