From 5b245b2c58c7fe80023ebed448f3afe7f0fde909 Mon Sep 17 00:00:00 2001 From: Amir Sadoughi Date: Wed, 27 Feb 2013 18:15:30 -0600 Subject: [PATCH 1/2] Added custom types: INET and MACAddress. --- quark/db/custom_types.py | 21 +++++++++++++++++++++ quark/db/models.py | 8 +++++--- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 quark/db/custom_types.py diff --git a/quark/db/custom_types.py b/quark/db/custom_types.py new file mode 100644 index 0000000..bd4e15a --- /dev/null +++ b/quark/db/custom_types.py @@ -0,0 +1,21 @@ +import sqlalchemy as sa +from sqlalchemy import types +from sqlalchemy.dialects import sqlite + + +class INET(types.TypeDecorator): + impl = types.LargeBinary + + def load_dialect_impl(self, dialect): + if dialect.name == 'sqlite': + return dialect.type_descriptor(sqlite.CHAR) + return dialect.type_descriptor(self.impl) + + +class MACAddress(types.TypeDecorator): + impl = types.BigInteger + + def load_dialect_impl(self, dialect): + if dialect.name == 'sqlite': + return dialect.type_descriptor(sqlite.CHAR) + return dialect.type_descriptor(self.impl) diff --git a/quark/db/models.py b/quark/db/models.py index f161ad4..0cdae71 100644 --- a/quark/db/models.py +++ b/quark/db/models.py @@ -26,6 +26,8 @@ from quantum.db.models_v2 import HasTenant, HasId from quantum.openstack.common import timeutils from quantum.openstack.common import log as logging +from quark.db import custom_types + LOG = logging.getLogger("quantum.quark.db.models") @@ -104,7 +106,7 @@ class IPAddress(BASEV2, CreatedAt, HasId, HasTenant): address_readable = sa.Column(sa.String(128), nullable=False) - address = sa.Column(sa.LargeBinary(16), nullable=False) + address = sa.Column(custom_types.INET(), nullable=False) subnet_id = sa.Column(sa.String(36), sa.ForeignKey("quark_subnets.id", @@ -185,8 +187,8 @@ class Subnet(BASEV2, CreatedAt, HasId, HasTenant, IsHazTags): def cidr(cls): return Subnet._cidr - first_ip = sa.Column(sa.LargeBinary()) - last_ip = sa.Column(sa.LargeBinary()) + first_ip = sa.Column(custom_types.INET()) + last_ip = sa.Column(custom_types.INET()) ip_version = sa.Column(sa.Integer()) allocated_ips = orm.relationship(IPAddress, backref="subnet") From 0ad7b8b0f320c6a059b1e590b952cdca3ba88aa0 Mon Sep 17 00:00:00 2001 From: Amir Sadoughi Date: Wed, 27 Feb 2013 18:22:18 -0600 Subject: [PATCH 2/2] Updated Subnet.allocated_ips to include only IPAddress with _deallocated != 1. --- quark/db/models.py | 6 ++++- quark/ipam.py | 3 ++- quark/plugin.py | 9 ++++--- quark/tests/test_base.py | 6 +++++ quark/tests/test_db_models.py | 51 +++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 quark/tests/test_base.py create mode 100644 quark/tests/test_db_models.py diff --git a/quark/db/models.py b/quark/db/models.py index 0cdae71..3315b2e 100644 --- a/quark/db/models.py +++ b/quark/db/models.py @@ -191,7 +191,11 @@ class Subnet(BASEV2, CreatedAt, HasId, HasTenant, IsHazTags): last_ip = sa.Column(custom_types.INET()) ip_version = sa.Column(sa.Integer()) - allocated_ips = orm.relationship(IPAddress, backref="subnet") + allocated_ips = orm.relationship(IPAddress, + primaryjoin= + 'and_(Subnet.id==IPAddress.subnet_id, ' + 'IPAddress._deallocated!=1)', + backref="subnet") routes = orm.relationship(Route, backref='subnet', cascade='delete') diff --git a/quark/ipam.py b/quark/ipam.py index 8c4e3d6..2aae560 100644 --- a/quark/ipam.py +++ b/quark/ipam.py @@ -65,7 +65,7 @@ class QuarkIpam(object): return deallocated_mac ranges = session.query(models.MacAddressRange, - sql_func.count(models.MacAddress). + sql_func.count(models.MacAddress.address). label("count")).\ outerjoin(models.MacAddress).\ group_by(models.MacAddressRange).\ @@ -128,6 +128,7 @@ class QuarkIpam(object): address["version"] = subnet["ip_version"] address["network_id"] = net_id address["tenant_id"] = subnet["tenant_id"] + address["_deallocated"] = 0 if address: address["port_id"] = port_id diff --git a/quark/plugin.py b/quark/plugin.py index 84a4b88..441ced4 100644 --- a/quark/plugin.py +++ b/quark/plugin.py @@ -50,7 +50,8 @@ quark_opts = [ ] CONF.register_opts(quark_opts, "QUARK") -CONF.set_override('api_extensions_path', ":".join(extensions.__path__)) +if 'api_extensions_path' in CONF: + CONF.set_override('api_extensions_path', ":".join(extensions.__path__)) #NOTE(mdietz): hacking this for now because disallowing subnets on a network # create is absurd. Might be useful to implement the ability @@ -327,7 +328,7 @@ class Plugin(quantum_plugin_base_v2.QuantumPluginBaseV2): subnets = [] if network["network"].get("subnets"): subnets = network["network"].pop("subnets") - new_net = models.Network(id=net_uuid) + new_net = models.Network(id=net_uuid, tenant_id=context.tenant_id) new_net.update(network["network"]) for sub in subnets: @@ -501,6 +502,7 @@ class Plugin(quantum_plugin_base_v2.QuantumPluginBaseV2): new_port["backend_key"] = backend_port["uuid"] new_port["addresses"] = [addresses] new_port["mac_address"] = mac["address"] + new_port["tenant_id"] = context.tenant_id session.add(new_port) new_port["mac_address"] = str(netaddr.EUI(new_port["mac_address"], @@ -641,8 +643,9 @@ class Plugin(quantum_plugin_base_v2.QuantumPluginBaseV2): raise exceptions.NetworkNotFound(net_id=id) backend_key = port["backend_key"] + mac_address = netaddr.EUI(port["mac_address"]).value self.ipam_driver.deallocate_mac_address(session, - port["mac_address"],) + mac_address,) self.ipam_driver.deallocate_ip_address(session, id, ipam_reuse_after=self.ipam_reuse_after) session.delete(port) diff --git a/quark/tests/test_base.py b/quark/tests/test_base.py new file mode 100644 index 0000000..8a513c3 --- /dev/null +++ b/quark/tests/test_base.py @@ -0,0 +1,6 @@ +import unittest2 + + +class TestBase(unittest2.TestCase): + '''Class to decide which unit test class to inherit from uniformly.''' + pass diff --git a/quark/tests/test_db_models.py b/quark/tests/test_db_models.py new file mode 100644 index 0000000..61fb218 --- /dev/null +++ b/quark/tests/test_db_models.py @@ -0,0 +1,51 @@ +from collections import namedtuple +from sqlalchemy import create_engine + +from quantum.db import api as db_api + +from quark.db import models +import quark.plugin + +import test_base + + +class TestSubnets(test_base.TestBase): + def setUp(self): + db_api._ENGINE = create_engine('sqlite://') + db_api.register_models() + self.session = db_api.get_session() + self.plugin = quark.plugin.Plugin() + + def test_allocated_ips_only(self): + MockContext = namedtuple('MockContext', ['tenant_id', 'session']) + context = MockContext('0', self.session) + + # 1. Create network + network = {'network': {'name': 'test'}} + response = self.plugin.create_network(context, network) + network_id = response['id'] + + # 2. Create subnet + subnet = {'subnet': {'cidr': '192.168.10.1/24', + 'network_id': network_id}} + self.plugin.create_subnet(context, subnet) + + # 3. Create M.A.R. + mac_range = {'mac_address_range': {'cidr': '01:23:45/24'}} + self.plugin.create_mac_address_range(context, mac_range) + + # 4. Create port + port = {'port': {'network_id': network_id, + 'device_id': ''}} + response = self.plugin.create_port(context, port) + + q = self.session.query(models.Subnet).outerjoin(models.IPAddress) + self.assertEqual(len(q.first().allocated_ips), + 1) + + # 5. Delete port. + self.plugin.delete_port(context, response['id']) + + q = self.session.query(models.Subnet).outerjoin(models.IPAddress) + self.assertEqual(len(q.first().allocated_ips), + 0)