]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
DVR: notify specific agent when creating floating ip
authorOleg Bondarev <obondarev@mirantis.com>
Mon, 5 Oct 2015 14:34:45 +0000 (17:34 +0300)
committerOleg Bondarev <obondarev@mirantis.com>
Mon, 12 Oct 2015 12:19:40 +0000 (12:19 +0000)
Currently when floating ip is created, a lot of useless action
is happening: floating ip router is scheduled, all l3 agents where
router is scheduled are notified about router update, all agents
request full router info from server. All this becomes a big
performance problem at scale with lots of compute nodes.

In fact on (associated) Floating IP creation we really need
to notify specific l3 agent on compute node where associated
VM port is located and do not need to schedule router and
bother other agents where rourter is scheduled. This should
significally decrease unneeded load on neutron server at scale.

Partial-Bug: #1486828
Change-Id: I0cbe8c51c3714e6cbdc48ca37135b783f8014905

neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py
neutron/db/l3_db.py
neutron/db/l3_dvr_db.py
neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py

index fb5b3d0467c76c5ab44de79eb3f69993da147c18..2bc6c2ccc79fc025e5a95f4e4928568016219d6a 100644 (file)
@@ -38,13 +38,13 @@ class L3AgentNotifyAPI(object):
         target = oslo_messaging.Target(topic=topic, version='1.0')
         self.client = n_rpc.get_client(target)
 
-    def _notification_host(self, context, method, payload, host):
+    def _notification_host(self, context, method, host, **kwargs):
         """Notify the agent that is hosting the router."""
         LOG.debug('Notify agent at %(host)s the message '
                   '%(method)s', {'host': host,
                                  'method': method})
         cctxt = self.client.prepare(server=host)
-        cctxt.cast(context, method, payload=payload)
+        cctxt.cast(context, method, **kwargs)
 
     def _agent_notification(self, context, method, router_ids, operation,
                             shuffle_agents):
@@ -131,9 +131,8 @@ class L3AgentNotifyAPI(object):
         cctxt.cast(context, method, router_id=router_id)
 
     def agent_updated(self, context, admin_state_up, host):
-        self._notification_host(context, 'agent_updated',
-                                {'admin_state_up': admin_state_up},
-                                host)
+        self._notification_host(context, 'agent_updated', host,
+                                payload={'admin_state_up': admin_state_up})
 
     def router_deleted(self, context, router_id):
         self._notification_fanout(context, 'router_deleted', router_id)
@@ -153,9 +152,13 @@ class L3AgentNotifyAPI(object):
                                      operation, arp_table)
 
     def router_removed_from_agent(self, context, router_id, host):
-        self._notification_host(context, 'router_removed_from_agent',
-                                {'router_id': router_id}, host)
+        self._notification_host(context, 'router_removed_from_agent', host,
+                                payload={'router_id': router_id})
 
     def router_added_to_agent(self, context, router_ids, host):
-        self._notification_host(context, 'router_added_to_agent',
-                                router_ids, host)
+        self._notification_host(context, 'router_added_to_agent', host,
+                                payload=router_ids)
+
+    def routers_updated_on_host(self, context, router_ids, host):
+        self._notification_host(context, 'routers_updated', host,
+                                routers=router_ids)
index 92df5abd959711df96ffb7f32d1a5ff889796de2..ca27cfbaf94f6932f488cc8c79d2d3d8b73f92c9 100644 (file)
@@ -938,7 +938,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
         net = self._core_plugin._get_network(context, net_id)
         return any(s.ip_version == 4 for s in net.subnets)
 
-    def create_floatingip(self, context, floatingip,
+    def _create_floatingip(self, context, floatingip,
             initial_status=l3_constants.FLOATINGIP_STATUS_ACTIVE):
         fip = floatingip['floatingip']
         tenant_id = self._get_tenant_id_for_create(context, fip)
@@ -1002,6 +1002,10 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
 
         return self._make_floatingip_dict(floatingip_db)
 
+    def create_floatingip(self, context, floatingip,
+            initial_status=l3_constants.FLOATINGIP_STATUS_ACTIVE):
+        return self._create_floatingip(context, floatingip, initial_status)
+
     def _update_floatingip(self, context, id, floatingip):
         fip = floatingip['floatingip']
         with context.session.begin(subtransactions=True):
index 4bafc56b225f6d4490348ab998dc90935d361275..ce6263e2e4bfafe134d75dd917417ff3cb3c71b3 100644 (file)
@@ -681,6 +681,27 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
                                                   p['id'],
                                                   l3_port_check=False)
 
+    def create_floatingip(self, context, floatingip,
+                          initial_status=l3_const.FLOATINGIP_STATUS_ACTIVE):
+        floating_ip = self._create_floatingip(
+            context, floatingip, initial_status)
+        router_id = floating_ip['router_id']
+        if not router_id:
+            return floating_ip
+
+        router = self._get_router(context, router_id)
+        # we need to notify agents only in case Floating IP is associated
+        fixed_port_id = floating_ip['port_id']
+        if fixed_port_id:
+            if is_distributed_router(router):
+                host = self._get_vm_port_hostid(context, fixed_port_id)
+                self.l3_rpc_notifier.routers_updated_on_host(
+                    context, [router_id], host)
+            else:
+                self.notify_router_updated(context, router_id,
+                                           'create_floatingip')
+        return floating_ip
+
 
 def is_distributed_router(router):
     """Return True if router to be handled is distributed."""
index 6dbcb59ccef8a26254ec85a9fd932425831d6026..75d5ce25a4cdd4f35a1e65bca2f983a496e3f163 100644 (file)
@@ -12,6 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import mock
+
 from neutron.api.v2 import attributes
 from neutron.common import constants as l3_const
 from neutron.extensions import external_net
@@ -220,3 +222,47 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework):
         self.assertIsNone(
             self.l3_plugin._get_agent_gw_ports_exist_for_network(
                 self.context, ext_net_id, "", self.l3_agent['id']))
+
+    def _test_create_floating_ip_agent_notification(self, dvr=True):
+        with self.subnet() as ext_subnet,\
+                self.subnet(cidr='20.0.0.0/24') as int_subnet,\
+                self.port(subnet=int_subnet,
+                          device_owner='compute:None') as int_port:
+            # make net external
+            ext_net_id = ext_subnet['subnet']['network_id']
+            self._update('networks', ext_net_id,
+                     {'network': {external_net.EXTERNAL: True}})
+
+            router = self._create_router(distributed=dvr)
+            self.l3_plugin.update_router(
+                self.context, router['id'],
+                {'router': {
+                    'external_gateway_info': {'network_id': ext_net_id}}})
+            self.l3_plugin.add_router_interface(
+                self.context, router['id'],
+                {'subnet_id': int_subnet['subnet']['id']})
+
+            floating_ip = {'floating_network_id': ext_net_id,
+                           'router_id': router['id'],
+                           'port_id': int_port['port']['id'],
+                           'tenant_id': int_port['port']['tenant_id']}
+            with mock.patch.object(
+                    self.l3_plugin, '_l3_rpc_notifier') as l3_notif:
+                self.l3_plugin.create_floatingip(
+                    self.context, {'floatingip': floating_ip})
+                if dvr:
+                    l3_notif.routers_updated_on_host.assert_called_once_with(
+                        self.context, [router['id']],
+                        int_port['port']['binding:host_id'])
+                    self.assertFalse(l3_notif.routers_updated.called)
+                else:
+                    l3_notif.routers_updated.assert_called_once_with(
+                        self.context, [router['id']], 'create_floatingip')
+                    self.assertFalse(
+                        l3_notif.routers_updated_on_host.called)
+
+    def test_create_floating_ip_agent_notification(self):
+        self._test_create_floating_ip_agent_notification()
+
+    def test_create_floating_ip_agent_notification_non_dvr(self):
+        self._test_create_floating_ip_agent_notification(dvr=False)