summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2011-12-09 18:57:06 +0000
committerGerrit Code Review <review@openstack.org>2011-12-09 18:57:06 +0000
commit9c34c82cf0e4f3e40b37b7cab37b2d1c88432d5f (patch)
treee8a5d4167b1fb4c5b0677b4d873bde0cd90e8fd8
parent2aadaba580f0253d64a69a5cf4127e50346a34fb (diff)
parent88a0f06ac7f104220b2d92f66599089a9e1d4c92 (diff)
Merge "Handle the 'instance' half of blueprint public-and-private-dns"
-rw-r--r--Authors1
-rw-r--r--nova/flags.py5
-rw-r--r--nova/network/instance_dns_driver.py41
-rw-r--r--nova/network/manager.py11
-rw-r--r--nova/network/minidns.py134
-rw-r--r--nova/tests/test_network.py62
6 files changed, 254 insertions, 0 deletions
diff --git a/Authors b/Authors
index adc7c9c95..c476240ec 100644
--- a/Authors
+++ b/Authors
@@ -6,6 +6,7 @@ Ahmad Hassan <ahmad.hassan@hp.com>
Alex Meade <alex.meade@rackspace.com>
Alexander Sakhnov <asakhnov@mirantis.com>
Alvaro Lopez Garcia <aloga@ifca.unican.es>
+Andrew Bogott <abogott@wikimedia.org>
Andrey Brindeyev <abrindeyev@griddynamics.com>
Andy Smith <code@term.ie>
Andy Southgate <andy.southgate@citrix.com>
diff --git a/nova/flags.py b/nova/flags.py
index dc4e648f0..97933a707 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -397,6 +397,11 @@ DEFINE_string('compute_manager', 'nova.compute.manager.ComputeManager',
'Manager for compute')
DEFINE_string('console_manager', 'nova.console.manager.ConsoleProxyManager',
'Manager for console proxy')
+DEFINE_string('instance_dns_manager',
+ 'nova.network.instance_dns_driver.InstanceDNSManagerDriver',
+ 'DNS Manager for instance IPs')
+DEFINE_string('instance_dns_zone', '',
+ 'DNS Zone for instance IPs')
DEFINE_string('network_manager', 'nova.network.manager.VlanManager',
'Manager for network')
DEFINE_string('volume_manager', 'nova.volume.manager.VolumeManager',
diff --git a/nova/network/instance_dns_driver.py b/nova/network/instance_dns_driver.py
new file mode 100644
index 000000000..c0c6af893
--- /dev/null
+++ b/nova/network/instance_dns_driver.py
@@ -0,0 +1,41 @@
+# Copyright 2011 Andrew Bogott for the Wikimedia Foundation
+#
+# 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.
+
+
+class InstanceDNSManagerDriver(object):
+ """ Defines the instance DNS interface. Does nothing. """
+
+ def __init__(self):
+ pass
+
+ def get_zones(self):
+ pass
+
+ def create_entry(self, _name, _address, _type, _dnszone):
+ pass
+
+ def delete_entry(self, _name, _dnszone=""):
+ pass
+
+ def rename_entry(self, _address, _name, _dnszone):
+ pass
+
+ def modify_address(self, _name, _address, _dnszone):
+ pass
+
+ def get_entries_by_address(self, _address, _dnszone=""):
+ return []
+
+ def get_entries_by_name(self, _name, _dnszone=""):
+ return []
diff --git a/nova/network/manager.py b/nova/network/manager.py
index b3e2e55f9..2d62581e0 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -466,6 +466,8 @@ class NetworkManager(manager.SchedulerDependentManager):
if not network_driver:
network_driver = FLAGS.network_driver
self.driver = utils.import_object(network_driver)
+ temp = utils.import_object(FLAGS.instance_dns_manager)
+ self.instance_dns_manager = temp
self.network_api = network_api.API()
self.compute_api = compute_api.API()
super(NetworkManager, self).__init__(service_name='network',
@@ -822,6 +824,11 @@ class NetworkManager(manager.SchedulerDependentManager):
'virtual_interface_id': vif['id']}
self.db.fixed_ip_update(context, address, values)
+ instance_ref = self.db.instance_get(context, instance_id)
+ name = instance_ref['display_name']
+ self.instance_dns_manager.create_entry(name, address,
+ "type", FLAGS.instance_dns_zone)
+
self._setup_network(context, network)
return address
@@ -835,6 +842,10 @@ class NetworkManager(manager.SchedulerDependentManager):
instance_id = instance_ref['id']
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
+
+ for name in self.instance_dns_manager.get_entries_by_address(address):
+ self.instance_dns_manager.delete_entry(name)
+
if FLAGS.force_dhcp_release:
dev = self.driver.get_dev(fixed_ip_ref['network'])
vif = self.db.virtual_interface_get_by_instance_and_network(
diff --git a/nova/network/minidns.py b/nova/network/minidns.py
new file mode 100644
index 000000000..3b9331318
--- /dev/null
+++ b/nova/network/minidns.py
@@ -0,0 +1,134 @@
+# Copyright 2011 Andrew Bogott for the Wikimedia Foundation
+#
+# 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.
+
+import os
+import shutil
+import tempfile
+
+from nova import flags
+
+
+class MiniDNS(object):
+ """ Trivial DNS driver. This will read/write to a local, flat file
+ and have no effect on your actual DNS system. This class is
+ strictly for testing purposes, and should keep you out of dependency
+ hell.
+
+ Note that there is almost certainly a race condition here that
+ will manifest anytime instances are rapidly created and deleted.
+ A proper implementation will need some manner of locking."""
+
+ def __init__(self):
+ if flags.FLAGS.logdir:
+ self.filename = os.path.join(flags.FLAGS.logdir, "dnstest.txt")
+ else:
+ self.filename = "dnstest.txt"
+
+ if not os.path.exists(self.filename):
+ f = open(self.filename, "w+")
+ f.write("# minidns\n\n\n")
+ f.close()
+
+ def get_zones(self):
+ return ["zone1.example.org", "zone2.example.org", "zone3.example.org"]
+
+ def qualify(self, name, zone):
+ if zone:
+ qualified = "%s.%s" % (name, zone)
+ else:
+ qualified = name
+
+ return qualified
+
+ def create_entry(self, name, address, type, dnszone):
+ outfile = open(self.filename, 'a+')
+ outfile.write("%s %s %s\n" %
+ (address, self.qualify(name, dnszone), type))
+ outfile.close()
+
+ def parse_line(self, line):
+ vals = line.split()
+ if len(vals) < 3:
+ return None
+ else:
+ entry = {}
+ entry['address'] = vals[0]
+ entry['name'] = vals[1]
+ entry['type'] = vals[2]
+ return entry
+
+ def delete_entry(self, name, dnszone=""):
+ infile = open(self.filename, 'r')
+ outfile = tempfile.NamedTemporaryFile('w', delete=False)
+ for line in infile:
+ entry = self.parse_line(line)
+ if ((not entry) or
+ entry['name'] != self.qualify(name, dnszone).lower()):
+ outfile.write(line)
+ infile.close()
+ outfile.close()
+ shutil.move(outfile.name, self.filename)
+
+ def rename_entry(self, address, name, dnszone):
+ infile = open(self.filename, 'r')
+ outfile = tempfile.NamedTemporaryFile('w', delete=False)
+ for line in infile:
+ entry = self.parse_line(line)
+ if entry and entry['address'] == address.lower():
+ outfile.write("%s %s %s\n" %
+ (address, self.qualify(name, dnszone), entry['type']))
+ else:
+ outfile.write(line)
+ infile.close()
+ outfile.close()
+ shutil.move(outfile.name, self.filename)
+
+ def modify_address(self, name, address, dnszone):
+ infile = open(self.filename, 'r')
+ outfile = tempfile.NamedTemporaryFile('w', delete=False)
+ for line in infile:
+ entry = self.parse_line(line)
+ if (entry and
+ entry['name'].lower() == self.qualify(name, dnszone).lower()):
+ outfile.write("%s %s %s\n" %
+ (address, self.qualify(name, dnszone), entry['type']))
+ else:
+ outfile.write(line)
+ infile.close()
+ outfile.close()
+ shutil.move(outfile.name, self.filename)
+
+ def get_entries_by_address(self, address, _dnszone=""):
+ entries = []
+ infile = open(self.filename, 'r')
+ for line in infile:
+ entry = self.parse_line(line)
+ if entry and entry['address'].lower() == address.lower():
+ entries.append(entry['name'])
+ infile.close()
+ return entries
+
+ def get_entries_by_name(self, name, dnszone=""):
+ entries = []
+ infile = open(self.filename, 'r')
+ for line in infile:
+ entry = self.parse_line(line)
+ if (entry and
+ entry['name'].lower() == self.qualify(name, dnszone).lower()):
+ entries.append(entry['address'])
+ infile.close()
+ return entries
+
+ def delete_dns_file(self):
+ os.remove(self.filename)
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 1cb049e4b..dfcc13f45 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -22,6 +22,7 @@ from nova import exception
from nova import log as logging
from nova import rpc
from nova import test
+from nova import utils
from nova.network import manager as network_manager
from nova.tests import fake_network
@@ -133,10 +134,16 @@ class FlatNetworkTestCase(test.TestCase):
def setUp(self):
super(FlatNetworkTestCase, self).setUp()
self.network = network_manager.FlatManager(host=HOST)
+ temp = utils.import_object('nova.network.minidns.MiniDNS')
+ self.network.instance_dns_manager = temp
self.network.db = db
self.context = context.RequestContext('testuser', 'testproject',
is_admin=False)
+ def tearDown(self):
+ super(FlatNetworkTestCase, self).tearDown()
+ self.network.instance_dns_manager.delete_dns_file()
+
def test_get_instance_nw_info(self):
fake_get_instance_nw_info = fake_network.fake_get_instance_nw_info
@@ -272,6 +279,8 @@ class FlatNetworkTestCase(test.TestCase):
db.instance_get(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({'security_groups':
[{'id': 0}]})
+ db.instance_get(self.context,
+ 1).AndReturn({'display_name': HOST})
db.fixed_ip_associate_pool(mox.IgnoreArg(),
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn('192.168.0.101')
@@ -282,6 +291,59 @@ class FlatNetworkTestCase(test.TestCase):
self.network.add_fixed_ip_to_instance(self.context, 1, HOST,
networks[0]['id'])
+ def test_mini_dns_driver(self):
+ driver = self.network.instance_dns_manager
+ driver.create_entry("hostone", "10.0.0.1", 0, "foozone")
+ driver.create_entry("hosttwo", "10.0.0.2", 0, "foozone")
+ driver.create_entry("hostthree", "10.0.0.3", 0, "foozone")
+ driver.create_entry("hostfour", "10.0.0.4", 0, "foozone")
+ driver.delete_entry("hosttwo", "foozone")
+ driver.rename_entry("10.0.0.3", "hostone", "foozone")
+ driver.modify_address("hostfour", "10.0.0.1", "foozone")
+ names = driver.get_entries_by_address("10.0.0.1", "foozone")
+ self.assertEqual(len(names), 2)
+ self.assertIn('hostone.foozone', names)
+ self.assertIn('hostfour.foozone', names)
+ addresses = driver.get_entries_by_name("hostone", "foozone")
+ self.assertEqual(len(addresses), 2)
+ self.assertIn('10.0.0.1', addresses)
+ self.assertIn('10.0.0.3', addresses)
+
+ def test_instance_dns(self):
+ fixedip = '192.168.0.101'
+ self.mox.StubOutWithMock(db, 'network_get')
+ self.mox.StubOutWithMock(db, 'network_update')
+ self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool')
+ self.mox.StubOutWithMock(db, 'instance_get')
+ self.mox.StubOutWithMock(db,
+ 'virtual_interface_get_by_instance_and_network')
+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
+
+ db.fixed_ip_update(mox.IgnoreArg(),
+ mox.IgnoreArg(),
+ mox.IgnoreArg())
+ db.virtual_interface_get_by_instance_and_network(mox.IgnoreArg(),
+ mox.IgnoreArg(), mox.IgnoreArg()).AndReturn({'id': 0})
+
+ db.instance_get(mox.IgnoreArg(),
+ mox.IgnoreArg()).AndReturn({'security_groups':
+ [{'id': 0}]})
+
+ db.instance_get(self.context,
+ 1).AndReturn({'display_name': HOST})
+ db.fixed_ip_associate_pool(mox.IgnoreArg(),
+ mox.IgnoreArg(),
+ mox.IgnoreArg()).AndReturn(fixedip)
+ db.network_get(mox.IgnoreArg(),
+ mox.IgnoreArg()).AndReturn(networks[0])
+ db.network_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
+ self.mox.ReplayAll()
+ self.network.add_fixed_ip_to_instance(self.context, 1, HOST,
+ networks[0]['id'])
+ addresses = self.network.instance_dns_manager.get_entries_by_name(HOST)
+ self.assertEqual(len(addresses), 1)
+ self.assertEqual(addresses[0], fixedip)
+
class VlanNetworkTestCase(test.TestCase):
def setUp(self):