Implemented quark notifications (w/ unit tests)

Notifications published on subnet creation/deletion and IP address
creation and deallocation.
This commit is contained in:
Amir Sadoughi
2013-08-08 23:39:48 -05:00
parent d887a2a93a
commit ad5fa60e45
5 changed files with 176 additions and 5 deletions

View File

@@ -21,13 +21,14 @@ import netaddr
from neutron.common import exceptions from neutron.common import exceptions
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.openstack.common.notifier import api as notifier_api
from neutron.openstack.common import timeutils from neutron.openstack.common import timeutils
from quark.db import api as db_api from quark.db import api as db_api
from quark.db import models from quark.db import models
LOG = logging.getLogger("neutron") LOG = logging.getLogger("neutron.quark.ipam")
class QuarkIpam(object): class QuarkIpam(object):
@@ -137,13 +138,39 @@ class QuarkIpam(object):
version=subnet["ip_version"], network_id=net_id) version=subnet["ip_version"], network_id=net_id)
address["deallocated"] = 0 address["deallocated"] = 0
payload = dict(tenant_id=address["tenant_id"],
ip_block_id=address["subnet_id"],
ip_address=address["address_readable"],
device_ids=[p["device_id"] for p in address["ports"]],
created_at=address["created_at"])
notifier_api.notify(context,
notifier_api.publisher_id("network"),
"ip_block.address.create",
notifier_api.CONF.default_notification_level,
payload)
return address return address
def _deallocate_ip_address(self, context, address):
address["deallocated"] = 1
payload = dict(tenant_id=address["tenant_id"],
ip_block_id=address["subnet_id"],
ip_address=address["address_readable"],
device_ids=[p["device_id"] for p in address["ports"]],
created_at=address["created_at"],
deleted_at=timeutils.utcnow())
notifier_api.notify(context,
notifier_api.publisher_id("network"),
"ip_block.address.delete",
notifier_api.CONF.default_notification_level,
payload)
def deallocate_ip_address(self, context, port, **kwargs): def deallocate_ip_address(self, context, port, **kwargs):
for addr in port["ip_addresses"]: for addr in port["ip_addresses"]:
# Note: only deallocate ip if this is the only port mapped to it # Note: only deallocate ip if this is the only port mapped to it
if len(addr["ports"]) == 1: if len(addr["ports"]) == 1:
addr["deallocated"] = 1 self._deallocate_ip_address(context, addr)
port["ip_addresses"] = [] port["ip_addresses"] = []
def deallocate_mac_address(self, context, address): def deallocate_mac_address(self, context, address):

View File

@@ -86,7 +86,6 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
neutron_session._MAKER = scoped_session(session_maker) neutron_session._MAKER = scoped_session(session_maker)
def __init__(self): def __init__(self):
neutron_db_api.configure_db() neutron_db_api.configure_db()
self._initDBMaker() self._initDBMaker()
neutron_db_api.register_models(base=models.BASEV2) neutron_db_api.register_models(base=models.BASEV2)

View File

