diff options
19 files changed, 479 insertions, 32 deletions
@@ -27,4 +27,6 @@ <vishvananda@gmail.com> <root@ubuntu> <sleepsonthefloor@gmail.com> <root@tonbuntu> <rlane@wikimedia.org> <laner@controller> -<rick.harris@rackspace.com> <rconradharris@gmail.com> +<rconradharris@gmail.com> <rick.harris@rackspace.com> +<corywright@gmail.com> <cory.wright@rackspace.com> +<ant@openstack.org> <amesserl@rackspace.com> @@ -1,9 +1,11 @@ Andy Smith <code@term.ie> Anne Gentle <anne@openstack.org> Anthony Young <sleepsonthefloor@gmail.com> +Antony Messerli <ant@openstack.org> Armando Migliaccio <Armando.Migliaccio@eu.citrix.com> Chris Behrens <cbehrens@codestud.com> Chmouel Boudjnah <chmouel@chmouel.com> +Cory Wright <corywright@gmail.com> David Pravec <David.Pravec@danix.org> Dean Troyer <dtroyer@gmail.com> Devin Carlen <devin.carlen@gmail.com> diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index bebcdc18c..66aceee2d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -100,7 +100,7 @@ class APIRouter(wsgi.Router): collection={'detail': 'GET'}, member=server_members) - mapper.resource("backup_schedule", "backup_schedules", + mapper.resource("backup_schedule", "backup_schedule", controller=backup_schedules.Controller(), parent_resource=dict(member_name='server', collection_name='servers')) diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py index fc70b5c6c..fcc07bdd3 100644 --- a/nova/api/openstack/backup_schedules.py +++ b/nova/api/openstack/backup_schedules.py @@ -23,13 +23,25 @@ from nova.api.openstack import faults import nova.image.service +def _translate_keys(inst): + """ Coerces the backup schedule into proper dictionary format """ + return dict(backupSchedule=inst) + + class Controller(wsgi.Controller): + """ The backup schedule API controller for the Openstack API """ + + _serialization_metadata = { + 'application/xml': { + 'attributes': { + 'backupSchedule': []}}} def __init__(self): pass def index(self, req, server_id): - return faults.Fault(exc.HTTPNotFound()) + """ Returns the list of backup schedules for a given instance """ + return _translate_keys({}) def create(self, req, server_id): """ No actual update method required, since the existing API allows @@ -37,4 +49,5 @@ class Controller(wsgi.Controller): return faults.Fault(exc.HTTPNotFound()) def delete(self, req, server_id, id): + """ Deletes an existing backup schedule """ return faults.Fault(exc.HTTPNotFound()) diff --git a/nova/api/openstack/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py index 91a8b2e55..cbb4b897e 100644 --- a/nova/api/openstack/ratelimiting/__init__.py +++ b/nova/api/openstack/ratelimiting/__init__.py @@ -64,9 +64,9 @@ class RateLimitingMiddleware(wsgi.Middleware): If the request should be rate limited, return a 413 status with a Retry-After header giving the time when the request would succeed. """ - return self.limited_request(req, self.application) + return self.rate_limited_request(req, self.application) - def limited_request(self, req, application): + def rate_limited_request(self, req, application): """Rate limit the request. If the request should be rate limited, return a 413 status with a diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 10c397384..845183258 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -35,14 +35,11 @@ LOG = logging.getLogger('server') LOG.setLevel(logging.DEBUG) -def _entity_list(entities): - """ Coerces a list of servers into proper dictionary format """ - return dict(servers=entities) - - -def _entity_detail(inst): - """ Maps everything to Rackspace-like attributes for return""" +def _translate_detail_keys(inst): + """ Coerces into dictionary format, mapping everything to Rackspace-like + attributes for return""" power_mapping = { + None: 'build', power_state.NOSTATE: 'build', power_state.RUNNING: 'active', power_state.BLOCKED: 'active', @@ -67,8 +64,9 @@ def _entity_detail(inst): return dict(server=inst_dict) -def _entity_inst(inst): - """ Filters all model attributes save for id and name """ +def _translate_keys(inst): + """ Coerces into dictionary format, excluding all model attributes + save for id and name """ return dict(server=dict(id=inst['internal_id'], name=inst['display_name'])) @@ -87,29 +85,29 @@ class Controller(wsgi.Controller): def index(self, req): """ Returns a list of server names and ids for a given user """ - return self._items(req, entity_maker=_entity_inst) + return self._items(req, entity_maker=_translate_keys) def detail(self, req): """ Returns a list of server details for a given user """ - return self._items(req, entity_maker=_entity_detail) + return self._items(req, entity_maker=_translate_detail_keys) def _items(self, req, entity_maker): """Returns a list of servers for a given user. - entity_maker - either _entity_detail or _entity_inst + entity_maker - either _translate_detail_keys or _translate_keys """ instance_list = self.compute_api.get_instances( req.environ['nova.context']) limited_list = common.limited(instance_list, req) res = [entity_maker(inst)['server'] for inst in limited_list] - return _entity_list(res) + return dict(servers=res) def show(self, req, id): """ Returns server details by server id """ try: instance = self.compute_api.get_instance( req.environ['nova.context'], int(id)) - return _entity_detail(instance) + return _translate_detail_keys(instance) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -138,7 +136,7 @@ class Controller(wsgi.Controller): description=env['server']['name'], key_name=key_pair['name'], key_data=key_pair['public_key']) - return _entity_inst(instances[0]) + return _translate_keys(instances[0]) def update(self, req, id): """ Updates the server name or password """ @@ -153,8 +151,9 @@ class Controller(wsgi.Controller): update_dict['display_name'] = inst_dict['server']['name'] try: - self.compute_api.update_instance(req.environ['nova.context'], - instance['id'], + ctxt = req.environ['nova.context'] + self.compute_api.update_instance(ctxt, + id, **update_dict) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) diff --git a/nova/api/openstack/sharedipgroups.py b/nova/api/openstack/sharedipgroups.py index 75d02905c..845f5bead 100644 --- a/nova/api/openstack/sharedipgroups.py +++ b/nova/api/openstack/sharedipgroups.py @@ -15,26 +15,51 @@ # License for the specific language governing permissions and limitations # under the License. +from webob import exc + from nova import wsgi +from nova.api.openstack import faults + + +def _translate_keys(inst): + """ Coerces a shared IP group instance into proper dictionary format """ + return dict(sharedIpGroup=inst) + + +def _translate_detail_keys(inst): + """ Coerces a shared IP group instance into proper dictionary format with + correctly mapped attributes """ + return dict(sharedIpGroup=inst) class Controller(wsgi.Controller): """ The Shared IP Groups Controller for the Openstack API """ + _serialization_metadata = { + 'application/xml': { + 'attributes': { + 'sharedIpGroup': []}}} + def index(self, req): - raise NotImplementedError + """ Returns a list of Shared IP Groups for the user """ + return dict(sharedIpGroups=[]) def show(self, req, id): - raise NotImplementedError + """ Shows in-depth information on a specific Shared IP Group """ + return _translate_keys({}) def update(self, req, id): - raise NotImplementedError + """ You can't update a Shared IP Group """ + raise faults.Fault(exc.HTTPNotImplemented()) def delete(self, req, id): - raise NotImplementedError + """ Deletes a Shared IP Group """ + raise faults.Fault(exc.HTTPNotFound()) - def detail(self, req): - raise NotImplementedError + def detail(self, req, id): + """ Returns a complete list of Shared IP Groups """ + return _translate_detail_keys({}) def create(self, req): - raise NotImplementedError + """ Creates a new Shared IP group """ + raise faults.Fault(exc.HTTPNotFound()) diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 91517cc5d..78e4a1c77 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -33,6 +33,7 @@ from nova.scheduler import driver FLAGS = flags.FLAGS flags.DECLARE('max_cores', 'nova.scheduler.simple') +flags.DECLARE('stub_network', 'nova.compute.manager') class TestDriver(driver.Scheduler): diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 1c155abe4..4aa489d08 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -33,6 +33,7 @@ flags.DECLARE('instances_path', 'nova.compute.manager') class LibvirtConnTestCase(test.TestCase): def setUp(self): super(LibvirtConnTestCase, self).setUp() + libvirt_conn._late_load_cheetah() self.flags(fake_call=True) self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake', diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5606865fe..3a9e58b50 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -58,10 +58,9 @@ from nova.compute import instance_types from nova.compute import power_state from nova.virt import images -from Cheetah.Template import Template - libvirt = None libxml2 = None +Template = None FLAGS = flags.FLAGS @@ -69,6 +68,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') +flags.DEFINE_string('injected_network_template', + utils.abspath('virt/interfaces.template'), + 'Template file for injected network') flags.DEFINE_string('libvirt_xml_template', utils.abspath('virt/libvirt.xml.template'), 'Libvirt XML Template') @@ -88,15 +90,26 @@ flags.DEFINE_bool('allow_project_net_traffic', def get_connection(read_only): # These are loaded late so that there's no need to install these # libraries when not using libvirt. + # Cheetah is separate because the unit tests want to load Cheetah, + # but not libvirt. global libvirt global libxml2 if libvirt is None: libvirt = __import__('libvirt') if libxml2 is None: libxml2 = __import__('libxml2') + _late_load_cheetah() return LibvirtConnection(read_only) +def _late_load_cheetah(): + global Template + if Template is None: + t = __import__('Cheetah.Template', globals(), locals(), ['Template'], + -1) + Template = t.Template + + def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) diff --git a/nova/wsgi.py b/nova/wsgi.py index c7ee9ed14..b5d6b96c1 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -270,7 +270,7 @@ class Serializer(object): needed to serialize a dictionary to that type. """ self.metadata = metadata or {} - req = webob.Request(environ) + req = webob.Request.blank('', environ) suffix = req.path_info.split('.')[-1].lower() if suffix == 'json': self.handler = self._to_json 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<domain id>.<network bridge>. +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=<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/etc/init.d/host-rules b/plugins/xenserver/networking/etc/init.d/host-rules new file mode 100755 index 000000000..798da9552 --- /dev/null +++ b/plugins/xenserver/networking/etc/init.d/host-rules @@ -0,0 +1,106 @@ +#!/bin/bash +# +# host-rules Start/Stop the networking host rules +# +# chkconfig: 2345 85 15 +# description: Networking Host Rules for Multi Tenancy Protections + +# 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. + +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 +} + +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 + RETVAL=$? + ;; + stop) + stop + RETVAL=$? + ;; + restart) + stop + start + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac +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 new file mode 100644 index 000000000..feaf1312d --- /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..d60816ce7 --- /dev/null +++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# 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 on +XenServer hosts. +""" + +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 + + +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) + devnull.close() + 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: + 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) diff --git a/plugins/xenapi/README b/plugins/xenserver/xenapi/README index fbd471035..fbd471035 100644 --- a/plugins/xenapi/README +++ b/plugins/xenserver/xenapi/README diff --git a/plugins/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 5e648b970..5e648b970 100644 --- a/plugins/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance diff --git a/plugins/xenapi/etc/xapi.d/plugins/objectstore b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore index 8ee2f748d..8ee2f748d 100644 --- a/plugins/xenapi/etc/xapi.d/plugins/objectstore +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore diff --git a/plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py index 2d323a016..2d323a016 100755 --- a/plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py |
