summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeisuke Tagami <tagami.keisuke@lab.ntt.co.jp>2011-09-08 19:56:45 +0000
committerTarmac <>2011-09-08 19:56:45 +0000
commita09d8123c6e7cea8f097fcdeb7f57cd75d682745 (patch)
treedaccf908a1e6d1213afc3930630945dd02c88845
parent816ae51f6580e3ed20597157f94a4ffd4e690c04 (diff)
parent229e5fe8a1bb94bdea17b2fd5868a0aac992e2b1 (diff)
downloadnova-a09d8123c6e7cea8f097fcdeb7f57cd75d682745.tar.gz
nova-a09d8123c6e7cea8f097fcdeb7f57cd75d682745.tar.xz
nova-a09d8123c6e7cea8f097fcdeb7f57cd75d682745.zip
Changing a behavior of update_dhcp() to write out dhcp options file.
This option file make dnsmasq offer a default gateway to only NICs of VM belonging to a network that the first NIC of VM belongs to. So, first NIC of VM must be connected to a network that a correct default gateway exists in. By means of this, VM will not get incorrect default gateways.
-rw-r--r--Authors1
-rwxr-xr-x[-rw-r--r--]nova/network/linux_net.py65
-rwxr-xr-xnova/tests/test_linux_net.py347
3 files changed, 409 insertions, 4 deletions
diff --git a/Authors b/Authors
index a6598103f..4e0848692 100644
--- a/Authors
+++ b/Authors
@@ -62,6 +62,7 @@ Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com>
Justin Shepherd <jshepher@rackspace.com>
Kei Masumoto <masumotok@nttdata.co.jp>
+Keisuke Tagami <tagami.keisuke@lab.ntt.co.jp>
masumoto<masumotok@nttdata.co.jp>
Ken Pepple <ken.pepple@gmail.com>
Kevin Bringard <kbringard@attinteractive.com>
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 57c1d0c28..7d89b2bcc 100644..100755
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -68,6 +68,9 @@ flags.DEFINE_string('linuxnet_interface_driver',
'Driver used to create ethernet devices.')
flags.DEFINE_string('linuxnet_ovs_integration_bridge',
'br-int', 'Name of Open vSwitch bridge used with linuxnet')
+flags.DEFINE_bool('use_single_default_gateway',
+ False, 'Use single default gateway. Only first nic of vm'
+ ' will get default gateway from dhcp server')
binary_name = os.path.basename(inspect.stack()[-1][1])
@@ -511,6 +514,32 @@ def get_dhcp_hosts(context, network_ref):
return '\n'.join(hosts)
+def get_dhcp_opts(context, network_ref):
+ """Get network's hosts config in dhcp-opts format."""
+ hosts = []
+ ips_ref = db.network_get_associated_fixed_ips(context, network_ref['id'])
+
+ if ips_ref:
+ #set of instance ids
+ instance_set = set([fixed_ip_ref['instance_id']
+ for fixed_ip_ref in ips_ref])
+ default_gw_network_node = {}
+ for instance_id in instance_set:
+ vifs = db.virtual_interface_get_by_instance(context, instance_id)
+ if vifs:
+ #offer a default gateway to the first virtual interface
+ default_gw_network_node[instance_id] = vifs[0]['network_id']
+
+ for fixed_ip_ref in ips_ref:
+ instance_id = fixed_ip_ref['instance_id']
+ if instance_id in default_gw_network_node:
+ target_network_id = default_gw_network_node[instance_id]
+ # we don't want default gateway for this fixed ip
+ if target_network_id != fixed_ip_ref['network_id']:
+ hosts.append(_host_dhcp_opts(fixed_ip_ref))
+ return '\n'.join(hosts)
+
+
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
@@ -526,6 +555,12 @@ def update_dhcp(context, dev, network_ref):
with open(conffile, 'w') as f:
f.write(get_dhcp_hosts(context, network_ref))
+ if FLAGS.use_single_default_gateway:
+ optsfile = _dhcp_file(dev, 'opts')
+ with open(optsfile, 'w') as f:
+ f.write(get_dhcp_opts(context, network_ref))
+ os.chmod(optsfile, 0644)
+
# Make sure dnsmasq can actually read it (it setuid()s to "nobody")
os.chmod(conffile, 0644)
@@ -563,6 +598,9 @@ def update_dhcp(context, dev, network_ref):
if FLAGS.dns_server:
cmd += ['-h', '-R', '--server=%s' % FLAGS.dns_server]
+ if FLAGS.use_single_default_gateway:
+ cmd += ['--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts')]
+
_execute(*cmd, run_as_root=True)
@@ -625,13 +663,32 @@ def _host_lease(fixed_ip_ref):
instance_ref['hostname'] or '*')
+def _host_dhcp_network(fixed_ip_ref):
+ instance_ref = fixed_ip_ref['instance']
+ return 'NW-i%08d-%s' % (instance_ref['id'],
+ fixed_ip_ref['network_id'])
+
+
def _host_dhcp(fixed_ip_ref):
"""Return a host string for an address in dhcp-host format."""
instance_ref = fixed_ip_ref['instance']
- return '%s,%s.%s,%s' % (fixed_ip_ref['virtual_interface']['address'],
- instance_ref['hostname'],
- FLAGS.dhcp_domain,
- fixed_ip_ref['address'])
+ vif = fixed_ip_ref['virtual_interface']
+ if FLAGS.use_single_default_gateway:
+ return '%s,%s.%s,%s,%s' % (vif['address'],
+ instance_ref['hostname'],
+ FLAGS.dhcp_domain,
+ fixed_ip_ref['address'],
+ "net:" + _host_dhcp_network(fixed_ip_ref))
+ else:
+ return '%s,%s.%s,%s' % (vif['address'],
+ instance_ref['hostname'],
+ FLAGS.dhcp_domain,
+ fixed_ip_ref['address'])
+
+
+def _host_dhcp_opts(fixed_ip_ref):
+ """Return a host string for an address in dhcp-host format."""
+ return '%s,%s' % (_host_dhcp_network(fixed_ip_ref), 3)
def _execute(*cmd, **kwargs):
diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py
new file mode 100755
index 000000000..99577b88e
--- /dev/null
+++ b/nova/tests/test_linux_net.py
@@ -0,0 +1,347 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 NTT
+# 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.
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.network import manager as network_manager
+from nova.network import linux_net
+
+import mox
+
+FLAGS = flags.FLAGS
+
+LOG = logging.getLogger('nova.tests.network')
+
+
+HOST = "testhost"
+
+instances = [{'id': 0,
+ 'host': 'fake_instance00',
+ 'hostname': 'fake_instance00'},
+ {'id': 1,
+ 'host': 'fake_instance01',
+ 'hostname': 'fake_instance01'}]
+
+
+addresses = [{"address": "10.0.0.1"},
+ {"address": "10.0.0.2"},
+ {"address": "10.0.0.3"},
+ {"address": "10.0.0.4"},
+ {"address": "10.0.0.5"},
+ {"address": "10.0.0.6"}]
+
+
+networks = [{'id': 0,
+ 'uuid': "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
+ 'label': 'test0',
+ 'injected': False,
+ 'multi_host': False,
+ 'cidr': '192.168.0.0/24',
+ 'cidr_v6': '2001:db8::/64',
+ 'gateway_v6': '2001:db8::1',
+ 'netmask_v6': '64',
+ 'netmask': '255.255.255.0',
+ 'bridge': 'fa0',
+ 'bridge_interface': 'fake_fa0',
+ 'gateway': '192.168.0.1',
+ 'broadcast': '192.168.0.255',
+ 'dns1': '192.168.0.1',
+ 'dns2': '192.168.0.2',
+ 'dhcp_server': '0.0.0.0',
+ 'dhcp_start': '192.168.100.1',
+ 'vlan': None,
+ 'host': None,
+ 'project_id': 'fake_project',
+ 'vpn_public_address': '192.168.0.2'},
+ {'id': 1,
+ 'uuid': "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
+ 'label': 'test1',
+ 'injected': False,
+ 'multi_host': False,
+ 'cidr': '192.168.1.0/24',
+ 'cidr_v6': '2001:db9::/64',
+ 'gateway_v6': '2001:db9::1',
+ 'netmask_v6': '64',
+ 'netmask': '255.255.255.0',
+ 'bridge': 'fa1',
+ 'bridge_interface': 'fake_fa1',
+ 'gateway': '192.168.1.1',
+ 'broadcast': '192.168.1.255',
+ 'dns1': '192.168.0.1',
+ 'dns2': '192.168.0.2',
+ 'dhcp_server': '0.0.0.0',
+ 'dhcp_start': '192.168.100.1',
+ 'vlan': None,
+ 'host': None,
+ 'project_id': 'fake_project',
+ 'vpn_public_address': '192.168.1.2'}]
+
+
+fixed_ips = [{'id': 0,
+ 'network_id': 0,
+ 'address': '192.168.0.100',
+ 'instance_id': 0,
+ 'allocated': True,
+ 'virtual_interface_id': 0,
+ 'virtual_interface': addresses[0],
+ 'instance': instances[0],
+ 'floating_ips': []},
+ {'id': 1,
+ 'network_id': 1,
+ 'address': '192.168.1.100',
+ 'instance_id': 0,
+ 'allocated': True,
+ 'virtual_interface_id': 1,
+ 'virtual_interface': addresses[1],
+ 'instance': instances[0],
+ 'floating_ips': []},
+ {'id': 2,
+ 'network_id': 1,
+ 'address': '192.168.0.101',
+ 'instance_id': 1,
+ 'allocated': True,
+ 'virtual_interface_id': 2,
+ 'virtual_interface': addresses[2],
+ 'instance': instances[1],
+ 'floating_ips': []},
+ {'id': 3,
+ 'network_id': 0,
+ 'address': '192.168.1.101',
+ 'instance_id': 1,
+ 'allocated': True,
+ 'virtual_interface_id': 3,
+ 'virtual_interface': addresses[3],
+ 'instance': instances[1],
+ 'floating_ips': []},
+ {'id': 4,
+ 'network_id': 0,
+ 'address': '192.168.0.102',
+ 'instance_id': 0,
+ 'allocated': True,
+ 'virtual_interface_id': 4,
+ 'virtual_interface': addresses[4],
+ 'instance': instances[0],
+ 'floating_ips': []},
+ {'id': 5,
+ 'network_id': 1,
+ 'address': '192.168.1.102',
+ 'instance_id': 1,
+ 'allocated': True,
+ 'virtual_interface_id': 5,
+ 'virtual_interface': addresses[5],
+ 'instance': instances[1],
+ 'floating_ips': []}]
+
+
+vifs = [{'id': 0,
+ 'address': 'DE:AD:BE:EF:00:00',
+ 'uuid': '00000000-0000-0000-0000-0000000000000000',
+ 'network_id': 0,
+ 'network': networks[0],
+ 'instance_id': 0},
+ {'id': 1,
+ 'address': 'DE:AD:BE:EF:00:01',
+ 'uuid': '00000000-0000-0000-0000-0000000000000001',
+ 'network_id': 1,
+ 'network': networks[1],
+ 'instance_id': 0},
+ {'id': 2,
+ 'address': 'DE:AD:BE:EF:00:02',
+ 'uuid': '00000000-0000-0000-0000-0000000000000002',
+ 'network_id': 1,
+ 'network': networks[1],
+ 'instance_id': 1},
+ {'id': 3,
+ 'address': 'DE:AD:BE:EF:00:03',
+ 'uuid': '00000000-0000-0000-0000-0000000000000003',
+ 'network_id': 0,
+ 'network': networks[0],
+ 'instance_id': 1},
+ {'id': 4,
+ 'address': 'DE:AD:BE:EF:00:04',
+ 'uuid': '00000000-0000-0000-0000-0000000000000004',
+ 'network_id': 0,
+ 'network': networks[0],
+ 'instance_id': 0},
+ {'id': 5,
+ 'address': 'DE:AD:BE:EF:00:05',
+ 'uuid': '00000000-0000-0000-0000-0000000000000005',
+ 'network_id': 1,
+ 'network': networks[1],
+ 'instance_id': 1}]
+
+
+class LinuxNetworkTestCase(test.TestCase):
+
+ def setUp(self):
+ super(LinuxNetworkTestCase, self).setUp()
+ network_driver = FLAGS.network_driver
+ self.driver = utils.import_object(network_driver)
+ self.driver.db = db
+
+ def test_update_dhcp_for_nw00(self):
+ self.flags(use_single_default_gateway=True)
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+ self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[0],
+ fixed_ips[3]])
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[0],
+ fixed_ips[3]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[0], vifs[1]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[2], vifs[3]])
+ self.mox.ReplayAll()
+
+ self.driver.update_dhcp(None, "eth0", networks[0])
+
+ def test_update_dhcp_for_nw01(self):
+ self.flags(use_single_default_gateway=True)
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+ self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[1],
+ fixed_ips[2]])
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[1],
+ fixed_ips[2]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[0], vifs[1]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[2], vifs[3]])
+ self.mox.ReplayAll()
+
+ self.driver.update_dhcp(None, "eth0", networks[0])
+
+ def test_get_dhcp_hosts_for_nw00(self):
+ self.flags(use_single_default_gateway=True)
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[0],
+ fixed_ips[3]])
+ self.mox.ReplayAll()
+
+ expected = \
+ "10.0.0.1,fake_instance00.novalocal,"\
+ "192.168.0.100,net:NW-i00000000-0\n"\
+ "10.0.0.4,fake_instance01.novalocal,"\
+ "192.168.1.101,net:NW-i00000001-0"
+ actual_hosts = self.driver.get_dhcp_hosts(None, networks[1])
+
+ self.assertEquals(actual_hosts, expected)
+
+ def test_get_dhcp_hosts_for_nw01(self):
+ self.flags(use_single_default_gateway=True)
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[1],
+ fixed_ips[2]])
+ self.mox.ReplayAll()
+
+ expected = \
+ "10.0.0.2,fake_instance00.novalocal,"\
+ "192.168.1.100,net:NW-i00000000-1\n"\
+ "10.0.0.3,fake_instance01.novalocal,"\
+ "192.168.0.101,net:NW-i00000001-1"
+ actual_hosts = self.driver.get_dhcp_hosts(None, networks[0])
+
+ self.assertEquals(actual_hosts, expected)
+
+ def test_get_dhcp_opts_for_nw00(self):
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+ self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[0],
+ fixed_ips[3],
+ fixed_ips[4]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[0],
+ vifs[1],
+ vifs[4]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[2],
+ vifs[3],
+ vifs[5]])
+ self.mox.ReplayAll()
+
+ expected_opts = 'NW-i00000001-0,3'
+ actual_opts = self.driver.get_dhcp_opts(None, networks[0])
+
+ self.assertEquals(actual_opts, expected_opts)
+
+ def test_get_dhcp_opts_for_nw01(self):
+ self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips')
+ self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
+
+ db.network_get_associated_fixed_ips(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([fixed_ips[1],
+ fixed_ips[2],
+ fixed_ips[5]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[0],
+ vifs[1],
+ vifs[4]])
+ db.virtual_interface_get_by_instance(mox.IgnoreArg(),
+ mox.IgnoreArg())\
+ .AndReturn([vifs[2],
+ vifs[3],
+ vifs[5]])
+ self.mox.ReplayAll()
+
+ expected_opts = "NW-i00000000-1,3"
+ actual_opts = self.driver.get_dhcp_opts(None, networks[1])
+
+ self.assertEquals(actual_opts, expected_opts)
+
+ def test_dhcp_opts_not_default_gateway_network(self):
+ expected = "NW-i00000000-0,3"
+ actual = self.driver._host_dhcp_opts(fixed_ips[0])
+ self.assertEquals(actual, expected)
+
+ def test_host_dhcp_without_default_gateway_network(self):
+ expected = ("10.0.0.1,fake_instance00.novalocal,192.168.0.100")
+ actual = self.driver._host_dhcp(fixed_ips[0])
+ self.assertEquals(actual, expected)