Implement Midonet Juno Network Api calls

This patch is the first in a series of converting the current midonet plugin
code to the new model described in blueprint midonet-plugin-juno. This patch
focuses specifically on the "network" handlers.

This patch also updates the midonet UT to handle the new objects that need
to be mocked with the new model.

Change-Id: I056d698ce2e312527e756ff56c962cff83a6bc04
Partially-Implements: blueprint midonet-plugin-juno
This commit is contained in:
Joe Mills 2014-07-01 09:07:05 +00:00
parent 30556c4a23
commit 32af49e0d3
2 changed files with 88 additions and 41 deletions

View File

@ -20,9 +20,14 @@
# @author: Rossella Sblendido, Midokura Japan KK
# @author: Duarte Nunes, Midokura Japan KK
import functools
from midonetclient import api
from midonetclient import exc
from midonetclient.neutron import client as n_client
from oslo.config import cfg
from sqlalchemy.orm import exc as sa_exc
from webob import exc as w_exc
from neutron.api.v2 import attributes
from neutron.common import constants
@ -66,6 +71,21 @@ SG_PORT_GROUP_NAME = "OS_PG_%s"
SNAT_RULE = 'SNAT'
def handle_api_error(fn):
"""Wrapper for methods that throws custom exceptions."""
@functools.wraps(fn)
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except (w_exc.HTTPException, exc.MidoApiConnectionError) as ex:
raise MidonetApiException(msg=ex)
return wrapped
class MidonetApiException(n_exc.NeutronException):
message = _("MidoNet API error: %(msg)s")
def _get_nat_ips(type, fip):
"""Get NAT IP address information.
@ -206,6 +226,10 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
self.provider_router_id = midonet_conf.provider_router_id
self.provider_router = None
self.api_cli = n_client.MidonetClient(midonet_conf.midonet_uri,
midonet_conf.username,
midonet_conf.password,
project_id=midonet_conf.project_id)
self.mido_api = api.MidonetApi(midonet_uri, admin_user,
admin_pass,
project_id=admin_project_id)
@ -445,72 +469,58 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
LOG.debug(_("MidonetPluginV2.delete_subnet exiting"))
@handle_api_error
def create_network(self, context, network):
"""Create Neutron network.
Create a new Neutron network and its corresponding MidoNet bridge.
"""
LOG.debug(_('MidonetPluginV2.create_network called: network=%r'),
LOG.debug('MidonetPluginV2.create_network called: network=%r',
network)
net_data = network['network']
tenant_id = self._get_tenant_id_for_create(context, net_data)
net_data['tenant_id'] = tenant_id
self._ensure_default_security_group(context, tenant_id)
bridge = self.client.create_bridge(**net_data)
net_data['id'] = bridge.get_id()
session = context.session
with session.begin(subtransactions=True):
with context.session.begin(subtransactions=True):
net = super(MidonetPluginV2, self).create_network(context, network)
self._process_l3_create(context, net, net_data)
self.api_cli.create_network(net)
LOG.debug(_("MidonetPluginV2.create_network exiting: net=%r"), net)
LOG.debug("MidonetPluginV2.create_network exiting: net=%r", net)
return net
@handle_api_error
def update_network(self, context, id, network):
"""Update Neutron network.
Update an existing Neutron network and its corresponding MidoNet
bridge.
"""
LOG.debug(_("MidonetPluginV2.update_network called: id=%(id)r, "
"network=%(network)r"), {'id': id, 'network': network})
session = context.session
with session.begin(subtransactions=True):
LOG.debug("MidonetPluginV2.update_network called: id=%(id)r, "
"network=%(network)r", {'id': id, 'network': network})
with context.session.begin(subtransactions=True):
net = super(MidonetPluginV2, self).update_network(
context, id, network)
self._process_l3_update(context, net, network['network'])
self.client.update_bridge(id, **network['network'])
self.api_cli.update_network(id, net)
LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net)
LOG.debug("MidonetPluginV2.update_network exiting: net=%r", net)
return net
def get_network(self, context, id, fields=None):
"""Get Neutron network.
Retrieves a Neutron network and its corresponding MidoNet bridge.
"""
LOG.debug(_("MidonetPluginV2.get_network called: id=%(id)r, "
"fields=%(fields)r"), {'id': id, 'fields': fields})
qnet = super(MidonetPluginV2, self).get_network(context, id, fields)
self.client.get_bridge(id)
LOG.debug(_("MidonetPluginV2.get_network exiting: qnet=%r"), qnet)
return qnet
@handle_api_error
def delete_network(self, context, id):
"""Delete a network and its corresponding MidoNet bridge."""
LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id)
self.client.delete_bridge(id)
try:
LOG.debug("MidonetPluginV2.delete_network called: id=%r", id)
with context.session.begin(subtransactions=True):
self._process_l3_delete(context, id)
super(MidonetPluginV2, self).delete_network(context, id)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_('Failed to delete neutron db, while Midonet '
'bridge=%r had been deleted'), id)
self.api_cli.delete_network(id)
LOG.debug("MidonetPluginV2.delete_network exiting: id=%r", id)
def create_port(self, context, port):
"""Create a L2 port in Neutron/MidoNet."""

View File

@ -53,12 +53,27 @@ class MidonetPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
self.instance = self.mock_api.start()
mock_cfg = mock_lib.MidonetLibMockConfig(self.instance.return_value)
mock_cfg.setup()
super(MidonetPluginV2TestCase, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
def tearDown(self):
super(MidonetPluginV2TestCase, self).tearDown()
self.mock_api.stop()
self.midoclient_mock = mock.MagicMock()
self.midoclient_mock.midonetclient.neutron.client.return_value = True
modules = {
'midonetclient': self.midoclient_mock,
'midonetclient.neutron': self.midoclient_mock.neutron,
'midonetclient.neutron.client': self.midoclient_mock.client,
}
self.module_patcher = mock.patch.dict('sys.modules', modules)
self.module_patcher.start()
self.addCleanup(self.module_patcher.stop)
# import midonetclient here because it needs proper mock objects to be
# assigned to this module first. 'midoclient_mock' object is the
# mock object used for this module.
from midonetclient.neutron.client import MidonetClient
client_class = MidonetClient
self.mock_class = client_class()
super(MidonetPluginV2TestCase, self).setUp(plugin=plugin)
class TestMidonetNetworksV2(test_plugin.TestNetworksV2,
@ -131,6 +146,9 @@ class TestMidonetL3NatTestCase(MidonetPluginV2TestCase,
None)
self.assertTrue(self.instance.return_value.add_static_nat.called)
def test_delete_ext_net_with_disassociated_floating_ips(self):
pass
class TestMidonetSecurityGroupsTestCase(sg.SecurityGroupDBTestCase):
@ -150,6 +168,25 @@ class TestMidonetSecurityGroupsTestCase(sg.SecurityGroupDBTestCase):
p.start()
# dict patches must be explicitly stopped
self.addCleanup(p.stop)
self.midoclient_mock = mock.MagicMock()
self.midoclient_mock.midonetclient.neutron.client.return_value = True
modules = {
'midonetclient': self.midoclient_mock,
'midonetclient.neutron': self.midoclient_mock.neutron,
'midonetclient.neutron.client': self.midoclient_mock.client,
}
self.module_patcher = mock.patch.dict('sys.modules', modules)
self.module_patcher.start()
self.addCleanup(self.module_patcher.stop)
# import midonetclient here because it needs proper mock objects to be
# assigned to this module first. 'midoclient_mock' object is the
# mock object used for this module.
from midonetclient.neutron.client import MidonetClient
client_class = MidonetClient
self.mock_class = client_class()
super(TestMidonetSecurityGroupsTestCase, self).setUp(self._plugin_name)