From 82804eb46542b2fdaef8b6db8696b50dbf5b7210 Mon Sep 17 00:00:00 2001 From: Jason Kölker Date: Mon, 14 Nov 2011 13:14:58 -0600 Subject: Remove VIF<->Network FK dependancy Related to blueprint untie-nova-network-models. Depends-On: I665f402fe0ab1b301ab6761e80b11b101656065e Change-Id: I9bfb957effe0a2420c312add1cca5d7af509579d --- nova/db/sqlalchemy/api.py | 8 --- .../versions/060_remove_network_fk_from_vif.py | 60 ++++++++++++++++++++++ .../migrate_repo/versions/060_sqlite_downgrade.sql | 46 +++++++++++++++++ .../migrate_repo/versions/060_sqlite_upgrade.sql | 44 ++++++++++++++++ nova/db/sqlalchemy/models.py | 16 +----- nova/network/manager.py | 10 +++- nova/tests/fake_network.py | 21 ++++++-- nova/tests/test_network.py | 55 +++++++++++--------- 8 files changed, 209 insertions(+), 51 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/060_remove_network_fk_from_vif.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_downgrade.sql create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_upgrade.sql diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7f142defd..8cd8daf1c 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -984,7 +984,6 @@ def virtual_interface_get(context, vif_id, session=None): vif_ref = session.query(models.VirtualInterface).\ filter_by(id=vif_id).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ first() return vif_ref @@ -999,7 +998,6 @@ def virtual_interface_get_by_address(context, address): session = get_session() vif_ref = session.query(models.VirtualInterface).\ filter_by(address=address).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ first() return vif_ref @@ -1014,7 +1012,6 @@ def virtual_interface_get_by_uuid(context, vif_uuid): session = get_session() vif_ref = session.query(models.VirtualInterface).\ filter_by(uuid=vif_uuid).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ first() return vif_ref @@ -1029,7 +1026,6 @@ def virtual_interface_get_by_fixed_ip(context, fixed_ip_id): session = get_session() vif_ref = session.query(models.VirtualInterface).\ filter_by(fixed_ip_id=fixed_ip_id).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ first() return vif_ref @@ -1045,7 +1041,6 @@ def virtual_interface_get_by_instance(context, instance_id): session = get_session() vif_refs = session.query(models.VirtualInterface).\ filter_by(instance_id=instance_id).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ all() return vif_refs @@ -1059,7 +1054,6 @@ def virtual_interface_get_by_instance_and_network(context, instance_id, vif_ref = session.query(models.VirtualInterface).\ filter_by(instance_id=instance_id).\ filter_by(network_id=network_id).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ first() return vif_ref @@ -1074,7 +1068,6 @@ def virtual_interface_get_by_network(context, network_id): session = get_session() vif_refs = session.query(models.VirtualInterface).\ filter_by(network_id=network_id).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ all() return vif_refs @@ -1109,7 +1102,6 @@ def virtual_interface_get_all(context): """Get all vifs""" session = get_session() vif_refs = session.query(models.VirtualInterface).\ - options(joinedload('network')).\ options(joinedload('fixed_ips')).\ all() return vif_refs diff --git a/nova/db/sqlalchemy/migrate_repo/versions/060_remove_network_fk_from_vif.py b/nova/db/sqlalchemy/migrate_repo/versions/060_remove_network_fk_from_vif.py new file mode 100644 index 000000000..7b4c470a7 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/060_remove_network_fk_from_vif.py @@ -0,0 +1,60 @@ +# Copyright 2011 OpenStack LLC. +# +# 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 sqlalchemy import Column, Integer, MetaData, Table +from migrate import ForeignKeyConstraint + +from nova import log as logging + +meta = MetaData() + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + if dialect.startswith('sqlite'): + return + + networks = Table('networks', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + + try: + fkey_name = list(vifs.c.network_id.foreign_keys)[0].constraint.name + ForeignKeyConstraint(columns=[vifs.c.network_id], + refcolumns=[networks.c.id], + name=fkey_name).drop() + + except Exception: + logging.error(_("foreign key constraint couldn't be removed")) + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + if dialect.startswith('sqlite'): + return + + networks = Table('networks', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + + try: + ForeignKeyConstraint(columns=[vifs.c.network_id], + refcolumns=[networks.c.id]).create() + except Exception: + logging.error(_("foreign key constraint couldn't be added")) + raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_downgrade.sql new file mode 100644 index 000000000..c804e3462 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_downgrade.sql @@ -0,0 +1,46 @@ +COMMIT; +BEGIN TRANSACTION; + CREATE TEMPORARY TABLE virtual_interfaces_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + network_id INTEGER, + instance_id INTEGER NOT NULL, + uuid VARCHAR(36), + PRIMARY KEY (id) + ); + + INSERT INTO virtual_interfaces_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + network_id, instance_id, uuid + FROM virtual_interfaces; + + DROP TABLE virtual_interfaces; + + CREATE TABLE virtual_interfaces ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + network_id INTEGER, + instance_id INTEGER NOT NULL, + uuid VARCHAR(36), + PRIMARY KEY (id), + FOREIGN KEY(network_id) REFERENCES networks (id), + UNIQUE (address), + CHECK (deleted IN (0, 1)) + ); + + INSERT INTO virtual_interfaces + SELECT created_at, updated_at, deleted_at, deleted, id, address, + network_id, instance_id, uuid + FROM virtual_interfaces_backup; + + DROP TABLE virtual_interfaces_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_upgrade.sql new file mode 100644 index 000000000..fd49ea4f5 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/060_sqlite_upgrade.sql @@ -0,0 +1,44 @@ +BEGIN TRANSACTION; + CREATE TEMPORARY TABLE virtual_interfaces_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + network_id INTEGER, + instance_id INTEGER NOT NULL, + uuid VARCHAR(36), + PRIMARY KEY (id) + ); + + INSERT INTO virtual_interfaces_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + network_id, instance_id, uuid + FROM virtual_interfaces; + + DROP TABLE virtual_interfaces; + + CREATE TABLE virtual_interfaces ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + network_id INTEGER, + instance_id INTEGER NOT NULL, + uuid VARCHAR(36), + PRIMARY KEY (id), + UNIQUE (address), + CHECK (deleted IN (0, 1)) + ); + + INSERT INTO virtual_interfaces + SELECT created_at, updated_at, deleted_at, deleted, id, address, + network_id, instance_id, uuid + FROM virtual_interfaces_backup; + + DROP TABLE virtual_interfaces_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 19ef8b3af..1505fca5f 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -645,24 +645,10 @@ class VirtualInterface(BASE, NovaBase): __tablename__ = 'virtual_interfaces' id = Column(Integer, primary_key=True) address = Column(String(255), unique=True) - network_id = Column(Integer, ForeignKey('networks.id')) - network = relationship(Network, backref=backref('virtual_interfaces')) + network_id = Column(Integer, nullable=False) instance_id = Column(Integer, nullable=False) - uuid = Column(String(36)) - @property - def fixed_ipv6(self): - cidr_v6 = self.network.cidr_v6 - if cidr_v6 is None: - ipv6_address = None - else: - project_id = self.instance.project_id - mac = self.address - ipv6_address = ipv6.to_global(cidr_v6, mac, project_id) - - return ipv6_address - # TODO(vish): can these both come from the same baseclass? class FixedIp(BASE, NovaBase): diff --git a/nova/network/manager.py b/nova/network/manager.py index f782593bd..20f42dc01 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -557,7 +557,13 @@ class NetworkManager(manager.SchedulerDependentManager): if vif['instance_id'] is None: continue - fixed_ipv6 = vif.get('fixed_ipv6') + network = self.db.network_get(context, vif['network_id']) + fixed_ipv6 = None + if network['cidr_v6'] is not None: + fixed_ipv6 = ipv6.to_global(network['cidr_v6'], + vif['address'], + context.project_id) + if fixed_ipv6 and ipv6_filter.match(fixed_ipv6): # NOTE(jkoelker) Will need to update for the UUID flip results.append({'instance_id': vif['instance_id'], @@ -675,7 +681,7 @@ class NetworkManager(manager.SchedulerDependentManager): # a vif has an address, instance_id, and network_id # it is also joined to the instance and network given by those IDs for vif in vifs: - network = vif['network'] + network = self.db.network_get(context, vif['network_id']) if network is None: continue diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index bf12740e3..e1af85239 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -83,6 +83,9 @@ class FakeNetworkManager(network_manager.NetworkManager): fakenet['id'] = 999 return fakenet + def network_get(self, context, network_id): + return {'cidr_v6': '2001:db8:69:%x::/64' % network_id} + def network_get_all(self, context): raise exception.NoNetworksFound() @@ -92,15 +95,18 @@ class FakeNetworkManager(network_manager.NetworkManager): {'address': '173.16.1.2'}] vifs = [{'instance_id': 0, - 'fixed_ipv6': '2001:db8::dcad:beff:feef:1', + 'network_id': 1, + 'address': 'DC:AD:BE:FF:EF:01', 'fixed_ips': [{'address': '172.16.0.1', 'floating_ips': [floats[0]]}]}, {'instance_id': 20, - 'fixed_ipv6': '2001:db8::dcad:beff:feef:2', + 'network_id': 21, + 'address': 'DC:AD:BE:FF:EF:02', 'fixed_ips': [{'address': '172.16.0.2', 'floating_ips': [floats[1]]}]}, {'instance_id': 30, - 'fixed_ipv6': '2002:db8::dcad:beff:feef:2', + 'network_id': 31, + 'address': 'DC:AD:BE:FF:EF:03', 'fixed_ips': [{'address': '173.16.0.2', 'floating_ips': [floats[2]]}]}] return vifs @@ -236,6 +242,8 @@ def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2, floating_ip_id = floating_ip_ids() fixed_ip_id = fixed_ip_ids() + networks = [fake_network(x) for x in xrange(num_networks)] + def fixed_ips_fake(*args, **kwargs): return [next_fixed_ip(i, floating_ips_per_fixed_ip) for i in xrange(num_networks) for j in xrange(ips_per_vif)] @@ -246,8 +254,15 @@ def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2, def instance_type_fake(*args, **kwargs): return flavor + def network_get_fake(context, network_id): + nets = [n for n in networks if n['id'] == network_id] + if not nets: + raise exception.NetworkNotFound(network_id=network_id) + return nets[0] + stubs.Set(db, 'fixed_ip_get_by_instance', fixed_ips_fake) stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) stubs.Set(db, 'instance_type_get', instance_type_fake) + stubs.Set(db, 'network_get', network_get_fake) return network.get_instance_nw_info(None, 0, 0, None) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 9e12ed765..1cb049e4b 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -889,40 +889,43 @@ class CommonNetworkTestCase(test.TestCase): def test_get_instance_uuids_by_ip_regex(self): manager = fake_network.FakeNetworkManager() _vifs = manager.db.virtual_interface_get_all(None) + fake_context = context.RequestContext('user', 'project') # Greedy get eveything - res = manager.get_instance_uuids_by_ip_filter(None, {'ip': '.*'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '.*'}) self.assertEqual(len(res), len(_vifs)) # Doesn't exist - res = manager.get_instance_uuids_by_ip_filter(None, {'ip': '10.0.0.1'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '10.0.0.1'}) self.assertFalse(res) # Get instance 1 - res = manager.get_instance_uuids_by_ip_filter(None, - {'ip': '172.16.0.2'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '172.16.0.2'}) self.assertTrue(res) self.assertEqual(len(res), 1) self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id']) # Get instance 2 - res = manager.get_instance_uuids_by_ip_filter(None, - {'ip': '173.16.0.2'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '173.16.0.2'}) self.assertTrue(res) self.assertEqual(len(res), 1) self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id']) # Get instance 0 and 1 - res = manager.get_instance_uuids_by_ip_filter(None, - {'ip': '172.16.0.*'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '172.16.0.*'}) self.assertTrue(res) self.assertEqual(len(res), 2) self.assertEqual(res[0]['instance_id'], _vifs[0]['instance_id']) self.assertEqual(res[1]['instance_id'], _vifs[1]['instance_id']) # Get instance 1 and 2 - res = manager.get_instance_uuids_by_ip_filter(None, - {'ip': '17..16.0.2'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip': '17..16.0.2'}) self.assertTrue(res) self.assertEqual(len(res), 2) self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id']) @@ -931,40 +934,45 @@ class CommonNetworkTestCase(test.TestCase): def test_get_instance_uuids_by_ipv6_regex(self): manager = fake_network.FakeNetworkManager() _vifs = manager.db.virtual_interface_get_all(None) + fake_context = context.RequestContext('user', 'project') # Greedy get eveything - res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': '.*'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip6': '.*'}) self.assertEqual(len(res), len(_vifs)) # Doesn't exist - res = manager.get_instance_uuids_by_ip_filter(None, + res = manager.get_instance_uuids_by_ip_filter(fake_context, {'ip6': '.*1034.*'}) self.assertFalse(res) # Get instance 1 - res = manager.get_instance_uuids_by_ip_filter(None, - {'ip6': '2001:.*:2'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip6': '2001:.*2'}) self.assertTrue(res) self.assertEqual(len(res), 1) self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id']) # Get instance 2 - ip6 = '2002:db8::dcad:beff:feef:2' - res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': ip6}) + ip6 = '2001:db8:69:1f:dead:beff:feff:ef03' + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip6': ip6}) self.assertTrue(res) self.assertEqual(len(res), 1) self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id']) # Get instance 0 and 1 - res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': '2001:.*'}) + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip6': '.*ef0[1,2]'}) self.assertTrue(res) self.assertEqual(len(res), 2) self.assertEqual(res[0]['instance_id'], _vifs[0]['instance_id']) self.assertEqual(res[1]['instance_id'], _vifs[1]['instance_id']) # Get instance 1 and 2 - ip6 = '200.:db8::dcad:beff:feef:2' - res = manager.get_instance_uuids_by_ip_filter(None, {'ip6': ip6}) + ip6 = '2001:db8:69:1.:dead:beff:feff:ef0.' + res = manager.get_instance_uuids_by_ip_filter(fake_context, + {'ip6': ip6}) self.assertTrue(res) self.assertEqual(len(res), 2) self.assertEqual(res[0]['instance_id'], _vifs[1]['instance_id']) @@ -973,21 +981,22 @@ class CommonNetworkTestCase(test.TestCase): def test_get_instance_uuids_by_ip(self): manager = fake_network.FakeNetworkManager() _vifs = manager.db.virtual_interface_get_all(None) + fake_context = context.RequestContext('user', 'project') # No regex for you! - res = manager.get_instance_uuids_by_ip_filter(None, + res = manager.get_instance_uuids_by_ip_filter(fake_context, {'fixed_ip': '.*'}) self.assertFalse(res) # Doesn't exist ip = '10.0.0.1' - res = manager.get_instance_uuids_by_ip_filter(None, + res = manager.get_instance_uuids_by_ip_filter(fake_context, {'fixed_ip': ip}) self.assertFalse(res) # Get instance 1 ip = '172.16.0.2' - res = manager.get_instance_uuids_by_ip_filter(None, + res = manager.get_instance_uuids_by_ip_filter(fake_context, {'fixed_ip': ip}) self.assertTrue(res) self.assertEqual(len(res), 1) @@ -995,7 +1004,7 @@ class CommonNetworkTestCase(test.TestCase): # Get instance 2 ip = '173.16.0.2' - res = manager.get_instance_uuids_by_ip_filter(None, + res = manager.get_instance_uuids_by_ip_filter(fake_context, {'fixed_ip': ip}) self.assertTrue(res) self.assertEqual(len(res), 1) -- cgit