From 675f15376f4ff99c16e59c005ac2ef157baf9431 Mon Sep 17 00:00:00 2001
From: Jaganathan Palanisamy <jpalanis@redhat.com>
Date: Sun, 11 Nov 2018 12:49:24 -0500
Subject: [PATCH] Derives NUMA aware vSwitches parameters

Derives the NUMA aware vSwitches parameters NeutronPhysnetNUMANodesMapping
and NeutronTunnelNUMANodes automatically using host introspection.

Change-Id: Ia3a55ec158b16485cd3ffa56d4931b73502a1058
Closes-Bug: #1797925
---
 tripleo_common/actions/derive_params.py       |  6 +-
 .../tests/actions/test_derive_params.py       | 18 ++++--
 workbooks/derive_params.yaml                  |  1 +
 workbooks/derive_params_formulas.yaml         | 55 ++++++++++++++++++-
 4 files changed, 73 insertions(+), 7 deletions(-)

diff --git a/tripleo_common/actions/derive_params.py b/tripleo_common/actions/derive_params.py
index 562ef7b88..74c61406d 100644
--- a/tripleo_common/actions/derive_params.py
+++ b/tripleo_common/actions/derive_params.py
@@ -142,6 +142,8 @@ class GetDpdkNicsNumaInfoAction(base.TripleOAction):
         # with mtu and numa node id
         for config in self.network_configs:
             if config.get('type', '') == 'ovs_user_bridge':
+                bridge_name = config.get('name', '')
+                addresses = config.get('addresses', [])
                 members = config.get('members', [])
                 dpdk_ifaces, mtu = self.get_dpdk_interfaces(members)
                 for dpdk_iface in dpdk_ifaces:
@@ -156,7 +158,9 @@ class GetDpdkNicsNumaInfoAction(base.TripleOAction):
 
                     dpdk_nic_info = {'name': phy_name,
                                      'numa_node': node,
-                                     'mtu': mtu}
+                                     'mtu': mtu,
+                                     'bridge_name': bridge_name,
+                                     'addresses': addresses}
                     dpdk_nics_numa_info.append(dpdk_nic_info)
         return dpdk_nics_numa_info
 
diff --git a/tripleo_common/tests/actions/test_derive_params.py b/tripleo_common/tests/actions/test_derive_params.py
index acc3c23d8..e2d90c56a 100644
--- a/tripleo_common/tests/actions/test_derive_params.py
+++ b/tripleo_common/tests/actions/test_derive_params.py
@@ -32,7 +32,8 @@ class GetDpdkNicsNumaInfoActionTest(base.TestCase):
                 "mtu": 8192,
                 "rx_queue": 4}],
             "name": "br-link",
-            "type": "ovs_user_bridge"}]
+            "type": "ovs_user_bridge",
+            "addresses": [{"ip_netmask": ""}]}]
 
         inspect_data = {
             "numa_topology": {
@@ -63,7 +64,9 @@ class GetDpdkNicsNumaInfoActionTest(base.TestCase):
                 }
             }
 
-        expected_result = [{'name': 'ens802f1', 'mtu': 8192, 'numa_node': 1}]
+        expected_result = [{'bridge_name': 'br-link', 'name': 'ens802f1',
+                            'mtu': 8192, 'numa_node': 1,
+                            'addresses': [{'ip_netmask': ''}]}]
 
         mock_ctx = mock.MagicMock()
         action = derive_params.GetDpdkNicsNumaInfoAction(network_configs,
@@ -84,7 +87,8 @@ class GetDpdkNicsNumaInfoActionTest(base.TestCase):
                                       "members": [{"type": "interface",
                                                    "name": "nic5"}]}]}],
             "name": "br-link",
-            "type": "ovs_user_bridge"}]
+            "type": "ovs_user_bridge",
+            "addresses": [{"ip_netmask": "172.16.10.0/24"}]}]
         inspect_data = {
             "numa_topology": {
                 "nics": [{"name": "ens802f1", "numa_node": 1},
@@ -113,8 +117,12 @@ class GetDpdkNicsNumaInfoActionTest(base.TestCase):
                                 "name": "enp13s0f1"}]
                 }
             }
