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: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, Midokura Japan KK # @author: Ryu Ishimoto, Midokura Japan KK
# @author: Rossella Sblendido, Midokura Japan KK # @author: Rossella Sblendido, Midokura Japan KK
# @author: Duarte Nunes, Midokura Japan KK
from midonetclient import exc from midonetclient import exc
from webob import exc as w_exc from webob import exc as w_exc
@ -55,19 +55,35 @@ class MidoClient:
def __init__(self, mido_api): def __init__(self, mido_api):
self.mido_api = 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 @handle_api_error
def create_bridge(self, tenant_id, name): def create_bridge(self, **kwargs):
"""Create a new bridge """Create a new bridge
:param tenant_id: id of tenant creating the bridge :param \**kwargs: configuration of the new bridge
:param name: name of the bridge
:returns: newly created bridge :returns: newly created bridge
""" """
LOG.debug(_("MidoClient.create_bridge called: " LOG.debug(_("MidoClient.create_bridge called: "
"tenant_id=%(tenant_id)s, name=%(name)s"), "kwargs=%(kwargs)s"), {'kwargs': kwargs})
{'tenant_id': tenant_id, 'name': name}) return self._create_dto(self.mido_api.add_bridge(), kwargs)
return self.mido_api.add_bridge().name(name).tenant_id(
tenant_id).create()
@handle_api_error @handle_api_error
def delete_bridge(self, id): def delete_bridge(self, id):
@ -92,17 +108,18 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Bridge', id=id) raise MidonetResourceNotFound(resource_type='Bridge', id=id)
@handle_api_error @handle_api_error
def update_bridge(self, id, name): def update_bridge(self, id, **kwargs):
"""Update a bridge of the given id with the new name """Update a bridge of the given id with the new fields
:param id: id of the bridge :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 :returns: bridge object
""" """
LOG.debug(_("MidoClient.update_bridge called: " 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: 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: except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Bridge', id=id) raise MidonetResourceNotFound(resource_type='Bridge', id=id)
@ -234,38 +251,53 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Port', id=id) raise MidonetResourceNotFound(resource_type='Port', id=id)
@handle_api_error @handle_api_error
def add_bridge_port(self, bridge): def add_bridge_port(self, bridge, **kwargs):
"""Add a port on a bridge """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 :returns: newly created port
""" """
LOG.debug(_("MidoClient.add_bridge_port called: " LOG.debug(_("MidoClient.add_bridge_port called: "
"bridge=%(bridge)s"), {'bridge': bridge}) "bridge=%(bridge)s, kwargs=%(kwargs)s"),
return self.mido_api.add_bridge_port(bridge) {'bridge': bridge, 'kwargs': kwargs})
return self._create_dto(self.mido_api.add_bridge_port(bridge), kwargs)
@handle_api_error @handle_api_error
def add_router_port(self, router, port_address=None, def update_port(self, id, **kwargs):
network_address=None, network_length=None): """Update a port of the given id with the new fields
"""Add a new port to an existing router."""
return self.mido_api.add_router_port(router, :param id: id of the port
port_address=port_address, :param \**kwargs: the fields to update and their values
network_address=network_address, """
network_length=network_length) 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 @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 """Create a new router
:param tenant_id: id of tenant creating the router :param \**kwargs: configuration of the new router
:param name: name of the router
:returns: newly created router :returns: newly created router
""" """
LOG.debug(_("MidoClient.create_router called: " LOG.debug(_("MidoClient.create_router called: "
"tenant_id=%(tenant_id)s, name=%(name)s"), "kwargs=%(kwargs)s"), {'kwargs': kwargs})
{'tenant_id': tenant_id, 'name': name}) return self._create_dto(self.mido_api.add_router(), kwargs)
return self.mido_api.add_router().name(name).tenant_id(
tenant_id).create()
@handle_api_error @handle_api_error
def delete_router(self, id): def delete_router(self, id):
@ -290,17 +322,18 @@ class MidoClient:
raise MidonetResourceNotFound(resource_type='Router', id=id) raise MidonetResourceNotFound(resource_type='Router', id=id)
@handle_api_error @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 """Update a router of the given id with the new name
:param id: id of the router :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 :returns: router object
""" """
LOG.debug(_("MidoClient.update_router called: " 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: 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: except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Router', id=id) raise MidonetResourceNotFound(resource_type='Router', id=id)

View File

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

View File

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