MidoNet: Added support for the admin_state_up flag

This commit enhances the port, bridge and router resources with the
admin_state_up flag in the MidoNet plugin.

Implements: blueprint midonet-admin-state
Change-Id: I67f4f9ad4099a05f8161aae79331ebba84f561b8
This commit is contained in:
Duarte Nunes 2013-10-03 16:03:14 +02:00
parent d91eeb4096
commit b16f0d3260
4 changed files with 153 additions and 78 deletions

View File

@ -19,7 +19,7 @@
# @author: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, Midokura Japan KK
# @author: Rossella Sblendido, Midokura Japan KK
# @author: Duarte Nunes, Midokura Japan KK
from midonetclient import exc
from webob import exc as w_exc
@ -55,19 +55,35 @@ class MidoClient:
def __init__(self, mido_api):
self.mido_api = mido_api
@classmethod
def _fill_dto(cls, dto, fields):
for field_name, field_value in fields.iteritems():
# We assume the setters are named the
# same way as the attributes themselves.
try:
getattr(dto, field_name)(field_value)
except AttributeError:
pass
return dto
@classmethod
def _create_dto(cls, dto, fields):
return cls._fill_dto(dto, fields).create()
@classmethod
def _update_dto(cls, dto, fields):
return cls._fill_dto(dto, fields).update()
@handle_api_error
def create_bridge(self, tenant_id, name):
def create_bridge(self, **kwargs):
"""Create a new bridge
:param tenant_id: id of tenant creating the bridge
:param name: name of the bridge
:param \**kwargs: configuration of the new bridge
:returns: newly created bridge
"""
LOG.debug(_("MidoClient.create_bridge called: "
"tenant_id=%(tenant_id)s, name=%(name)s"),
{'tenant_id': tenant_id, 'name': name})
return self.mido_api.add_bridge().name(name).tenant_id(
tenant_id).create()
"kwargs=%(kwargs)s"), {'kwargs': kwargs})
return self._create_dto(self.mido_api.add_bridge(), kwargs)
@handle_api_error
def delete_bridge(self, id):
@ -92,17 +108,18 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Bridge', id=id)
@handle_api_error
def update_bridge(self, id, name):
"""Update a bridge of the given id with the new name
def update_bridge(self, id, **kwargs):
"""Update a bridge of the given id with the new fields
:param id: id of the bridge
:param name: name of the bridge to set to
:param \**kwargs: the fields to update and their values
:returns: bridge object
"""
LOG.debug(_("MidoClient.update_bridge called: "
"id=%(id)s, name=%(name)s"), {'id': id, 'name': name})
"id=%(id)s, kwargs=%(kwargs)s"),
{'id': id, 'kwargs': kwargs})
try:
return self.mido_api.get_bridge(id).name(name).update()
return self._update_dto(self.mido_api.get_bridge(id), kwargs)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge', id=id)
@ -234,38 +251,53 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Port', id=id)
@handle_api_error
def add_bridge_port(self, bridge):
def add_bridge_port(self, bridge, **kwargs):
"""Add a port on a bridge
:param bridge: Bridge to add a new port to
:param bridge: bridge to add a new port to
:param \**kwargs: configuration of the new port
:returns: newly created port
"""
LOG.debug(_("MidoClient.add_bridge_port called: "
"bridge=%(bridge)s"), {'bridge': bridge})
return self.mido_api.add_bridge_port(bridge)
"bridge=%(bridge)s, kwargs=%(kwargs)s"),
{'bridge': bridge, 'kwargs': kwargs})
return self._create_dto(self.mido_api.add_bridge_port(bridge), kwargs)
@handle_api_error
def add_router_port(self, router, port_address=None,
network_address=None, network_length=None):
"""Add a new port to an existing router."""
return self.mido_api.add_router_port(router,
port_address=port_address,
network_address=network_address,
network_length=network_length)
def update_port(self, id, **kwargs):
"""Update a port of the given id with the new fields
:param id: id of the port
:param \**kwargs: the fields to update and their values
"""
LOG.debug(_("MidoClient.update_port called: "
"id=%(id)s, kwargs=%(kwargs)s"),
{'id': id, 'kwargs': kwargs})
try:
return self._update_dto(self.mido_api.get_port(id), kwargs)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Port', id=id)
@handle_api_error
def create_router(self, tenant_id, name):
def add_router_port(self, router, **kwargs):
"""Add a new port to an existing router.
:param router: router to add a new port to
:param \**kwargs: configuration of the new port
:returns: newly created port
"""
return self._create_dto(self.mido_api.add_router_port(router), kwargs)
@handle_api_error
def create_router(self, **kwargs):
"""Create a new router
:param tenant_id: id of tenant creating the router
:param name: name of the router
:param \**kwargs: configuration of the new router
:returns: newly created router
"""
LOG.debug(_("MidoClient.create_router called: "
"tenant_id=%(tenant_id)s, name=%(name)s"),
{'tenant_id': tenant_id, 'name': name})
return self.mido_api.add_router().name(name).tenant_id(
tenant_id).create()
"kwargs=%(kwargs)s"), {'kwargs': kwargs})
return self._create_dto(self.mido_api.add_router(), kwargs)
@handle_api_error
def delete_router(self, id):
@ -290,17 +322,18 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Router', id=id)
@handle_api_error
def update_router(self, id, name):
def update_router(self, id, **kwargs):
"""Update a router of the given id with the new name
:param id: id of the router
:param name: name of the router to set to
:param \**kwargs: the fields to update and their values
:returns: router object
"""
LOG.debug(_("MidoClient.update_router called: "
"id=%(id)s, name=%(name)s"), {'id': id, 'name': name})
"id=%(id)s, kwargs=%(kwargs)s"),
{'id': id, 'kwargs': kwargs})
try:
return self.mido_api.get_router(id).name(name).update()
return self._update_dto(self.mido_api.get_router(id), kwargs)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Router', id=id)

View File

@ -20,6 +20,7 @@
# @author: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, Midokura Japan KK
# @author: Rossella Sblendido, Midokura Japan KK
# @author: Duarte Nunes, Midokura Japan KK
from midonetclient import api
from oslo.config import cfg
@ -458,17 +459,18 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
"""
LOG.debug(_('MidonetPluginV2.create_network called: network=%r'),
network)
tenant_id = self._get_tenant_id_for_create(context, network['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(tenant_id,
network['network']['name'])
network['network']['id'] = bridge.get_id()
bridge = self.client.create_bridge(**net_data)
net_data['id'] = bridge.get_id()
session = context.session
with session.begin(subtransactions=True):
net = super(MidonetPluginV2, self).create_network(context, network)
self._process_l3_create(context, net, network['network'])
self._process_l3_create(context, net, net_data)
LOG.debug(_("MidonetPluginV2.create_network exiting: net=%r"), net)
return net
@ -486,7 +488,7 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
net = super(MidonetPluginV2, self).update_network(
context, id, network)
self._process_l3_update(context, net, network['network'])
self.client.update_bridge(id, net['name'])
self.client.update_bridge(id, **network['network'])
LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net)
return net
@ -524,8 +526,11 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# port ID in Neutron.
bridge = self.client.get_bridge(port_data["network_id"])
tenant_id = bridge.get_tenant_id()
bridge_port = self.client.add_bridge_port(bridge)
asu = port_data.get("admin_state_up", True)
bridge_port = self.client.add_bridge_port(bridge,
admin_state_up=asu)
port_data["id"] = bridge_port.get_id()
try:
session = context.session
with session.begin(subtransactions=True):
@ -661,6 +666,17 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# update the port DB
p = super(MidonetPluginV2, self).update_port(context, id, port)
if "admin_state_up" in port["port"]:
asu = port["port"]["admin_state_up"]
mido_port = self.client.update_port(id, admin_state_up=asu)
# If we're changing the admin_state_up flag and the port is
# associated with a router, then we also need to update the
# peer port.
if _is_router_interface_port(p):
self.client.update_port(mido_port.get_peer_id(),
admin_state_up=asu)
new_ips = p["fixed_ips"]
if new_ips:
bridge = self.client.get_bridge(net_id)
@ -698,7 +714,7 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
When a new Neutron router is created, its corresponding MidoNet router
is also created. In MidoNet, this router is initialized with chains
for inbuond and outbound traffic, which will be used to hold other
for inbound and outbound traffic, which will be used to hold other
chains that include various rules, such as NAT.
:param router: Router information provided to create a new router.
@ -710,19 +726,17 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# 3rd parties to specify IDs as we do with l2 plugin
LOG.debug(_("MidonetPluginV2.create_router called: router=%(router)s"),
{"router": router})
tenant_id = self._get_tenant_id_for_create(context, router['router'])
mido_router = self.client.create_router(tenant_id,
router['router']['name'])
r = router['router']
tenant_id = self._get_tenant_id_for_create(context, r)
r['tenant_id'] = tenant_id
mido_router = self.client.create_router(**r)
mido_router_id = mido_router.get_id()
try:
r = router['router']
has_gw_info = False
if EXTERNAL_GW_INFO in r:
has_gw_info = True
gw_info = r[EXTERNAL_GW_INFO]
del r[EXTERNAL_GW_INFO]
tenant_id = self._get_tenant_id_for_create(context, r)
gw_info = r.pop(EXTERNAL_GW_INFO)
with context.session.begin(subtransactions=True):
# pre-generate id so it will be available when
# configuring external gw port
@ -868,10 +882,7 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
chain_names['post-routing'],
gw_ip, gw_port["id"], **props)
# Update the name if changed
changed_name = router_data.get('name')
if changed_name:
self.client.update_router(id, changed_name)
self.client.update_router(id, **router_data)
LOG.debug(_("MidonetPluginV2.update_router exiting: router=%r"), r)
return r
@ -945,12 +956,12 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
for port in bridge_ports_to_delete:
self.client.delete_port(port.get_id())
def _link_bridge_to_router(self, router, bridge_port_id, net_addr, net_len,
def _link_bridge_to_router(self, router, bridge_port, net_addr, net_len,
gw_ip, metadata_gw_ip):
router_port = self.client.add_router_port(
router, port_address=gw_ip, network_address=net_addr,
network_length=net_len)
self.client.link(router_port, bridge_port_id)
router, network_length=net_len, network_address=net_addr,
port_address=gw_ip, admin_state_up=bridge_port['admin_state_up'])
self.client.link(router_port, bridge_port['id'])
self.client.add_router_route(router, type='Normal',
src_network_addr='0.0.0.0',
src_network_length=0,
@ -999,7 +1010,7 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
net_addr, net_len = net_util.net_addr(cidr)
router = self.client.get_router(router_id)
# Get the metadatat GW IP
# Get the metadata GW IP
metadata_gw_ip = None
rport_qry = context.session.query(models_v2.Port)
dhcp_ports = rport_qry.filter_by(
@ -1011,7 +1022,9 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
LOG.warn(_("DHCP agent is not working correctly. No port "
"to reach the Metadata server on this network"))
# Link the router and the bridge
self._link_bridge_to_router(router, info["port_id"], net_addr,
port = super(MidonetPluginV2, self).get_port(context,
info["port_id"])
self._link_bridge_to_router(router, port, net_addr,
net_len, subnet["gateway_ip"],
metadata_gw_ip)
except Exception:

View File

@ -21,21 +21,21 @@ import mock
import uuid
def get_bridge_mock(id=None, tenant_id='test-tenant', name='net'):
def get_bridge_mock(id=None, **kwargs):
if id is None:
id = str(uuid.uuid4())
bridge = mock.Mock()
bridge.get_id.return_value = id
bridge.get_tenant_id.return_value = tenant_id
bridge.get_name.return_value = name
bridge.get_tenant_id.return_value = kwargs.get("tenant_id", "test-tenant")
bridge.get_name.return_value = kwargs.get("name", "net")
bridge.get_ports.return_value = []
bridge.get_peer_ports.return_value = []
bridge.get_admin_state_up.return_value = kwargs.get("admin_state_up", True)
return bridge
def get_bridge_port_mock(id=None, bridge_id=None,
type='ExteriorBridge'):
def get_bridge_port_mock(id=None, bridge_id=None, **kwargs):
if id is None:
id = str(uuid.uuid4())
if bridge_id is None:
@ -43,8 +43,10 @@ def get_bridge_port_mock(id=None, bridge_id=None,
port = mock.Mock()
port.get_id.return_value = id
port.get_brige_id.return_value = bridge_id
port.get_type.return_value = type
port.get_bridge_id.return_value = bridge_id
port.get_admin_state_up.return_value = kwargs.get("admin_state_up", True)
port.get_type.return_value = "Bridge"
port.create.return_value = port
return port
@ -75,17 +77,18 @@ def get_port_group_mock(id=None, tenant_id='test-tenant', name='pg'):
return port_group
def get_router_mock(id=None, tenant_id='test-tenant', name='router'):
def get_router_mock(id=None, **kwargs):
if id is None:
id = str(uuid.uuid4())
router = mock.Mock()
router.get_id.return_value = id
router.get_tenant_id.return_value = tenant_id
router.get_name.return_value = name
router.get_tenant_id.return_value = kwargs.get("tenant_id", "test-tenant")
router.get_name.return_value = kwargs.get("name", "router")
router.get_ports.return_value = []
router.get_peer_ports.return_value = []
router.get_routes.return_value = []
router.get_admin_state_up.return_value = kwargs.get("admin_state_up", True)
return router
@ -125,19 +128,19 @@ class MidonetLibMockConfig():
def __init__(self, inst):
self.inst = inst
def _create_bridge(self, tenant_id, name):
return get_bridge_mock(tenant_id=tenant_id, name=name)
def _create_bridge(self, **kwargs):
return get_bridge_mock(**kwargs)
def _create_router(self, tenant_id, name):
return get_router_mock(tenant_id=tenant_id, name=name)
def _create_router(self, **kwargs):
return get_router_mock(**kwargs)
def _create_subnet(self, bridge, gateway_ip, subnet_prefix, subnet_len):
return get_subnet_mock(bridge.get_id(), gateway_ip=gateway_ip,
subnet_prefix=subnet_prefix,
subnet_len=subnet_len)
def _add_bridge_port(self, bridge):
return get_bridge_port_mock(bridge_id=bridge.get_id())
def _add_bridge_port(self, bridge, **kwargs):
return get_bridge_port_mock(bridge_id=bridge.get_id(), **kwargs)
def _get_bridge(self, id):
return get_bridge_mock(id=id)
@ -148,8 +151,8 @@ class MidonetLibMockConfig():
def _get_router(self, id):
return get_router_mock(id=id)
def _update_bridge(self, id, name):
return get_bridge_mock(id=id, name=name)
def _update_bridge(self, id, **kwargs):
return get_bridge_mock(id=id, **kwargs)
def setup(self):
# Bridge methods side effects
@ -250,9 +253,13 @@ class MidoClientMockConfig():
def _get_router(self, id):
return get_router_mock(id=id)
def _add_bridge_port(self, bridge):
return get_bridge_port_mock(bridge_id=bridge.get_id())
def setup(self):
self.inst.get_bridge.side_effect = self._get_bridge
self.inst.get_chains.side_effect = self._get_chains
self.inst.get_chain.side_effect = self._get_chain
self.inst.get_port_groups.side_effect = self._get_port_groups
self.inst.get_router.side_effect = self._get_router
self.inst.add_bridge_port.side_effect = self._add_bridge_port

View File

@ -166,3 +166,25 @@ class MidoClientTestCase(testtools.TestCase):
self.assertIsNotNone(bridge)
self.assertEqual(bridge.get_id(), bridge_id)
self.assertTrue(bridge.get_admin_state_up())
def test_add_bridge_port(self):
bridge_id = uuidutils.generate_uuid()
bridge = self.client.get_bridge(bridge_id)
self.assertIsNotNone(bridge)
port = self.client.add_bridge_port(bridge)
self.assertEqual(bridge.get_id(), port.get_bridge_id())
self.assertTrue(port.get_admin_state_up())
def test_get_router(self):
router_id = uuidutils.generate_uuid()
router = self.client.get_router(router_id)
self.assertIsNotNone(router)
self.assertEqual(router.get_id(), router_id)
self.assertTrue(router.get_admin_state_up())