-        expected_result = [{'mtu': 9000, 'numa_node': 1, 'name': 'ens802f0'},
-                           {'mtu': 9000, 'numa_node': 1, 'name': 'ens802f1'}]
+        expected_result = [{'bridge_name': 'br-link', 'mtu': 9000,
+                            'numa_node': 1, 'name': 'ens802f0',
+                            'addresses': [{'ip_netmask': '172.16.10.0/24'}]},
+                           {'bridge_name': 'br-link', 'mtu': 9000,
+                            'numa_node': 1, 'name': 'ens802f1',
+                            'addresses': [{'ip_netmask': '172.16.10.0/24'}]}]
 
         mock_ctx = mock.MagicMock()
         action = derive_params.GetDpdkNicsNumaInfoAction(network_configs,
diff --git a/workbooks/derive_params.yaml b/workbooks/derive_params.yaml
index 577191798..c9bdd4aa6 100644
--- a/workbooks/derive_params.yaml
+++ b/workbooks/derive_params.yaml
@@ -291,6 +291,7 @@ workflows:
         input:
           plan: <% $.plan %>
           role_name: <% $.role_name %>
+          heat_resource_tree: <% $.heat_resource_tree %>
           hw_data: <% $.hw_data %>
           user_inputs: <% $.user_inputs %>
         publish:
diff --git a/workbooks/derive_params_formulas.yaml b/workbooks/derive_params_formulas.yaml
index 640d681c4..df172511b 100644
--- a/workbooks/derive_params_formulas.yaml
+++ b/workbooks/derive_params_formulas.yaml
@@ -12,6 +12,7 @@ workflows:
     input:
       - plan
       - role_name
+      - heat_resource_tree
       - hw_data # introspection data
       - user_inputs
       - derived_parameters: {}
@@ -117,13 +118,65 @@ workflows:
         publish:
           sock_mem: <% task().result %>
         on-success:
-          - get_dpdk_parameters: <% $.sock_mem %>
+          - get_neutron_bridge_mappings: <% $.sock_mem %>
           - set_status_failed_get_sock_mem: <% not $.sock_mem %>
         on-error: set_status_failed_on_error_get_sock_mem
 
+      get_neutron_bridge_mappings:
+        publish:
+          neutron_bridge_mappings: <% $.heat_resource_tree.parameters.get('NeutronBridgeMappings', {}).get('default', '') %>
+        on-success:
+          - get_phy_nw_bridge_mappings: <% $.neutron_bridge_mappings %>
+          - get_neutron_network_type: <% not $.neutron_bridge_mappings %>
+
+      # Gets the physical network and ovs bridge mappings
+      get_phy_nw_bridge_mappings:
+        publish:
+          phy_nw_bridge_mappings: <% $.neutron_bridge_mappings.split(',').select(let(mapping => $.split(':')) -> dict($mapping[0] => $mapping[1])).sum() %>
+        on-success: get_bridge_numa_nodes_mappings
+
+      # Gets the ovs bridge and NUMA nodes mappings
+      get_bridge_numa_nodes_mappings:
+        publish:
+          bridge_numa_nodes_mappings: <% $.dpdk_nics_numa_info.groupBy($.bridge_name).select(dict($[0]=>$[1].select($.numa_node).distinct())).sum() %>
+        on-success: get_phy_nw_numa_nodes_mappings
+
+      # Gets the physical network and NUMA nodes mappings
+      get_phy_nw_numa_nodes_mappings:
+        publish:
+          phy_nw_numa_nodes_mappings: <% let(br_numa_mappings => bridge_numa_nodes_mappings) -> $.phy_nw_bridge_mappings.items().select(dict($[0]=>$br_numa_mappings.get($[1], []))).sum() %>
+        on-success: get_neutron_network_type
+
+      get_neutron_network_type:
+        publish:
+          neutron_network_type: <% $.heat_resource_tree.parameters.get('NeutronNetworkType', {}).get('default', '') %>
+        on-success:
+          - get_tunnel_numa_nodes_mappings: <% 'vxlan' in $.neutron_network_type %>
+          - get_dpdk_parameters: <% not 'vxlan' in $.neutron_network_type %>
+
+      # Gets the list of NUMA nodes associated to all tunneled networks
+      # OVS-DPDK on VxLAN tunnel requires Tenant Network IP to be applied on the OVS User Bridge itself.
+      # With this assumption, if the IP is set on the OVS User Bridge, then OVS-DPDK is used for VxLAN tunnels also.
+      # Here dpdk_nics_numa_info will have the OVS User Bridges with DPDK ports only.
+      get_tunnel_numa_nodes_mappings:
+        publish:
+          tunnel_numa_nodes_mappings: <% $.dpdk_nics_numa_info.where($.addresses.any($.ip_netmask)).select($.numa_node).distinct() %>
+        on-success: get_dpdk_parameters
+
       get_dpdk_parameters:
         publish:
           dpdk_parameters: <% dict(concat($.role_name, 'Parameters') => dict('OvsPmdCoreList' => $.get('pmd_cpus', ''), 'OvsDpdkCoreList' => $.get('host_cpus', ''), 'OvsDpdkSocketMemory' => $.get('sock_mem', ''))) %>
+        on-success:
+          - add_phy_nw_numa_nodes_mappings: <% $.get('phy_nw_numa_nodes_mappings', {}) %>
+          - add_tunnel_numa_nodes_mappings: <% $.get('tunnel_numa_nodes_mappings', []) %>
+
+      add_phy_nw_numa_nodes_mappings:
+        publish:
+          dpdk_parameters: <% $.dpdk_parameters.mergeWith(dict(concat($.role_name, 'Parameters') => dict('NeutronPhysnetNUMANodesMapping' => $.get('phy_nw_numa_nodes_mappings', {})))) %>
+
+      add_tunnel_numa_nodes_mappings:
+        publish:
+          dpdk_parameters: <% $.dpdk_parameters.mergeWith(dict(concat($.role_name, 'Parameters') => dict('NeutronTunnelNUMANodes' => $.get('tunnel_numa_nodes_mappings', [])))) %>
 
       set_status_failed_get_network_config:
         publish: