Merging trunk
This commit is contained in:
commit
2a08bed775
4
.mailmap
4
.mailmap
@ -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>
|
||||
|
2
Authors
2
Authors
@ -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>
|
||||
|
@ -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'))
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
144
plugins/xenserver/doc/networking.rst
Normal file
144
plugins/xenserver/doc/networking.rst
Normal file
@ -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
|
||||
|
106
plugins/xenserver/networking/etc/init.d/host-rules
Executable file
106
plugins/xenserver/networking/etc/init.d/host-rules
Executable file
@ -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
|
@ -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
|
||||
+
|
119
plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
Executable file
119
plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
Executable file
@ -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)
|
Loading…
x
Reference in New Issue
Block a user