@@ -19,6 +19,9 @@ from neutron.common import config as neutron_cfg
from neutron.common import exceptions from neutron.common import exceptions
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.openstack.common.notifier import api as notifier_api
from neutron.openstack.common import timeutils
from oslo.config import cfg from oslo.config import cfg
from quark.db import api as db_api from quark.db import api as db_api
@@ -133,6 +136,15 @@ def create_subnet(context, subnet):
subnet_dict = v._make_subnet_dict(new_subnet, subnet_dict = v._make_subnet_dict(new_subnet,
default_route=routes.DEFAULT_ROUTE) default_route=routes.DEFAULT_ROUTE)
subnet_dict["gateway_ip"] = gateway_ip subnet_dict["gateway_ip"] = gateway_ip
notifier_api.notify(context,
notifier_api.publisher_id("network"),
"ip_block.create",
notifier_api.CONF.default_notification_level,
dict(tenant_id=subnet_dict["tenant_id"],
ip_block_id=subnet_dict["id"],
created_at=new_subnet["created_at"]))
return subnet_dict return subnet_dict
@@ -283,8 +295,20 @@ def delete_subnet(context, id):
subnet = db_api.subnet_find(context, id=id, scope=db_api.ONE) subnet = db_api.subnet_find(context, id=id, scope=db_api.ONE)
if not subnet: if not subnet:
raise exceptions.SubnetNotFound(subnet_id=id) raise exceptions.SubnetNotFound(subnet_id=id)
payload = dict(tenant_id=subnet["tenant_id"],
ip_block_id=subnet["id"],
created_at=subnet["created_at"],
deleted_at=timeutils.utcnow())
_delete_subnet(context, subnet) _delete_subnet(context, subnet)
notifier_api.notify(context,
notifier_api.publisher_id("network"),
"ip_block.delete",
notifier_api.CONF.default_notification_level,
payload)
def diagnose_subnet(context, id, fields): def diagnose_subnet(context, id, fields):
if id == "*": if id == "*":

View File

