diff options
author | Alexandru Avadanii <Alexandru.Avadanii@enea.com> | 2018-11-08 19:06:46 +0100 |
---|---|---|
committer | Alexandru Avadanii <Alexandru.Avadanii@enea.com> | 2019-01-09 15:39:05 +0100 |
commit | 455b46a6be4bca145c047ed6957727c119285796 (patch) | |
tree | 717062888465d74227a45d724fab21e9c8fd5957 | |
parent | ad2bdf2eb08c0991757f30d370c90d5c9d814d3e (diff) |
Bring in FDIO (VPP+DPDK) scenario
- cmp, gtw: bump RAM allocation to accomodate hugepages/VPP;
for now we overcommit, gtw01 resources can probably be lowered;
- submodule: add salt-formula-neutron so we can locally patch it;
- repo:
* FD.IO repos for VPP packages;
* networking-vpp PPA for python-networking-vpp Neutron driver;
- use vpp-router for L3, disable neutron-l3-agent;
- baremetal_init: apply repo config before network (otherwise UCA
repo is missing when trying to install DPDK on baremetal nodes);
- arm64: iommu.passthrough=1 is required on ThunderX for VPP on
newer kernels;
Design quirks:
- vpp service runs as 'neutron' user, which does not exist at the
time VPP is installed and initially started, hence the need to
restart it before starting the vpp-agent service;
- gtw01 node has DPDK, yet to configure it via IDF we use the
compute-specific OVS-targeted parameters like
`compute_ovs_dpdk_socket_mem`, which is a bit misleading;
- vpp-agent requires ml2_conf.ini on ALL compute AND network nodes
to parse per-node physnet-to-real interface names;
- vpp process is bound to core '1' (not parameterized via IDF);
Change-Id: I659f7dbebcab7b154e7b1fb829cd7159b4372ec8
Signed-off-by: Alexandru Avadanii <Alexandru.Avadanii@enea.com>
20 files changed, 700 insertions, 73 deletions
diff --git a/mcp/config/scenario/os-nosdn-fdio-noha.yaml b/mcp/config/scenario/os-nosdn-fdio-noha.yaml index b52a89cf4..747adbee2 100644 --- a/mcp/config/scenario/os-nosdn-fdio-noha.yaml +++ b/mcp/config/scenario/os-nosdn-fdio-noha.yaml @@ -24,4 +24,35 @@ virtual: vcpus: 4 ram: 14336 gtw01: - ram: 2048 + vcpus: 8 + ram: 8192 + cpu_topology: + sockets: 1 + cores: 4 + threads: 2 + numa: + cell0: + memory: 8388608 + cpus: 0-7 + cmp001: + vcpus: 8 + ram: 8192 + cpu_topology: + sockets: 1 + cores: 4 + threads: 2 + numa: + cell0: + memory: 8388608 + cpus: 0-7 + cmp002: + vcpus: 8 + ram: 8192 + cpu_topology: + sockets: 1 + cores: 4 + threads: 2 + numa: + cell0: + memory: 8388608 + cpus: 0-7 diff --git a/mcp/config/states/baremetal_init b/mcp/config/states/baremetal_init index 358e1874d..ba7ae30e5 100755 --- a/mcp/config/states/baremetal_init +++ b/mcp/config/states/baremetal_init @@ -27,7 +27,7 @@ salt -C "${cluster_nodes_query}" file.replace $debian_ip_source \ repl="\n if not __salt__['pkg.version']('vlan'):\n __salt__['pkg.install']('vlan')" salt -C "${cluster_nodes_query}" pkg.install bridge-utils -salt -C "${control_nodes_query}" state.apply linux.network,linux.system.kernel +salt -C "${control_nodes_query}" state.apply linux.system.repo,linux.network,linux.system.kernel wait_for 5.0 "salt -C '${cluster_nodes_query}' state.apply salt.minion" wait_for 5.0 "salt -C '${compute_nodes_query}' state.apply linux.system,linux.network" wait_for 30.0 "salt -C '${cluster_nodes_query}' test.ping" diff --git a/mcp/config/states/openstack_noha b/mcp/config/states/openstack_noha index 98e2eff73..01f686b1f 100755 --- a/mcp/config/states/openstack_noha +++ b/mcp/config/states/openstack_noha @@ -38,7 +38,7 @@ salt -I 'heat:server' state.sls heat salt -I 'cinder:controller' state.sls cinder wait_for 3 "salt -I 'cinder:volume' state.sls cinder" -salt -I 'neutron:server' state.sls neutron +salt -I 'neutron:server' state.sls etcd,neutron salt -I 'neutron:compute' state.sls neutron salt -I 'nova:compute' state.sls nova diff --git a/mcp/patches/salt-formula-linux/0002-network-Bring-in-basic-VPP-support.patch b/mcp/patches/salt-formula-linux/0002-network-Bring-in-basic-VPP-support.patch new file mode 100644 index 000000000..756c575b4 --- /dev/null +++ b/mcp/patches/salt-formula-linux/0002-network-Bring-in-basic-VPP-support.patch @@ -0,0 +1,135 @@ +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: Copyright (c) 2018 Mirantis Inc., Enea AB and others. +: +: All rights reserved. This program and the accompanying materials +: are made available under the terms of the Apache License, Version 2.0 +: which accompanies this distribution, and is available at +: http://www.apache.org/licenses/LICENSE-2.0 +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +From: Alexandru Avadanii <Alexandru.Avadanii@enea.com> +Date: Mon, 10 Dec 2018 16:16:26 +0100 +Subject: [PATCH] network: Bring in basic VPP support + +For now, we only care about DPDK-backed VPP ports. + +TODO: +- README.rst: VPP port usage + +Signed-off-by: Alexandru Avadanii <Alexandru.Avadanii@enea.com> +--- + linux/files/vpp_commands.txt | 4 ++++ + linux/files/vpp_startup.conf | 44 ++++++++++++++++++++++++++++++++++++ + linux/network/dpdk.sls | 38 +++++++++++++++++++++++++++++++ + 3 files changed, 86 insertions(+) + create mode 100644 linux/files/vpp_commands.txt + create mode 100644 linux/files/vpp_startup.conf + +diff --git a/linux/files/vpp_commands.txt b/linux/files/vpp_commands.txt +new file mode 100644 +index 0000000..2ce4726 +--- /dev/null ++++ b/linux/files/vpp_commands.txt +@@ -0,0 +1,4 @@ ++{%- from "linux/map.jinja" import network with context %} ++{%- if network.vpp.commands is defined %} ++{{ network.vpp.commands }} ++{%- endif %} +diff --git a/linux/files/vpp_startup.conf b/linux/files/vpp_startup.conf +new file mode 100644 +index 0000000..d66ecd3 +--- /dev/null ++++ b/linux/files/vpp_startup.conf +@@ -0,0 +1,44 @@ ++{%- from "linux/map.jinja" import network with context %} ++unix { ++ cli-listen /run/vpp/cli.sock ++ log /var/log/vpp.log ++ full-coredump ++ nodaemon ++ startup-config /etc/vpp/commands.txt ++{%- if network.vpp.gid is defined %} ++ gid {{ network.vpp.gid }} ++{%- endif %} ++} ++api-trace { ++ on ++} ++{%- if network.vpp.gid is defined %} ++api-segment { ++ gid {{ network.vpp.gid }} ++} ++{%- endif %} ++cpu { ++{%- if network.vpp.main_core is defined %} ++ main-core {{ network.vpp.main_core }} ++{%- endif %} ++{%- if network.vpp.corelist_workers is defined %} ++ corelist-workers {{ network.vpp.corelist_workers }} ++{%- endif %} ++{%- if network.vpp.skip_core is defined %} ++ skip-core {{ network.vpp.skip_core }} ++{%- endif %} ++{%- if network.vpp.workers is defined %} ++ workers {{ network.vpp.workers }} ++{%- endif %} ++} ++dpdk { ++{%- if network.vpp.dpdk_socket_mem is defined %} ++ socket-mem {{ network.vpp.dpdk_socket_mem }} ++{%- endif %} ++ ## Whitelist specific interface by specifying PCI address ++{%- for interface_name, interface in network.interface.items() %} ++{%- if 'dpdk_vpp_port' in interface.type and interface.pci is defined %} ++ dev {{ interface.pci }} ++{%- endif %} ++{%- endfor %} ++} +diff --git a/linux/network/dpdk.sls b/linux/network/dpdk.sls +index 786f7c8..09453c6 100644 +--- a/linux/network/dpdk.sls ++++ b/linux/network/dpdk.sls +@@ -32,6 +32,44 @@ linux_network_dpdk_service: + - watch: + - file: /etc/dpdk/interfaces + ++{%- if network.vpp is defined %} ++ ++vpp_pkgs: ++ pkg.installed: ++ - pkgs: ++ - vpp ++ - vpp-plugins ++ - bridge-utils ++ ++/etc/vpp/commands.txt: ++ file.managed: ++ - source: salt://linux/files/vpp_commands.txt ++ - template: jinja ++ - require: ++ - pkg: vpp_pkgs ++ ++/etc/vpp/startup.conf: ++ file.managed: ++ - source: salt://linux/files/vpp_startup.conf ++ - template: jinja ++ - require: ++ - pkg: vpp_pkgs ++ ++/etc/sysctl.d/80-vpp.conf: ++ file.managed: ++ - contents: ++ - '# Disabled by salt-formula-linux' ++ ++linux_network_vpp_service: ++ service.running: ++ - enable: true ++ - name: vpp ++ - watch: ++ - file: /etc/vpp/startup.conf ++ - file: /etc/vpp/commands.txt ++ ++{%- endif %} ++ + {%- if network.openvswitch is defined %} + + openvswitch_dpdk_pkgs: diff --git a/mcp/patches/salt-formula-neutron/0001-Bring-in-basic-VPP-support.patch b/mcp/patches/salt-formula-neutron/0001-Bring-in-basic-VPP-support.patch new file mode 100644 index 000000000..3e04bc823 --- /dev/null +++ b/mcp/patches/salt-formula-neutron/0001-Bring-in-basic-VPP-support.patch @@ -0,0 +1,204 @@ +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: Copyright (c) 2018 Mirantis Inc., Enea AB and others. +: +: All rights reserved. This program and the accompanying materials +: are made available under the terms of the Apache License, Version 2.0 +: which accompanies this distribution, and is available at +: http://www.apache.org/licenses/LICENSE-2.0 +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +From: Alexandru Avadanii <Alexandru.Avadanii@enea.com> +Date: Thu, 6 Dec 2018 18:25:42 +0100 +Subject: [PATCH] Bring in basic VPP support + +TODO: +- update README + +Signed-off-by: Alexandru Avadanii <Alexandru.Avadanii@enea.com> +--- + neutron/agents/_vpp.sls | 25 ++++++++++++++++ + neutron/compute.sls | 2 ++ + neutron/files/queens/_ml2_conf.vpp.ini | 41 ++++++++++++++++++++++++++ + neutron/files/queens/ml2_conf.ini | 5 ++++ + neutron/gateway.sls | 2 ++ + neutron/map.jinja | 16 ++++++++++ + 6 files changed, 91 insertions(+) + create mode 100644 neutron/agents/_vpp.sls + create mode 100644 neutron/files/queens/_ml2_conf.vpp.ini + +diff --git a/neutron/agents/_vpp.sls b/neutron/agents/_vpp.sls +new file mode 100644 +index 0000000..c1845d0 +--- /dev/null ++++ b/neutron/agents/_vpp.sls +@@ -0,0 +1,25 @@ ++{%- if pillar.neutron.gateway is defined %} ++{%- from "neutron/map.jinja" import gateway as neutron with context %} ++{%- else %} ++{%- from "neutron/map.jinja" import compute as neutron with context %} ++{%- endif %} ++ ++{%- if 'vpp' in neutron.get('backend', {}).get('mechanism', []) %} ++ ++/etc/neutron/plugins/ml2/ml2_conf.ini: ++ file.managed: ++ - source: salt://neutron/files/{{ neutron.version }}/_ml2_conf.vpp.ini ++ - mode: 0640 ++ - user: root ++ - group: neutron ++ - template: jinja ++ ++vpp: ++ service.running: ++ - enable: True ++ ++vpp-agent: ++ service.running: ++ - enable: True ++ ++{%- endif %} +diff --git a/neutron/compute.sls b/neutron/compute.sls +index e815410..e99c110 100644 +--- a/neutron/compute.sls ++++ b/neutron/compute.sls +@@ -111,7 +111,9 @@ neutron_metadata_agent: + {%- if compute.opendaylight is defined %} + {%- include "neutron/opendaylight/client.sls" %} + {%- else %} ++ {#- We can reuse this for ml2_vpp and ignore openvswitch_agent.ini #} + {%- include "neutron/ml2_ovs/init.sls" %} ++ {%- include "neutron/agents/_vpp.sls" %} + {%- endif %} + + {%- elif compute.backend.engine == "ovn" %} +diff --git a/neutron/files/queens/_ml2_conf.vpp.ini b/neutron/files/queens/_ml2_conf.vpp.ini +new file mode 100644 +index 0000000..2373f64 +--- /dev/null ++++ b/neutron/files/queens/_ml2_conf.vpp.ini +@@ -0,0 +1,41 @@ ++{%- if pillar.neutron.server is defined %} ++{%- from "neutron/map.jinja" import server as neutron with context %} ++{%- elif pillar.neutron.gateway is defined %} ++{%- from "neutron/map.jinja" import gateway as neutron with context %} ++{%- else %} ++{%- from "neutron/map.jinja" import compute as neutron with context %} ++{%- endif %} ++ ++{%- if 'vpp' in neutron.get('backend', {}).get('mechanism', []) %} ++ ++{%- set physnets_vpp = [] %} ++{%- set mechanism_vpp = neutron.backend.mechanism.vpp %} ++{%- for physnet, params in neutron.backend.get('physnets', {}).iteritems() %} ++{%- if params.get('vpp_interface', False) %} ++{%- do physnets_vpp.append([physnet, params.get('vpp_interface')]|join(":")) %} ++{%- endif %} ++{%- endfor %} ++{%- if not physnets_vpp %} ++{%- do physnets_vpp.append('physnet1:tap-0') %} ++{%- endif %} ++ ++{%- if pillar.neutron.server is not defined %} ++[ml2] ++type_drivers = flat,vlan ++{%- endif %} ++ ++[ml2_vpp] ++jwt_signing = False ++etcd_insecure_explicit_disable_https = True ++l3_hosts = {{ mechanism_vpp.get('l3_hosts', '127.0.0.1') }} ++enable_l3_ha = False ++gpe_locators = ++gpe_src_cidr = ++enable_vpp_restart = False ++etcd_pass = {{ mechanism_vpp.get('etcd_pass', '') }} ++etcd_user = {{ mechanism_vpp.get('etcd_user', '') }} ++etcd_port = {{ mechanism_vpp.get('etcd_port', 2379) }} ++etcd_host = {{ mechanism_vpp.get('etcd_host', '127.0.0.1') }} ++physnets = {{ ','.join(physnets_vpp) }} ++ ++{%- endif %} +diff --git a/neutron/files/queens/ml2_conf.ini b/neutron/files/queens/ml2_conf.ini +index bb2f126..057d325 100644 +--- a/neutron/files/queens/ml2_conf.ini ++++ b/neutron/files/queens/ml2_conf.ini +@@ -28,6 +28,9 @@ agent_boot_time = {{ server.get('agent_boot_time', 180) }} + # List of network type driver entrypoints to be loaded from the + # neutron.ml2.type_drivers namespace. (list value) + #type_drivers = local,flat,vlan,gre,vxlan,geneve ++{%- if 'vpp' in server.backend.get('mechanism', []) %} ++type_drivers = flat,vlan ++{%- endif %} + + # Ordered list of network_types to allocate as tenant networks. The default + # value 'local' is useful for single-box testing but provides no connectivity +@@ -264,6 +267,8 @@ neutron_sync_mode = {{ _ovn.neutron_sync_mode|default('repair') }} + enable_distributed_floating_ip = {{ server.dvr|default('false') }} + {%- endif %} + ++{%- include "neutron/files/queens/_ml2_conf.vpp.ini" %} ++ + {%- if server.backend.opendaylight|default(False) %} + [ml2_odl] + # HTTP URL of OpenDaylight REST interface. (string value) +diff --git a/neutron/gateway.sls b/neutron/gateway.sls +index 61b4372..ca07b9f 100644 +--- a/neutron/gateway.sls ++++ b/neutron/gateway.sls +@@ -40,6 +40,8 @@ haproxy: + + {%- endif %} + ++{%- include "neutron/agents/_vpp.sls" %} ++ + {%- if gateway.l2gw is defined %} + {%- include "neutron/agents/_l2gw.sls" %} + {%- endif %} +diff --git a/neutron/map.jinja b/neutron/map.jinja +index 78e2867..3e93b1f 100644 +--- a/neutron/map.jinja ++++ b/neutron/map.jinja +@@ -12,9 +12,13 @@ + {%- do compute_pkgs_ovn.extend(['neutron-common', 'python-networking-ovn', 'haproxy']) %} + {%- endif %} + {%- set linuxbridge_enabled = pillar.neutron.compute is defined and pillar.neutron.compute.get('backend', {}).get('mechanism', {}).get('lb', {}).get('driver', {}) == "linuxbridge" %} ++{%- set vpp_enabled = 'vpp' in pillar.neutron.get('compute', {}).get('backend', {}).get('mechanism', []) %} + {%- if linuxbridge_enabled %} + {%- set pkgs_cmp = ['neutron-linuxbridge-agent'] %} + {%- set services_cmp = ['neutron-linuxbridge-agent'] %} ++{%- elif vpp_enabled %} ++{%- set pkgs_cmp = ['vpp-agent'] %} ++{%- set services_cmp = ['vpp-agent'] %} + {%- else %} + {%- set pkgs_cmp = ['neutron-openvswitch-agent', 'python-pycadf'] %} + {%- set services_cmp = ['neutron-openvswitch-agent'] %} +@@ -70,11 +74,19 @@ + + {%- set opendaylight_enabled = pillar.neutron.gateway is defined and pillar.neutron.gateway.opendaylight is defined %} + {%- set linuxbridge_enabled = pillar.neutron.gateway is defined and pillar.neutron.gateway.get('backend', {}).get('mechanism', {}).get('lb', {}).get('driver', {}) == "linuxbridge" %} ++{%- set vpp_enabled = 'vpp' in pillar.neutron.get('gateway', {}).get('backend', {}).get('mechanism', []) %} + {%- set pkgs_list = ['neutron-dhcp-agent', 'neutron-metadata-agent'] %} + {%- set services_list = ['neutron-metadata-agent', 'neutron-dhcp-agent'] %} + {%- if linuxbridge_enabled %} + {%- do pkgs_list.extend(['neutron-linuxbridge-agent', 'neutron-l3-agent']) %} + {%- do services_list.extend(['neutron-linuxbridge-agent', 'neutron-l3-agent']) %} ++{%- elif vpp_enabled %} ++{%- do pkgs_list.extend(['vpp-agent']) %} ++{%- do services_list.extend(['vpp-agent']) %} ++{%- if 'vpp-router' not in pillar.neutron.gateway.backend.get('router', '') %} ++{%- do pkgs_list.extend(['neutron-l3-agent']) %} ++{%- do services_list.extend(['neutron-l3-agent']) %} ++{%- endif %} + {%- elif not opendaylight_enabled %} + {%- do pkgs_list.extend(['neutron-openvswitch-agent', 'neutron-l3-agent']) %} + {%- do services_list.extend(['neutron-openvswitch-agent', 'neutron-l3-agent']) %} +@@ -122,6 +134,10 @@ + {%- do server_pkgs_list.append('python-networking-sfc') %} + {%- endif %} + ++{%- if 'vpp' in pillar.neutron.get('server', {}).get('backend', {}).get('mechanism', []) %} ++{%- do server_pkgs_list.extend(['python-networking-vpp']) %} ++{%- endif %} ++ + {% set server = salt['grains.filter_by']({ + 'BaseDefaults': default_params, + 'Debian': { diff --git a/mcp/patches/salt-formula-neutron/0001-Support-rocky-version.patch b/mcp/patches/salt-formula-neutron/0002-Support-rocky-version.patch index 41795486d..41795486d 100644 --- a/mcp/patches/salt-formula-neutron/0001-Support-rocky-version.patch +++ b/mcp/patches/salt-formula-neutron/0002-Support-rocky-version.patch diff --git a/mcp/reclass/classes/cluster/.gitignore b/mcp/reclass/classes/cluster/.gitignore index 0179ad987..37832a89c 100644 --- a/mcp/reclass/classes/cluster/.gitignore +++ b/mcp/reclass/classes/cluster/.gitignore @@ -17,8 +17,9 @@ mcp-*-ha/infra/init_vcp.yml mcp-odl-ha/infra/maas.yml mcp-odl-*/opendaylight/control.yml mcp-odl-ha/openstack/init.yml -mcp-odl-noha/infra/config.yml +mcp-odl-*/infra/config.yml mcp-*-noha/openstack/compute.yml mcp-common-noha/infra/init.yml mcp-common-noha/init_options.yml mcp-*-noha/openstack/gateway.yml +mcp-fdio-noha/infra/config.yml diff --git a/mcp/reclass/classes/cluster/all-mcp-arch-common/fdio_repo.yml b/mcp/reclass/classes/cluster/all-mcp-arch-common/fdio_repo.yml new file mode 100644 index 000000000..a74fa63a2 --- /dev/null +++ b/mcp/reclass/classes/cluster/all-mcp-arch-common/fdio_repo.yml @@ -0,0 +1,51 @@ +############################################################################## +# Copyright (c) 2018 Mirantis Inc., Enea AB and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +parameters: + linux: + system: + repo: + fdio.ubuntu: + # yamllint disable-line rule:line-length + source: "deb [trusted=yes] http://nexus.fd.io/content/repositories/fd.io.ubuntu.${_param:linux_system_codename}.main/ ./" + # NOTE(armband): For now we define all repos on all archs, let APT figure it out + fdio.ubuntu-arm: + # yamllint disable-line rule:line-length + source: "deb [trusted=yes] http://nexus.fd.io/content/repositories/fd.io.ubuntu-arm.${_param:linux_system_codename}.main/ ./" + networking-vpp: + source: "deb http://ppa.launchpad.net/opnfv-fuel/networking-vpp/ubuntu ${_param:linux_system_codename} main" + key: | + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mQINBFwKq9ABEADqWu9anJFs3RJ87i53tU8lBC8JGa55YmRlN7LgvkPYMtXj3xOR + tBn8HJ3B0b2fKx2htUs+oWtFFCkNUmptnNz+tMVdwXt1lXSr2MEzO6PgBBAvak0j + GMLSsI4p60YqoPARMjPXvZ+VNcGZ6RSOKlNnEqSb+M76iaVaqEWBipDR1g+llCd9 + lgUVQ8iKolw+5iCnPnjmm0GdE9iw7Az0aUIv3yXNaEZwnGb9egdoioY4OvkY9HqR + KkgsrTVBWiTOsoDctrPkLNsB1BZLA/Qkgv4Sih2Bc7atgid6SvvuGClex+9MdBPQ + r0nT03O0uiXQ4Zk/ULlXaE2ci9dhMD5SNspgZnEULcubqL/Xd2iq6DlW22iXmj2X + PSoF6YxrtxlocaC2ChKFGITR7yiudxDYSCyBzXBMP7zfLVwZC3IX309HaxJRPCk5 + PEatmq0++z3lWfNXEjQ48Rt0mYTC5ktcJQGpSSp30hjrIfz5Jxa/FACQCJBGbr0/ + jO6cB6TJpHDnwdsEvCLJmeI6+OYkEzExarL8Wg8DdQUo5uppS4zANAgMsUbVqFz5 + 7WDlLMKPRAheEdZJIwCHXZrB3TibZTNUuafmQD+4a50cfKgNHlb+ks/5gbkxRdNj + DdZYI6gbh7PZcvIKOvakrEer8RIpqgSXyWPxIviyCGpp/+webUyapFwstQARAQAB + tBxMYXVuY2hwYWQgUFBBIGZvciBPUE5GViBGdWVsiQI4BBMBAgAiBQJcCqvQAhsD + BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCVWdwe/i1a+JgbEADZPwsdXStw + kqS+bg+bL4sCK55LnYAPWWnqXLuqpEEXusuGYEyahu69SOidL3/AXY1iM9FnbBE0 + qyycLQVOv/lt7Bs1WVg7M3gNjTsnCH7RbZsGVWDnOuZ1G0KP2o72dmrR9GYFArHA + MMc3YVoKAWhRBWHUKdSp/D68i/cfJ4V1PNhDpchOz4ytPjo2xyHyBW+wxLxNiC32 + 3uZeT7EpO8UbhuFDd3+PLaNrI1p2mkYxdmTpVBLIdKdAMq1QYi0B1nLvJ7Cp2yck + 2HKrI6pb74l7dkQOxx+x/inAMbZKX/AvKSjzyJ+Fxc4TT28m79QLuHtORiaPWCep + HePcl/0Qu2n85qOtWbWFWCJwlmvfTkHw2u7PEjutTgX9zOLdEFliu3v9nhvec7Mk + AzwpilBD6eAHav8Yhx6CKNR5GReK3viJ8+lso/D/56ap7el+W+M6K59imJ/r8WVx + 79qPXTAB29Co8hC5ky2qqeHMHw39VqC/JpCYPjH7qZNyWWhXBwHcobktuCc+tXdq + t1qlTz0aU/DLGUW8Buk9R6ZZTvSUibT8tRqDYtVhyJ7u/2qCdqhFoculWr6e6DQF + KP41NGKN4LtqQh7HmFCswvBnlu7BpkVlBqlHEMpqRUbJd7fg0oGkEf6P8hhWwdd2 + 0keWK/lCMRHDEN6+/1ppP7M90/JyUPXfFA== + =73aY + -----END PGP PUBLIC KEY BLOCK----- diff --git a/mcp/reclass/classes/cluster/all-mcp-arch-common/infra/config_pdf.yml.j2 b/mcp/reclass/classes/cluster/all-mcp-arch-common/infra/config_pdf.yml.j2 index 3c7fa50b8..452f7f9d9 100644 --- a/mcp/reclass/classes/cluster/all-mcp-arch-common/infra/config_pdf.yml.j2 +++ b/mcp/reclass/classes/cluster/all-mcp-arch-common/infra/config_pdf.yml.j2 @@ -6,6 +6,7 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## {%- import 'net_map.j2' as nm with context %} +{%- import 'net_macros.j2' as ma with context %} --- classes: - system.reclass.storage.salt @@ -61,11 +62,14 @@ parameters: linux_system_codename: xenial {#- No partial defaults, all or nothing. Defaults tuned for lf-pod2. #} - {%- if '-ovs-' in conf.MCP_DEPLOY_SCENARIO %} + {%- if '-ovs-' in conf.MCP_DEPLOY_SCENARIO or '-fdio-' in conf.MCP_DEPLOY_SCENARIO %} + {%- set private_speed = conf.nodes[i].interfaces[nm.idx_private].speed %} + {%- set private_pci = conf.idf.fuel.network.node[i].busaddr[nm.idx_private] %} {%- if conf.idf.fuel.reclass is defined %} {%- if conf.idf.fuel.reclass.node[i].compute_params.dpdk is defined %} {#- Can't dump json here due to dpdk0_* below, explicitly create yaml #} {%- set _dpdk = conf.idf.fuel.reclass.node[i].compute_params.dpdk %} + {%- set private_drv = _dpdk.dpdk0_driver %} {%- for _i in _dpdk %} {{ _i }}: '"{{ _dpdk[_i] }}"' {%- endfor %} @@ -84,7 +88,8 @@ parameters: dpdk0_n_rxq: 2 {%- endif %} dpdk0_name: {{ conf.idf.fuel.network.node[i].interfaces[nm.idx_private] }} - dpdk0_pci: '"{{ conf.idf.fuel.network.node[i].busaddr[nm.idx_private] }}"' + dpdk0_pci: '"{{ private_pci }}"' + dpdk0_vpp: {{ ma.vpp_interface_str(private_speed, private_pci, private_drv or '') }} {%- else %} {%- if conf.idf.fuel.reclass is defined %} {%- if conf.idf.fuel.reclass.node[i].compute_params.common is defined %} diff --git a/mcp/reclass/classes/cluster/mcp-common-noha/openstack_compute_pdf.yml.j2 b/mcp/reclass/classes/cluster/mcp-common-noha/openstack_compute_pdf.yml.j2 index 1f45ddf35..e1de5206d 100644 --- a/mcp/reclass/classes/cluster/mcp-common-noha/openstack_compute_pdf.yml.j2 +++ b/mcp/reclass/classes/cluster/mcp-common-noha/openstack_compute_pdf.yml.j2 @@ -11,7 +11,7 @@ {#- Filter-out NIC duplicates by constructing a dict (used NICs only) #} {%- set nics = { nm.cmp001.nic_mgmt: True, nm.cmp001.nic_private: True } %} {%- set vlans = { nm.vlan_mgmt: nm.cmp001.nic_mgmt } %} -{%- if '-ovs-' not in conf.MCP_DEPLOY_SCENARIO %} +{%- if '-ovs-' not in conf.MCP_DEPLOY_SCENARIO and '-fdio-' not in conf.MCP_DEPLOY_SCENARIO %} {%- set vlan_private_start = (nm.vlan_private | string).rsplit('-')[0] %} {%- do vlans.update({ vlan_private_start: nm.cmp001.nic_private }) %} {%- endif %} @@ -29,8 +29,20 @@ parameters: {%- endif %} linux: network: +{%- if '-fdio-' not in conf.MCP_DEPLOY_SCENARIO %} ovs_nowait: true bridge: openvswitch +{%- else %} + dpdk: + enabled: true + driver: "${_param:compute_dpdk_driver}" + vpp: + enabled: true + # Reuse ovs-dpdk socket mem configuration from IDF + dpdk_socket_mem: ${_param:compute_ovs_dpdk_socket_mem} + main_core: ${linux:system:kernel:isolcpu} + gid: 'neutron' +{%- endif %} interface: pxe_admin_int: enabled: true diff --git a/mcp/reclass/classes/cluster/mcp-common-noha/openstack_gateway_pdf.yml.j2 b/mcp/reclass/classes/cluster/mcp-common-noha/openstack_gateway_pdf.yml.j2 index bd62ae990..d2003d235 100644 --- a/mcp/reclass/classes/cluster/mcp-common-noha/openstack_gateway_pdf.yml.j2 +++ b/mcp/reclass/classes/cluster/mcp-common-noha/openstack_gateway_pdf.yml.j2 @@ -11,14 +11,34 @@ {#- Filter-out NIC duplicates by constructing a dict (used NICs only) #} {%- set nics = { nm.ctl01.nic_mgmt: True, nm.ctl01.nic_private: True } %} {%- set vlans = { nm.vlan_mgmt: nm.ctl01.nic_mgmt } %} -{%- if '-ovs-' not in conf.MCP_DEPLOY_SCENARIO %} +{%- if '-fdio-' in conf.MCP_DEPLOY_SCENARIO %} +{%- do nics.update({ nm.ctl01.nic_public: True }) %} +{%- do vlans.update({ nm.vlan_public: nm.ctl01.nic_public }) %} +{%- elif '-ovs-' not in conf.MCP_DEPLOY_SCENARIO %} {%- set vlan_private_start = (nm.vlan_private | string).rsplit('-')[0] %} {%- do vlans.update({ vlan_private_start: nm.ctl01.nic_private }) %} {%- endif %} parameters: linux: network: +{%- if '-fdio-' not in conf.MCP_DEPLOY_SCENARIO %} +{%- set floating_br_type = 'ovs_bridge' %} bridge: openvswitch +{%- else %} +{%- set floating_br_type = 'bridge' %} + vpp: + enabled: true + # Reuse ovs-dpdk socket mem configuration from IDF + dpdk_socket_mem: ${_param:compute_ovs_dpdk_socket_mem} + main_core: ${linux:system:kernel:isolcpu} + gid: 'neutron' + commands: | + create tap host-if-name vpp_ext_tap host-bridge br-floating rx-ring-size 1024 tx-ring-size 1024 + set interface state ${_param:external_vpp_tap} up + dpdk: + enabled: true + driver: "${_param:compute_dpdk_driver}" +{%- endif %} interface: pxe_admin_int: enabled: true @@ -39,6 +59,7 @@ parameters: {{ ma.linux_network_interfaces_vlan(vlans) }} +{%- if '-fdio-' not in conf.MCP_DEPLOY_SCENARIO %} ovs_port_{{ nm.ctl01.nic_public }}: enabled: true name: {{ ma.interface_str(nm.ctl01.nic_public, nm.vlan_public) }} @@ -48,16 +69,17 @@ parameters: type: ovs_port ovs_bridge: br-floating bridge: br-floating +{%- endif %} br-floating: enabled: true - type: ovs_bridge + type: {{ floating_br_type }} proto: static address: ${_param:external_address} netmask: ${_param:opnfv_net_public_mask} - use_interfaces: - - {{ ma.interface_str(nm.ctl01.nic_public, nm.vlan_public) }} gateway: ${_param:opnfv_net_public_gw} name_servers: {{ nm.dns_public }} + use_interfaces: + - {{ ma.interface_str(nm.ctl01.nic_public, nm.vlan_public) }} noifupdown: true br-mgmt: enabled: true diff --git a/mcp/reclass/classes/cluster/mcp-fdio-ha/openstack/init.yml b/mcp/reclass/classes/cluster/mcp-fdio-ha/openstack/init.yml index 9dbfd59a1..8aa203d0c 100644 --- a/mcp/reclass/classes/cluster/mcp-fdio-ha/openstack/init.yml +++ b/mcp/reclass/classes/cluster/mcp-fdio-ha/openstack/init.yml @@ -8,6 +8,7 @@ --- classes: - cluster.mcp-common-ha.openstack_init + - cluster.all-mcp-arch-common.fdio_repo parameters: _param: neutron_tenant_network_types: "flat,vxlan" diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml b/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml deleted file mode 100644 index 6627ae6fe..000000000 --- a/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml +++ /dev/null @@ -1,22 +0,0 @@ -############################################################################## -# Copyright (c) 2018 Mirantis Inc., Enea AB and others. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## ---- -classes: - - system.reclass.storage.system.openstack_gateway_single - - cluster.mcp-common-noha.infra.config - - cluster.mcp-fdio-noha - - cluster.all-mcp-arch-common.infra.config_pdf -parameters: - reclass: - storage: - node: - openstack_gateway_node01: - params: - tenant_address: ${_param:opnfv_openstack_gateway_node01_tenant_address} - external_address: ${_param:opnfv_openstack_gateway_node01_external_address} - pxe_admin_address: ${_param:opnfv_openstack_gateway_node01_pxe_admin_address} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml.j2 b/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml.j2 new file mode 100644 index 000000000..fefdf217e --- /dev/null +++ b/mcp/reclass/classes/cluster/mcp-fdio-noha/infra/config.yml.j2 @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2018 Mirantis Inc., Enea AB and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +{%- import 'net_map.j2' as nm with context %} +{%- import 'net_macros.j2' as ma with context %} +{#- Until SDF is implemented, hardcode gtw01 node index in IDF as ctl01 +1 #} +{%- set gi = nm.ctl01.idx + 1 %} +--- +classes: + - system.reclass.storage.system.openstack_gateway_single + - cluster.mcp-common-noha.infra.config + - cluster.mcp-fdio-noha + - cluster.all-mcp-arch-common.infra.config_pdf +parameters: + reclass: + storage: + node: + openstack_gateway_node01: + params: + tenant_address: ${_param:opnfv_openstack_gateway_node01_tenant_address} + external_address: ${_param:opnfv_openstack_gateway_node01_external_address} + pxe_admin_address: ${_param:opnfv_openstack_gateway_node01_pxe_admin_address} +{%- if '-fdio-' in conf.MCP_DEPLOY_SCENARIO %} +{%- set private_speed = conf.nodes[gi].interfaces[nm.idx_private].speed %} +{%- set private_pci = conf.idf.fuel.network.node[gi].busaddr[nm.idx_private] %} + # We reuse compute-specific configuration from IDF, so we don't have + # to rework everything in both Pharos and Fuel + # However, OVS-related configuration is unused and only DPDK is relevant + {%- if conf.idf.fuel.reclass is defined %} + {%- if conf.idf.fuel.reclass.node[gi].compute_params.dpdk is defined %} + {#- Can't dump json here due to dpdk0_* below, explicitly create yaml #} + {%- set _dpdk = conf.idf.fuel.reclass.node[gi].compute_params.dpdk %} + {%- set private_drv = _dpdk.dpdk0_driver %} + {%- for _i in _dpdk %} + {{ _i }}: '"{{ _dpdk[_i] }}"' + {%- endfor %} + {%- endif %} + {%- else %} + compute_hugepages_size: 2M + compute_hugepages_count: 8192 + compute_hugepages_mount: /mnt/hugepages_2M + compute_kernel_isolcpu: 2,3,10,11 + compute_dpdk_driver: uio + compute_ovs_pmd_cpu_mask: '"0xc04"' + compute_ovs_dpdk_socket_mem: '"2048,2048"' + compute_ovs_dpdk_lcore_mask: '"0x8"' + compute_ovs_memory_channels: '"2"' + dpdk0_driver: igb_uio + dpdk0_n_rxq: 2 + {%- endif %} + dpdk0_name: {{ conf.idf.fuel.network.node[gi].interfaces[nm.idx_private] }} + dpdk0_pci: '"{{ conf.idf.fuel.network.node[gi].busaddr[nm.idx_private] }}"' + dpdk0_vpp: {{ ma.vpp_interface_str(private_speed, private_pci, private_drv or '') }} +{%- endif %} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/compute.yml.j2 b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/compute.yml.j2 index 2f1b8c39e..825d9d550 100644 --- a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/compute.yml.j2 +++ b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/compute.yml.j2 @@ -6,27 +6,54 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## {%- import 'net_map.j2' as nm with context %} -{%- import 'net_macros.j2' as ma with context %} -{%- set vlan_private_start = (nm.vlan_private | string).rsplit('-')[0] %} --- classes: - service.neutron.compute.single + - system.nova.compute.nfv.hugepages - cluster.mcp-common-noha.openstack_compute - cluster.mcp-fdio-noha parameters: + neutron: + compute: + backend: + router: 'vpp-router' + tenant_network_types: "${_param:neutron_tenant_network_types}" + ~mechanism: + vpp: + driver: vpp + etcd_port: ${_param:node_port} + etcd_host: ${_param:node_address} + l3_hosts: ${_param:openstack_gateway_node01_hostname} + physnets: + physnet1: + vpp_interface: ${_param:external_vpp_tap} + physnet2: + vpp_interface: ${_param:dpdk0_vpp} linux: + system: + kernel: + isolcpu: 1 # NOTE: Hardcoded for now + boot_options: + - spectre_v2=off + - nopti + - intel_iommu=on + - iommu=pt + - nohz_full=${linux:system:kernel:isolcpu} + - rcu_nocbs=${linux:system:kernel:isolcpu} + - iommu.passthrough=1 network: interface: + dpdk0: + name: ${_param:dpdk0_name} + pci: ${_param:dpdk0_pci} + driver: ${_param:dpdk0_driver} + enabled: true + type: dpdk_vpp_port + mtu: ${_param:interface_mtu} + {{ nm.cmp001.nic_private }}: + type: dpdk # Not a meaningful type, just match 'dpdk' for filtering pxe_admin_int: # For scenarios without public network on cmp, set admin gw gateway: {{ nm.net_admin_gw }} name_servers: - {{ nm.net_admin_gw }} - br-mesh: - enabled: true - type: bridge - proto: static - address: ${_param:tenant_address} - netmask: ${_param:opnfv_net_private_mask} - use_interfaces: - - {{ ma.interface_str(nm.cmp001.nic_private, vlan_private_start) }} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/control.yml b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/control.yml index 3c2cdefaa..0faf1b86a 100644 --- a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/control.yml +++ b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/control.yml @@ -7,6 +7,65 @@ ############################################################################## --- classes: - - system.neutron.control.openvswitch.single - cluster.mcp-common-noha.openstack_control - cluster.mcp-fdio-noha + - system.neutron.control.single + - service.etcd.server.single + - system.galera.server.database.neutron +# NOTE: All this configuration should later be moved to reclass.system as +# neutron.control.vpp.single +parameters: + _param: + # yamllint disable rule:truthy + neutron_control_dvr: True + neutron_l3_ha: False + neutron_enable_qos: False + neutron_enable_vlan_aware_vms: False + neutron_enable_bgp_vpn: False + # yamllint enable rule:truthy + neutron_global_physnet_mtu: 1500 + neutron_external_mtu: 1500 + neutron_bgp_vpn_driver: bagpipe + internal_protocol: 'http' + neutron_firewall_driver: 'iptables_hybrid' + openstack_node_role: primary + neutron: + server: + role: ${_param:openstack_node_role} + global_physnet_mtu: ${_param:neutron_global_physnet_mtu} + l3_ha: ${_param:neutron_l3_ha} + dvr: ${_param:neutron_control_dvr} + qos: ${_param:neutron_enable_qos} + vlan_aware_vms: ${_param:neutron_enable_vlan_aware_vms} + firewall_driver: ${_param:neutron_firewall_driver} + bgp_vpn: + enabled: ${_param:neutron_enable_bgp_vpn} + driver: ${_param:neutron_bgp_vpn_driver} + backend: + engine: ml2 + router: 'vpp-router' + tenant_network_types: "${_param:neutron_tenant_network_types}" + external_mtu: ${_param:neutron_external_mtu} + mechanism: + vpp: + driver: vpp + etcd_port: ${_param:node_port} + etcd_host: ${_param:node_address} + l3_hosts: ${_param:openstack_gateway_node01_hostname} + physnets: + physnet1: + vpp_interface: ${_param:external_vpp_tap} + physnet2: + # NOTE: Not a meaningful interface name, just avoid a filter-out + vpp_interface: 'dummy' + vlan_range: '${_param:opnfv_net_tenant_vlan}' + compute: + region: ${_param:openstack_region} + database: + host: ${_param:openstack_database_address} + identity: + region: ${_param:openstack_region} + protocol: ${_param:internal_protocol} + message_queue: + members: + - host: ${_param:single_address} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml new file mode 100644 index 000000000..3fbec1bcd --- /dev/null +++ b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml @@ -0,0 +1,64 @@ +############################################################################## +# Copyright (c) 2018 Mirantis Inc., Enea AB and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +classes: + - cluster.mcp-common-noha.openstack_gateway + - service.neutron.gateway.single + - cluster.mcp-fdio-noha +parameters: + _param: + compute_hugepages_size: 1G + compute_hugepages_mount: /mnt/hugepages_1G + neutron: + gateway: + agents: + l3: + interface_driver: neutron.agent.linux.interface.BridgeInterfaceDriver + dhcp: + interface_driver: neutron.agent.linux.interface.BridgeInterfaceDriver + backend: + router: 'vpp-router' + tenant_network_types: "${_param:neutron_tenant_network_types}" + ~mechanism: + vpp: + driver: vpp + etcd_port: ${_param:node_port} + etcd_host: ${_param:node_address} + l3_hosts: ${_param:openstack_gateway_node01_hostname} + physnets: + physnet1: + vpp_interface: ${_param:external_vpp_tap} + physnet2: + vpp_interface: ${_param:dpdk0_vpp} + linux: + system: + kernel: + hugepages: + large: + default: true + size: ${_param:compute_hugepages_size} + count: ${_param:compute_hugepages_count} + mount_point: ${_param:compute_hugepages_mount} + isolcpu: 1 # NOTE: Hardcoded for now + boot_options: + - spectre_v2=off + - nopti + - intel_iommu=on + - iommu=pt + - nohz_full=${linux:system:kernel:isolcpu} + - rcu_nocbs=${linux:system:kernel:isolcpu} + - iommu.passthrough=1 + network: + interface: + dpdk0: + name: ${_param:dpdk0_name} + pci: ${_param:dpdk0_pci} + driver: ${_param:dpdk0_driver} + enabled: true + type: dpdk_vpp_port + mtu: ${_param:interface_mtu} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml.j2 b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml.j2 deleted file mode 100644 index 1b2ecb04b..000000000 --- a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/gateway.yml.j2 +++ /dev/null @@ -1,28 +0,0 @@ -############################################################################## -# Copyright (c) 2018 Mirantis Inc., Enea AB and others. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## -{%- import 'net_map.j2' as nm with context %} -{%- import 'net_macros.j2' as ma with context %} -{%- set vlan_private_start = (nm.vlan_private | string).rsplit('-')[0] %} ---- -classes: - - cluster.mcp-common-noha.openstack_gateway - - service.neutron.gateway.single - - cluster.mcp-fdio-noha -parameters: - linux: - network: - interface: - br-mesh: - enabled: true - type: bridge - mtu: ${_param:interface_mtu} - proto: static - address: ${_param:tenant_address} - netmask: ${_param:opnfv_net_private_mask} - use_interfaces: - - {{ ma.interface_str(nm.ctl01.nic_private, vlan_private_start) }} diff --git a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/init.yml b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/init.yml index 79e231825..1e1388485 100644 --- a/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/init.yml +++ b/mcp/reclass/classes/cluster/mcp-fdio-noha/openstack/init.yml @@ -8,6 +8,13 @@ --- classes: - cluster.mcp-common-noha.openstack_init + - cluster.all-mcp-arch-common.fdio_repo parameters: _param: - neutron_tenant_network_types: "flat,vxlan" + openstack_gateway_node01_hostname: 'gtw01' + neutron_tenant_network_types: "vlan" + etcd_initial_token: ${_param:opnfv_main_password} + node_address: ${_param:cluster_node01_address} + node_hostname: ${_param:cluster_node01_hostname} + node_port: 4001 + external_vpp_tap: 'tap0' diff --git a/mcp/salt-formulas/salt-formula-neutron b/mcp/salt-formulas/salt-formula-neutron -Subproject e58671b2069e8ceae87d55052b770d35c688cda +Subproject 365f43c2fab5546d56fd0a68fa8105500e6a7fa |