From c66212cf5d8397cb62a9e5f28b822a5546b768e5 Mon Sep 17 00:00:00 2001 From: Duarte Nunes Date: Thu, 3 Oct 2013 16:03:14 +0200 Subject: [PATCH] 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 --- neutron/plugins/midonet/midonet_lib.py | 103 ++++++++++++------ neutron/plugins/midonet/plugin.py | 63 ++++++----- neutron/tests/unit/midonet/mock_lib.py | 43 +++++--- .../tests/unit/midonet/test_midonet_lib.py | 22 ++++ 4 files changed, 153 insertions(+), 78 deletions(-) diff --git a/neutron/plugins/midonet/midonet_lib.py b/neutron/plugins/midonet/midonet_lib.py index 8f40b3986..74d2bae6a 100644 --- a/neutron/plugins/midonet/midonet_lib.py +++ b/neutron/plugins/midonet/midonet_lib.py @@ -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) diff --git a/neutron/plugins/midonet/plugin.py b/neutron/plugins/midonet/plugin.py index ca2cb9c18..d2f758b92 100644 --- a/neutron/plugins/midonet/plugin.py +++ b/neutron/plugins/midonet/plugin.py @@ -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: diff --git a/neutron/tests/unit/midonet/mock_lib.py b/neutron/tests/unit/midonet/mock_lib.py index dc1d5ed25..9fdae9cd6 100644 --- a/neutron/tests/unit/midonet/mock_lib.py +++ b/neutron/tests/unit/midonet/mock_lib.py @@ -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 diff --git a/neutron/tests/unit/midonet/test_midonet_lib.py b/neutron/tests/unit/midonet/test_midonet_lib.py index bc489e1a1..f9e42990a 100644 --- a/neutron/tests/unit/midonet/test_midonet_lib.py +++ b/neutron/tests/unit/midonet/test_midonet_lib.py @@ -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())