@@ -20,6 +20,7 @@ import uuid
import mock import mock
from neutron.api.v2 import attributes as neutron_attrs from neutron.api.v2 import attributes as neutron_attrs
from neutron.common import exceptions from neutron.common import exceptions
from neutron.openstack.common.notifier import api as notifier_api
from oslo.config import cfg from oslo.config import cfg
from quark.db import models from quark.db import models
@@ -694,3 +695,53 @@ class TestQuarkDeleteSubnet(test_quark_plugin.TestQuarkPlugin):
with self._stubs(subnet=subnet, ips=[{}]): with self._stubs(subnet=subnet, ips=[{}]):
with self.assertRaises(exceptions.SubnetInUse): with self.assertRaises(exceptions.SubnetInUse):
self.plugin.delete_subnet(self.context, 1) self.plugin.delete_subnet(self.context, 1)
class TestSubnetsNotification(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
def _stubs(self, s, deleted_at=None):
s["network"] = models.Network()
subnet = models.Subnet(**s)
db_mod = "quark.db.api"
api_mod = "neutron.openstack.common.notifier.api"
time_mod = "neutron.openstack.common.timeutils"
with contextlib.nested(
mock.patch("%s.subnet_find" % db_mod),
mock.patch("%s.network_find" % db_mod),
mock.patch("%s.subnet_create" % db_mod),
mock.patch("%s.subnet_delete" % db_mod),
mock.patch("%s.notify" % api_mod),
mock.patch("%s.utcnow" % time_mod)
) as (sub_find, net_find, sub_create, sub_del, notify, time):
sub_create.return_value = subnet
sub_find.return_value = subnet
time.return_value = deleted_at
yield notify
def test_create_subnet_notification(self):
s = dict(network_id=1, cidr="192.168.10.0/24",
tenant_id=1, id=1, created_at="123")
with self._stubs(s) as notify:
self.plugin.create_subnet(self.context, dict(subnet=s))
notify.assert_called_once_with(
self.context,
notifier_api.publisher_id("network"),
"ip_block.create",
notifier_api.CONF.default_notification_level,
dict(tenant_id=s["tenant_id"],
ip_block_id=s["id"],
created_at=s["created_at"]))
def test_delete_subnet_notification(self):
s = dict(tenant_id=1, id=1, created_at="123")
with self._stubs(s, deleted_at="456") as notify:
self.plugin.delete_subnet(self.context, 1)
notify.assert_called_once_with(
self.context,
notifier_api.publisher_id("network"),
"ip_block.delete",
notifier_api.CONF.default_notification_level,
dict(tenant_id=s["tenant_id"],
created_at=s["created_at"],
ip_block_id=s["id"],
deleted_at="456"))

View File

@@ -18,9 +18,11 @@ import mock
from neutron.common import exceptions from neutron.common import exceptions
from neutron.db import api as neutron_db_api from neutron.db import api as neutron_db_api
from neutron.openstack.common.db.sqlalchemy import session as neutron_session from neutron.openstack.common.db.sqlalchemy import session as neutron_session
from neutron.openstack.common.notifier import api as notifier_api
from oslo.config import cfg from oslo.config import cfg
from quark.db import models from quark.db import models
import quark.ipam import quark.ipam
from quark.tests import test_base from quark.tests import test_base
@@ -173,8 +175,9 @@ class QuarkMacAddressDeallocation(QuarkIpamBaseTest):
class QuarkIPAddressDeallocation(QuarkIpamBaseTest): class QuarkIPAddressDeallocation(QuarkIpamBaseTest):
def test_deallocate_ip_address(self): def test_deallocate_ip_address(self):
port = dict(ip_addresses=[]) port = dict(ip_addresses=[], device_id="foo")
addr = dict(ports=[port]) addr = dict(ports=[port], tenant_id=1, subnet_id=1,
address_readable=None, created_at=None)
port["ip_addresses"].append(addr) port["ip_addresses"].append(addr)
self.ipam.deallocate_ip_address(self.context, port) self.ipam.deallocate_ip_address(self.context, port)
# ORM takes care of other model if one model is modified # ORM takes care of other model if one model is modified
@@ -468,3 +471,70 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
address = self.ipam.allocate_ip_address( address = self.ipam.allocate_ip_address(
self.context, 0, 0, 0, ip_address="0.0.0.240") self.context, 0, 0, 0, ip_address="0.0.0.240")
self.assertEqual(address["address"], 240) self.assertEqual(address["address"], 240)
class QuarkIPAddressAllocationNotifications(QuarkIpamBaseTest):
@contextlib.contextmanager
def _stubs(self, address, addresses=None, subnets=None, deleted_at=None):
address = models.IPAddress(**address)
if not addresses:
addresses = [None]
db_mod = "quark.db.api"
api_mod = "neutron.openstack.common.notifier.api"
time_mod = "neutron.openstack.common.timeutils"
with contextlib.nested(
mock.patch("%s.ip_address_find" % db_mod),
mock.patch("%s.ip_address_create" % db_mod),
mock.patch("%s.subnet_find_allocation_counts" % db_mod),
mock.patch("%s.notify" % api_mod),
mock.patch("%s.utcnow" % time_mod),
) as (addr_find, addr_create, subnet_find, notify, time):
addr_find.side_effect = addresses
addr_create.return_value = address
subnet_find.return_value = subnets
time.return_value = deleted_at
yield notify
def test_allocation_notification(self):
subnet = dict(id=1, first_ip=0, last_ip=255,
cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0, network=dict(ip_policy=None),
ip_policy=None)
address = dict(tenant_id=1, address=0, created_at="123",
subnet_id=1, address_readable="0.0.0.0")
with self._stubs(
address,
subnets=[(subnet, 0)],
addresses=[None, None]
) as notify:
self.ipam.allocate_ip_address(self.context, 0, 0, 0,
version=4)
notify.assert_called_once_with(
self.context,
notifier_api.publisher_id("network"),
"ip_block.address.create",
notifier_api.CONF.default_notification_level,
dict(tenant_id=address["tenant_id"],
ip_block_id=address["subnet_id"],
ip_address="0.0.0.0",
device_ids=[],
created_at=address["created_at"]))
def test_deallocation_notification(self):
address = dict(tenant_id=1, address=0, created_at="123",
subnet_id=1, address_readable="0.0.0.0",
ports=[dict(device_id="foo")])
port = dict(ip_addresses=[address])
with self._stubs(dict(), deleted_at="456") as notify:
self.ipam.deallocate_ip_address(self.context, port)
notify.assert_called_once_with(
self.context,
notifier_api.publisher_id("network"),
"ip_block.address.delete",
notifier_api.CONF.default_notification_level,
dict(tenant_id=address["tenant_id"],
ip_block_id=address["subnet_id"],
ip_address="0.0.0.0",
device_ids=["foo"],
created_at=address["created_at"],
deleted_at="456"))