diff options
-rw-r--r-- | nova/db/api.py | 4 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 48 | ||||
-rwxr-xr-x | nova/network/linux_net.py | 126 | ||||
-rw-r--r-- | nova/tests/test_db_api.py | 26 | ||||
-rw-r--r-- | nova/tests/test_linux_net.py | 192 |
5 files changed, 182 insertions, 214 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index 286fdc8d0..2a38d026f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -782,9 +782,9 @@ def network_get_all_by_uuids(context, network_uuids, project_id=None): # pylint: disable=C0103 -def network_get_associated_fixed_ips(context, network_id): +def network_get_associated_fixed_ips(context, network_id, host=None): """Get all network's ips that have been associated.""" - return IMPL.network_get_associated_fixed_ips(context, network_id) + return IMPL.network_get_associated_fixed_ips(context, network_id, host) def network_get_by_bridge(context, bridge): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 49810de22..9eda43941 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1990,15 +1990,49 @@ def network_get_all_by_uuids(context, network_uuids, project_id=None): @require_admin_context -def network_get_associated_fixed_ips(context, network_id): +def network_get_associated_fixed_ips(context, network_id, host=None): # FIXME(sirp): since this returns fixed_ips, this would be better named # fixed_ip_get_all_by_network. - return model_query(context, models.FixedIp, read_deleted="no").\ - filter_by(network_id=network_id).\ - filter_by(allocated=True).\ - filter(models.FixedIp.instance_id != None).\ - filter(models.FixedIp.virtual_interface_id != None).\ - all() + # NOTE(vish): The ugly joins here are to solve a performance issue and + # should be removed once we can add and remove leases + # without regenerating the whole list + vif_and = and_(models.VirtualInterface.id == + models.FixedIp.virtual_interface_id, + models.VirtualInterface.deleted == False) + inst_and = and_(models.Instance.id == models.FixedIp.instance_id, + models.Instance.deleted == False) + session = get_session() + query = session.query(models.FixedIp.address, + models.FixedIp.instance_id, + models.FixedIp.network_id, + models.FixedIp.virtual_interface_id, + models.VirtualInterface.address, + models.Instance.hostname, + models.Instance.updated_at, + models.Instance.created_at).\ + filter(models.FixedIp.deleted == False).\ + filter(models.FixedIp.network_id == network_id).\ + filter(models.FixedIp.allocated == True).\ + join((models.VirtualInterface, vif_and)).\ + join((models.Instance, inst_and)).\ + filter(models.FixedIp.instance_id != None).\ + filter(models.FixedIp.virtual_interface_id != None) + if host: + query = query.filter(models.Instance.host == host) + result = query.all() + data = [] + for datum in result: + cleaned = {} + cleaned['address'] = datum[0] + cleaned['instance_id'] = datum[1] + cleaned['network_id'] = datum[2] + cleaned['vif_id'] = datum[3] + cleaned['vif_address'] = datum[4] + cleaned['instance_hostname'] = datum[5] + cleaned['instance_updated'] = datum[6] + cleaned['instance_created'] = datum[7] + data.append(cleaned) + return data @require_admin_context diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 5a4c5cdfa..7258e627a 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -567,44 +567,26 @@ def initialize_gateway_device(dev, network_ref): def get_dhcp_leases(context, network_ref): """Return a network's hosts config in dnsmasq leasefile format.""" hosts = [] - for fixed_ref in db.network_get_associated_fixed_ips(context, - network_ref['id']): - vif_id = fixed_ref['virtual_interface_id'] - # NOTE(jkoelker) We need a larger refactor to happen to prevent - # looking these up here - vif_ref = db.virtual_interface_get(context, vif_id) - instance_id = fixed_ref['instance_id'] - try: - instance_ref = db.instance_get(context, instance_id) - except exception.InstanceNotFound: - msg = _("Instance %(instance_id)s not found") - LOG.debug(msg % {'instance_id': instance_id}) - continue - if network_ref['multi_host'] and FLAGS.host != instance_ref['host']: - continue - hosts.append(_host_lease(fixed_ref, vif_ref, instance_ref)) + host = None + if network_ref['multi_host']: + host = FLAGS.host + for data in db.network_get_associated_fixed_ips(context, + network_ref['id'], + host=host): + hosts.append(_host_lease(data)) return '\n'.join(hosts) def get_dhcp_hosts(context, network_ref): """Get network's hosts config in dhcp-host format.""" hosts = [] - for fixed_ref in db.network_get_associated_fixed_ips(context, - network_ref['id']): - vif_id = fixed_ref['virtual_interface_id'] - # NOTE(jkoelker) We need a larger refactor to happen to prevent - # looking these up here - vif_ref = db.virtual_interface_get(context, vif_id) - instance_id = fixed_ref['instance_id'] - try: - instance_ref = db.instance_get(context, instance_id) - except exception.InstanceNotFound: - msg = _("Instance %(instance_id)s not found") - LOG.debug(msg % {'instance_id': instance_id}) - continue - if network_ref['multi_host'] and FLAGS.host != instance_ref['host']: - continue - hosts.append(_host_dhcp(fixed_ref, vif_ref, instance_ref)) + host = None + if network_ref['multi_host']: + host = FLAGS.host + for data in db.network_get_associated_fixed_ips(context, + network_ref['id'], + host=host): + hosts.append(_host_dhcp(data)) return '\n'.join(hosts) @@ -623,34 +605,28 @@ def _add_dnsmasq_accept_rules(dev): 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: + host = None + if network_ref['multi_host']: + host = FLAGS.host + data = db.network_get_associated_fixed_ips(context, + network_ref['id'], + host=host) + + if data: #set of instance ids - instance_set = set([fixed_ip_ref['instance_id'] - for fixed_ip_ref in ips_ref]) - default_gw_network_node = {} + instance_set = set([datum['instance_id'] for datum in data]) + default_gw_vif = {} 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'] + default_gw_vif[instance_id] = vifs[0]['id'] - for fixed_ip_ref in ips_ref: - instance_id = fixed_ip_ref['instance_id'] - try: - instance_ref = db.instance_get(context, instance_id) - except exception.InstanceNotFound: - msg = _("Instance %(instance_id)s not found") - LOG.debug(msg % {'instance_id': instance_id}) - continue - - if instance_id in default_gw_network_node: - target_network_id = default_gw_network_node[instance_id] + for datum in data: + if instance_id in default_gw_vif: # 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, - instance_ref)) + if default_gw_vif[instance_id] != datum['vif_id']: + hosts.append(_host_dhcp_opts(datum)) return '\n'.join(hosts) @@ -689,6 +665,8 @@ def restart_dhcp(context, dev, network_ref): conffile = _dhcp_file(dev, 'conf') if FLAGS.use_single_default_gateway: + # NOTE(vish): this will have serious performance implications if we + # are not in multi_host mode. optsfile = _dhcp_file(dev, 'opts') write_to_file(optsfile, get_dhcp_opts(context, network_ref)) os.chmod(optsfile, 0644) @@ -782,45 +760,43 @@ interface %s _execute(*cmd, run_as_root=True) -def _host_lease(fixed_ip_ref, vif_ref, instance_ref): +def _host_lease(data): """Return a host string for an address in leasefile format.""" - if instance_ref['updated_at']: - timestamp = instance_ref['updated_at'] + if data['instance_updated']: + timestamp = data['instance_updated'] else: - timestamp = instance_ref['created_at'] + timestamp = data['instance_created'] seconds_since_epoch = calendar.timegm(timestamp.utctimetuple()) return '%d %s %s %s *' % (seconds_since_epoch + FLAGS.dhcp_lease_time, - vif_ref['address'], - fixed_ip_ref['address'], - instance_ref['hostname'] or '*') + data['vif_address'], + data['address'], + data['instance_hostname'] or '*') -def _host_dhcp_network(fixed_ip_ref, instance_ref): - return 'NW-i%08d-%s' % (instance_ref['id'], - fixed_ip_ref['network_id']) +def _host_dhcp_network(data): + return 'NW-%s' % data['vif_id'] -def _host_dhcp(fixed_ip_ref, vif_ref, instance_ref): +def _host_dhcp(data): """Return a host string for an address in dhcp-host format.""" if FLAGS.use_single_default_gateway: - return '%s,%s.%s,%s,%s' % (vif_ref['address'], - instance_ref['hostname'], + return '%s,%s.%s,%s,%s' % (data['vif_address'], + data['instance_hostname'], FLAGS.dhcp_domain, - fixed_ip_ref['address'], - "net:" + _host_dhcp_network(fixed_ip_ref, - instance_ref)) + data['address'], + "net:" + _host_dhcp_network(data)) else: - return '%s,%s.%s,%s' % (vif_ref['address'], - instance_ref['hostname'], + return '%s,%s.%s,%s' % (data['vif_address'], + data['instance_hostname'], FLAGS.dhcp_domain, - fixed_ip_ref['address']) + data['address']) -def _host_dhcp_opts(fixed_ip_ref, instance_ref): - """Return a host string for an address in dhcp-host format.""" - return '%s,%s' % (_host_dhcp_network(fixed_ip_ref, instance_ref), 3) +def _host_dhcp_opts(data): + """Return an empty gateway option.""" + return '%s,%s' % (_host_dhcp_network(data), 3) def _execute(*cmd, **kwargs): diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 099e2b8a8..9e5e3b0ad 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -281,6 +281,32 @@ class DbApiTestCase(test.TestCase): db.dnsdomain_unregister(ctxt, domain1) db.dnsdomain_unregister(ctxt, domain2) + def test_network_get_associated_fixed_ips(self): + ctxt = context.get_admin_context() + values = {'host': 'foo', 'hostname': 'myname'} + instance = db.instance_create(ctxt, values) + values = {'address': 'bar', 'instance_id': instance['id']} + vif = db.virtual_interface_create(ctxt, values) + values = {'address': 'baz', + 'network_id': 1, + 'allocated': True, + 'instance_id': instance['id'], + 'virtual_interface_id': vif['id']} + fixed_address = db.fixed_ip_create(ctxt, values) + data = db.network_get_associated_fixed_ips(ctxt, 1) + self.assertEqual(len(data), 1) + record = data[0] + self.assertEqual(record['address'], fixed_address) + self.assertEqual(record['instance_id'], instance['id']) + self.assertEqual(record['network_id'], 1) + self.assertEqual(record['instance_created'], instance['created_at']) + self.assertEqual(record['instance_updated'], instance['updated_at']) + self.assertEqual(record['instance_hostname'], instance['hostname']) + self.assertEqual(record['vif_id'], vif['id']) + self.assertEqual(record['vif_address'], vif['address']) + data = db.network_get_associated_fixed_ips(ctxt, 1, 'nothing') + self.assertEqual(len(data), 0) + def _get_fake_aggr_values(): return {'name': 'fake_aggregate', diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index d50bae68d..f760acbec 100644 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -37,9 +37,13 @@ HOST = "testhost" instances = [{'id': 0, 'host': 'fake_instance00', + 'created_at': 'fakedate', + 'updated_at': 'fakedate', 'hostname': 'fake_instance00'}, {'id': 1, 'host': 'fake_instance01', + 'created_at': 'fakedate', + 'updated_at': 'fakedate', 'hostname': 'fake_instance01'}] @@ -77,7 +81,7 @@ networks = [{'id': 0, 'uuid': "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", 'label': 'test1', 'injected': False, - 'multi_host': False, + 'multi_host': True, 'cidr': '192.168.1.0/24', 'cidr_v6': '2001:db9::/64', 'gateway_v6': '2001:db9::1', @@ -179,6 +183,29 @@ vifs = [{'id': 0, 'instance_id': 1}] +def get_associated(context, network_id, host=None): + result = [] + for datum in fixed_ips: + if (datum['network_id'] == network_id and datum['allocated'] + and datum['instance_id'] is not None + and datum['virtual_interface_id'] is not None): + instance = instances[datum['instance_id']] + if host and host != instance['host']: + continue + cleaned = {} + cleaned['address'] = datum['address'] + cleaned['instance_id'] = datum['instance_id'] + cleaned['network_id'] = datum['network_id'] + cleaned['vif_id'] = datum['virtual_interface_id'] + vif = vifs[datum['virtual_interface_id']] + cleaned['vif_address'] = vif['address'] + cleaned['instance_hostname'] = instance['hostname'] + cleaned['instance_updated'] = instance['updated_at'] + cleaned['instance_created'] = instance['created_at'] + result.append(cleaned) + return result + + class LinuxNetworkTestCase(test.TestCase): def setUp(self): @@ -189,36 +216,23 @@ class LinuxNetworkTestCase(test.TestCase): self.context = context.RequestContext('testuser', 'testproject', is_admin=True) - def test_update_dhcp_for_nw00(self): - self.flags(use_single_default_gateway=True) - - def get_vif(_context, vif_id): - return vifs[vif_id] + def get_vifs(_context, instance_id): + return [vif for vif in vifs if vif['instance_id'] == instance_id] def get_instance(_context, instance_id): return instances[instance_id] - self.stubs.Set(db, 'virtual_interface_get', get_vif) + self.stubs.Set(db, 'virtual_interface_get_by_instance', get_vifs) self.stubs.Set(db, 'instance_get', get_instance) - self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') + self.stubs.Set(db, 'network_get_associated_fixed_ips', get_associated) + + def test_update_dhcp_for_nw00(self): + self.flags(use_single_default_gateway=True) + self.mox.StubOutWithMock(self.driver, 'write_to_file') self.mox.StubOutWithMock(self.driver, 'ensure_path') self.mox.StubOutWithMock(os, 'chmod') - 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.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg()) self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg()) self.driver.ensure_path(mox.IgnoreArg()) @@ -238,33 +252,10 @@ class LinuxNetworkTestCase(test.TestCase): def test_update_dhcp_for_nw01(self): self.flags(use_single_default_gateway=True) - def get_vif(_context, vif_id): - return vifs[vif_id] - - def get_instance(_context, instance_id): - return instances[instance_id] - - self.stubs.Set(db, 'virtual_interface_get', get_vif) - self.stubs.Set(db, 'instance_get', get_instance) - self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') self.mox.StubOutWithMock(self.driver, 'write_to_file') self.mox.StubOutWithMock(self.driver, 'ensure_path') self.mox.StubOutWithMock(os, 'chmod') - 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.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg()) self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg()) self.driver.ensure_path(mox.IgnoreArg()) @@ -284,116 +275,57 @@ class LinuxNetworkTestCase(test.TestCase): def test_get_dhcp_hosts_for_nw00(self): self.flags(use_single_default_gateway=True) - def get_vif(_context, vif_id): - return vifs[vif_id] - - def get_instance(_context, instance_id): - return instances[instance_id] - - self.stubs.Set(db, 'virtual_interface_get', get_vif) - self.stubs.Set(db, 'instance_get', get_instance) - 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 = ("DE:AD:BE:EF:00:00,fake_instance00.novalocal," - "192.168.0.100,net:NW-i00000000-0\n" - "DE:AD:BE:EF:00:03,fake_instance01.novalocal," - "192.168.1.101,net:NW-i00000001-0") - actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[1]) + expected = ( + "DE:AD:BE:EF:00:00,fake_instance00.novalocal," + "192.168.0.100,net:NW-0\n" + "DE:AD:BE:EF:00:03,fake_instance01.novalocal," + "192.168.1.101,net:NW-3\n" + "DE:AD:BE:EF:00:04,fake_instance00.novalocal," + "192.168.0.102,net:NW-4" + ) + actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[0]) self.assertEquals(actual_hosts, expected) def test_get_dhcp_hosts_for_nw01(self): self.flags(use_single_default_gateway=True) - - def get_vif(_context, vif_id): - return vifs[vif_id] - - def get_instance(_context, instance_id): - return instances[instance_id] - - self.stubs.Set(db, 'virtual_interface_get', get_vif) - self.stubs.Set(db, 'instance_get', get_instance) - 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 = ("DE:AD:BE:EF:00:01,fake_instance00.novalocal," - "192.168.1.100,net:NW-i00000000-1\n" - "DE:AD:BE:EF:00:02,fake_instance01.novalocal," - "192.168.0.101,net:NW-i00000001-1") - actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[0]) + self.flags(host='fake_instance01') + + expected = ( + "DE:AD:BE:EF:00:02,fake_instance01.novalocal," + "192.168.0.101,net:NW-2\n" + "DE:AD:BE:EF:00:05,fake_instance01.novalocal," + "192.168.1.102,net:NW-5" + ) + actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[1]) self.assertEquals(actual_hosts, expected) def test_get_dhcp_opts_for_nw00(self): - def get_instance(_context, instance_id): - return instances[instance_id] - - self.stubs.Set(db, 'instance_get', get_instance) - 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' + expected_opts = 'NW-0,3\nNW-3,3\nNW-4,3' actual_opts = self.driver.get_dhcp_opts(self.context, networks[0]) self.assertEquals(actual_opts, expected_opts) def test_get_dhcp_opts_for_nw01(self): - def get_instance(_context, instance_id): - print instance_id - return instances[instance_id] - - self.stubs.Set(db, 'instance_get', get_instance) - 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" + self.flags(host='fake_instance01') + expected_opts = "NW-5,3" actual_opts = self.driver.get_dhcp_opts(self.context, 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], instances[0]) + expected = "NW-0,3" + data = get_associated(self.context, 0)[0] + actual = self.driver._host_dhcp_opts(data) self.assertEquals(actual, expected) def test_host_dhcp_without_default_gateway_network(self): expected = ','.join(['DE:AD:BE:EF:00:00', 'fake_instance00.novalocal', '192.168.0.100']) - actual = self.driver._host_dhcp(fixed_ips[0], vifs[0], instances[0]) + data = get_associated(self.context, 0)[0] + actual = self.driver._host_dhcp(data) self.assertEquals(actual, expected) def test_linux_bridge_driver_plug(self): |