From 9fafe08b65bb0b6832441ef3ab726b4d28e3601d Mon Sep 17 00:00:00 2001 From: Matt Peters Date: Thu, 7 Jun 2018 17:47:42 -0500 Subject: [PATCH] Open vSwitch integration with host and configuration framework Integrates the latest Open vSwitch with DPDK into the host management and configuration framework and configures the default system vswitch type to be ovs-dpdk. Change-Id: If7ef2975e4b90ce84d170051f332f778a867a873 Signed-off-by: Matt Peters --- .../configutilities/common/validator.py | 6 +- .../controllerconfig/configassistant.py | 85 +----- ...iS_region_config.share.keystoneonly.result | 2 +- .../files/TiS_region_config.shareall.result | 2 +- .../tests/files/cgcs_config.ceph | 13 +- .../tests/files/cgcs_config.default | 13 +- .../tests/files/cgcs_config.ipv6 | 13 +- .../tests/files/cgcs_config.region | 13 +- .../tests/files/region_config.lag.vlan.result | 2 +- .../tests/files/region_config.security.result | 2 +- .../tests/files/region_config.simple.result | 2 +- .../tests/test_region_config.py | 4 +- puppet-manifests/src/hieradata/compute.yaml | 13 + .../src/hieradata/controller.yaml | 10 +- puppet-manifests/src/hieradata/global.yaml | 4 +- .../modules/openstack/manifests/neutron.pp | 32 +- .../src/modules/openstack/manifests/nova.pp | 9 +- .../src/modules/platform/manifests/config.pp | 10 +- .../src/modules/platform/manifests/ntp.pp | 6 +- .../src/modules/platform/manifests/vswitch.pp | 149 ++++++++-- .../platform/templates/ovs.add-bridge.erb | 2 + .../platform/templates/ovs.add-port.erb | 16 + .../modules/platform/templates/ovs.clean.erb | 7 + .../platform/templates/ovsdb.clean.erb | 7 + .../cgts-client/cgtsclient/v1/imemory.py | 5 +- .../cgtsclient/v1/imemory_shell.py | 50 ++-- .../cgtsclient/v1/isystem_shell.py | 14 +- sysinv/sysinv/centos/sysinv.spec | 3 +- sysinv/sysinv/sysinv/sysinv/agent/node.py | 65 ++-- sysinv/sysinv/sysinv/sysinv/agent/pci.py | 20 +- .../api/controllers/v1/ethernet_port.py | 2 +- .../sysinv/sysinv/api/controllers/v1/host.py | 12 +- .../sysinv/api/controllers/v1/interface.py | 42 +-- .../sysinv/api/controllers/v1/memory.py | 30 +- .../sysinv/sysinv/api/controllers/v1/port.py | 2 +- .../sysinv/api/controllers/v1/system.py | 25 +- .../sysinv/sysinv/api/controllers/v1/utils.py | 12 +- sysinv/sysinv/sysinv/sysinv/cmd/query_pci_id | 61 ++++ .../sysinv/sysinv/sysinv/common/constants.py | 2 +- .../sysinv/sysinv/sysinv/conductor/manager.py | 31 +- .../sysinv/sysinv/sysinv/conductor/rpcapi.py | 8 + .../versions/068_memory_column_rename.py | 29 ++ .../sysinv/sysinv/db/sqlalchemy/models.py | 8 +- sysinv/sysinv/sysinv/sysinv/objects/memory.py | 8 +- .../sysinv/sysinv/sysinv/puppet/__init__.py | 19 ++ sysinv/sysinv/sysinv/sysinv/puppet/base.py | 21 ++ .../sysinv/sysinv/sysinv/puppet/interface.py | 208 ++----------- sysinv/sysinv/sysinv/sysinv/puppet/neutron.py | 26 +- sysinv/sysinv/sysinv/sysinv/puppet/nfv.py | 4 - sysinv/sysinv/sysinv/sysinv/puppet/ovs.py | 277 ++++++++++++++++++ .../sysinv/sysinv/sysinv/puppet/platform.py | 13 +- sysinv/sysinv/sysinv/sysinv/puppet/puppet.py | 12 +- .../sysinv/sysinv/tests/api/test_interface.py | 3 +- sysinv/sysinv/sysinv/sysinv/tests/db/utils.py | 11 +- .../sysinv/tests/puppet/test_interface.py | 197 +------------ sysinv/sysinv/sysinv/tox.ini | 1 - 56 files changed, 873 insertions(+), 770 deletions(-) create mode 100644 puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb create mode 100644 puppet-manifests/src/modules/platform/templates/ovs.add-port.erb create mode 100644 puppet-manifests/src/modules/platform/templates/ovs.clean.erb create mode 100644 puppet-manifests/src/modules/platform/templates/ovsdb.clean.erb create mode 100755 sysinv/sysinv/sysinv/sysinv/cmd/query_pci_id create mode 100644 sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/068_memory_column_rename.py create mode 100644 sysinv/sysinv/sysinv/sysinv/puppet/ovs.py diff --git a/configutilities/configutilities/configutilities/common/validator.py b/configutilities/configutilities/configutilities/common/validator.py index a493c12594..1ad174359b 100755 --- a/configutilities/configutilities/configutilities/common/validator.py +++ b/configutilities/configutilities/configutilities/common/validator.py @@ -710,7 +710,7 @@ class ConfigValidator(object): self.vswitch_type = self.conf.get('NETWORK', 'VSWITCH_TYPE').upper() else: - self.vswitch_type = 'AVS' + self.vswitch_type = 'OVS-DPDK' if self.vswitch_type == 'NUAGE_VRS': metadata_proxy_shared_secret = self.conf.get( @@ -755,10 +755,10 @@ class ConfigValidator(object): raise ConfigFail( "The Region Names must be unique.") # validate VSWITCH_TYPE configuration - if self.vswitch_type == 'AVS': + if self.vswitch_type == 'OVS-DPDK': if self.conf.has_option('SHARED_SERVICES', 'NEUTRON_SERVICE_NAME'): raise ConfigFail( - "When VSWITCH_TYPE is AVS, NEUTRON service must " + "When VSWITCH_TYPE is OVS-DPDK, NEUTRON service must " "only be configured in REGION_2_SERVICES.") neutron_group = 'REGION_2_SERVICES' neutron_region_name = region_2_name diff --git a/controllerconfig/controllerconfig/controllerconfig/configassistant.py b/controllerconfig/controllerconfig/controllerconfig/configassistant.py index 1c1d47eb86..839581bbbb 100644 --- a/controllerconfig/controllerconfig/controllerconfig/configassistant.py +++ b/controllerconfig/controllerconfig/controllerconfig/configassistant.py @@ -443,23 +443,7 @@ class ConfigAssistant(): # HTTPS self.enable_https = False # Network config - self.vswitch_type = "avs" - self.neutron_l2_plugin = "ml2" - self.neutron_l2_agent = "vswitch" - self.neutron_l3_ext_bridge = 'provider' - self.neutron_mechanism_drivers = "vswitch,sriovnicswitch,l2population" - self.neutron_sriov_agent_required = "y" - self.neutron_type_drivers = "managed_flat,managed_vlan,managed_vxlan" - self.neutron_network_types = "vlan,vxlan" - self.neutron_host_driver = \ - "neutron.plugins.wrs.drivers.host.DefaultHostDriver" - self.neutron_fm_driver = \ - "neutron.plugins.wrs.drivers.fm.DefaultFmDriver" - self.neutron_network_scheduler = \ - "neutron.scheduler.dhcp_host_agent_scheduler.HostBasedScheduler" - self.neutron_router_scheduler = \ - "neutron.scheduler.l3_host_agent_scheduler.HostBasedScheduler" - self.metadata_proxy_shared_secret = "" + self.vswitch_type = "ovs-dpdk" # Authentication config self.admin_username = "admin" @@ -2622,48 +2606,6 @@ class ConfigAssistant(): # If any of the network options are missing, use defaults. if config.has_option('cNETWORK', 'VSWITCH_TYPE'): self.vswitch_type = config.get('cNETWORK', 'VSWITCH_TYPE') - if config.has_option('cNETWORK', 'NEUTRON_L2_PLUGIN'): - self.neutron_l2_plugin = config.get( - 'cNETWORK', 'NEUTRON_L2_PLUGIN') - if config.has_option('cNETWORK', 'NEUTRON_L2_AGENT'): - self.neutron_l2_agent = config.get( - 'cNETWORK', 'NEUTRON_L2_AGENT') - if config.has_option('cNETWORK', 'NEUTRON_L3_EXT_BRIDGE'): - self.neutron_l3_ext_bridge = config.get( - 'cNETWORK', 'NEUTRON_L3_EXT_BRIDGE') - if config.has_option('cNETWORK', - 'NEUTRON_ML2_MECHANISM_DRIVERS'): - self.neutron_mechanism_drivers = config.get( - 'cNETWORK', 'NEUTRON_ML2_MECHANISM_DRIVERS') - if config.has_option('cNETWORK', - 'NEUTRON_ML2_TYPE_DRIVERS'): - self.neutron_type_drivers = config.get( - 'cNETWORK', 'NEUTRON_ML2_TYPE_DRIVERS') - if config.has_option('cNETWORK', - 'NEUTRON_ML2_TENANT_NETWORK_TYPES'): - self.neutron_network_types = config.get( - 'cNETWORK', 'NEUTRON_ML2_TENANT_NETWORK_TYPES') - if config.has_option('cNETWORK', - 'NEUTRON_ML2_SRIOV_AGENT_REQUIRED'): - self.neutron_sriov_agent_required = config.get( - 'cNETWORK', 'NEUTRON_ML2_SRIOV_AGENT_REQUIRED') - if config.has_option('cNETWORK', 'NEUTRON_HOST_DRIVER'): - self.neutron_host_driver = config.get( - 'cNETWORK', 'NEUTRON_HOST_DRIVER') - if config.has_option('cNETWORK', 'NEUTRON_FM_DRIVER'): - self.neutron_fm_driver = config.get( - 'cNETWORK', 'NEUTRON_FM_DRIVER') - if config.has_option('cNETWORK', - 'NEUTRON_NETWORK_SCHEDULER'): - self.neutron_network_scheduler = config.get( - 'cNETWORK', 'NEUTRON_NETWORK_SCHEDULER') - if config.has_option('cNETWORK', - 'NEUTRON_ROUTER_SCHEDULER'): - self.neutron_router_scheduler = config.get( - 'cNETWORK', 'NEUTRON_ROUTER_SCHEDULER') - if self.vswitch_type == "nuage_vrs": - self.metadata_proxy_shared_secret = config.get( - 'cNETWORK', 'METADATA_PROXY_SHARED_SECRET') # Authentication configuration if config.has_section('cAUTHENTICATION'): @@ -3289,31 +3231,6 @@ class ConfigAssistant(): f.write("\n[cNETWORK]") f.write("\n# Data Network Configuration\n") f.write("VSWITCH_TYPE=%s\n" % self.vswitch_type) - f.write("NEUTRON_L2_PLUGIN=" + - str(self.neutron_l2_plugin) + "\n") - f.write("NEUTRON_L2_AGENT=" + - str(self.neutron_l2_agent) + "\n") - f.write("NEUTRON_L3_EXT_BRIDGE=" + - str(self.neutron_l3_ext_bridge) + "\n") - f.write("NEUTRON_ML2_MECHANISM_DRIVERS=" + - str(self.neutron_mechanism_drivers) + "\n") - f.write("NEUTRON_ML2_TYPE_DRIVERS=" + - str(self.neutron_type_drivers) + "\n") - f.write("NEUTRON_ML2_TENANT_NETWORK_TYPES=" + - str(self.neutron_network_types) + "\n") - f.write("NEUTRON_ML2_SRIOV_AGENT_REQUIRED=" + - str(self.neutron_sriov_agent_required) + "\n") - f.write("NEUTRON_HOST_DRIVER=" + - str(self.neutron_host_driver) + "\n") - f.write("NEUTRON_FM_DRIVER=" + - str(self.neutron_fm_driver) + "\n") - f.write("NEUTRON_NETWORK_SCHEDULER=" + - str(self.neutron_network_scheduler) + "\n") - f.write("NEUTRON_ROUTER_SCHEDULER=" + - str(self.neutron_router_scheduler) + "\n") - if self.vswitch_type == "nuage_vrs": - f.write("METADATA_PROXY_SHARED_SECRET=" + - str(self.metadata_proxy_shared_secret) + "\n") # Security configuration f.write("\n[cSECURITY]") diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.share.keystoneonly.result b/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.share.keystoneonly.result index 033929a142..9f3b6a48a0 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.share.keystoneonly.result +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.share.keystoneonly.result @@ -54,7 +54,7 @@ EXTERNAL_OAM_0_ADDRESS = 10.10.10.3 EXTERNAL_OAM_1_ADDRESS = 10.10.10.4 [cNETWORK] -VSWITCH_TYPE = avs +VSWITCH_TYPE = ovs-dpdk [cREGION] REGION_CONFIG = True diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.shareall.result b/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.shareall.result index c82af2cd23..8eb0ffe3b7 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.shareall.result +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/TiS_region_config.shareall.result @@ -54,7 +54,7 @@ EXTERNAL_OAM_0_ADDRESS = 10.10.10.3 EXTERNAL_OAM_1_ADDRESS = 10.10.10.4 [cNETWORK] -VSWITCH_TYPE = avs +VSWITCH_TYPE = ovs-dpdk [cREGION] REGION_CONFIG = True diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ceph b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ceph index fca11cf504..6481d1991c 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ceph +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ceph @@ -56,18 +56,7 @@ EXTERNAL_OAM_1_ADDRESS=10.10.10.4 [cNETWORK] # Data Network Configuration -VSWITCH_TYPE=avs -NEUTRON_L2_PLUGIN=ml2 -NEUTRON_L2_AGENT=vswitch -NEUTRON_L3_EXT_BRIDGE=provider -NEUTRON_ML2_MECHANISM_DRIVERS=vswitch,sriovnicswitch -NEUTRON_ML2_TYPE_DRIVERS=managed_flat,managed_vlan,managed_vxlan -NEUTRON_ML2_TENANT_NETWORK_TYPES=vlan,vxlan -NEUTRON_ML2_SRIOV_AGENT_REQUIRED=False -NEUTRON_HOST_DRIVER=neutron.plugins.wrs.drivers.host.DefaultHostDriver -NEUTRON_FM_DRIVER=neutron.plugins.wrs.drivers.fm.DefaultFmDriver -NEUTRON_NETWORK_SCHEDULER=neutron.scheduler.dhcp_host_agent_scheduler.HostChanceScheduler -NEUTRON_ROUTER_SCHEDULER=neutron.scheduler.l3_host_agent_scheduler.HostChanceScheduler +VSWITCH_TYPE=ovs-dpdk [cSECURITY] [cREGION] diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.default b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.default index d9f088f25c..87ff2f9cd4 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.default +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.default @@ -62,18 +62,7 @@ EXTERNAL_OAM_1_ADDRESS=10.10.10.4 [cNETWORK] # Data Network Configuration -VSWITCH_TYPE=avs -NEUTRON_L2_PLUGIN=ml2 -NEUTRON_L2_AGENT=vswitch -NEUTRON_L3_EXT_BRIDGE=provider -NEUTRON_ML2_MECHANISM_DRIVERS=vswitch,sriovnicswitch -NEUTRON_ML2_TYPE_DRIVERS=managed_flat,managed_vlan,managed_vxlan -NEUTRON_ML2_TENANT_NETWORK_TYPES=vlan,vxlan -NEUTRON_ML2_SRIOV_AGENT_REQUIRED=False -NEUTRON_HOST_DRIVER=neutron.plugins.wrs.drivers.host.DefaultHostDriver -NEUTRON_FM_DRIVER=neutron.plugins.wrs.drivers.fm.DefaultFmDriver -NEUTRON_NETWORK_SCHEDULER=neutron.scheduler.dhcp_host_agent_scheduler.HostChanceScheduler -NEUTRON_ROUTER_SCHEDULER=neutron.scheduler.l3_host_agent_scheduler.HostChanceScheduler +VSWITCH_TYPE=ovs-dpdk [cSECURITY] [cREGION] diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ipv6 b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ipv6 index 8884a14b1a..e52aaf774b 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ipv6 +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.ipv6 @@ -62,18 +62,7 @@ EXTERNAL_OAM_1_ADDRESS=abcd::4 [cNETWORK] # Data Network Configuration -VSWITCH_TYPE=avs -NEUTRON_L2_PLUGIN=ml2 -NEUTRON_L2_AGENT=vswitch -NEUTRON_L3_EXT_BRIDGE=provider -NEUTRON_ML2_MECHANISM_DRIVERS=vswitch,sriovnicswitch -NEUTRON_ML2_TYPE_DRIVERS=managed_flat,managed_vlan,managed_vxlan -NEUTRON_ML2_TENANT_NETWORK_TYPES=vlan,vxlan -NEUTRON_ML2_SRIOV_AGENT_REQUIRED=False -NEUTRON_HOST_DRIVER=neutron.plugins.wrs.drivers.host.DefaultHostDriver -NEUTRON_FM_DRIVER=neutron.plugins.wrs.drivers.fm.DefaultFmDriver -NEUTRON_NETWORK_SCHEDULER=neutron.scheduler.dhcp_host_agent_scheduler.HostChanceScheduler -NEUTRON_ROUTER_SCHEDULER=neutron.scheduler.l3_host_agent_scheduler.HostChanceScheduler +VSWITCH_TYPE=ovs-dpdk [cSECURITY] [cREGION] diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.region b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.region index 6634b40b70..9c57b722e5 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.region +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/cgcs_config.region @@ -64,18 +64,7 @@ EXTERNAL_OAM_1_ADDRESS=10.10.10.4 [cNETWORK] # Data Network Configuration -VSWITCH_TYPE=avs -NEUTRON_L2_PLUGIN=ml2 -NEUTRON_L2_AGENT=vswitch -NEUTRON_L3_EXT_BRIDGE=provider -NEUTRON_ML2_MECHANISM_DRIVERS=vswitch,sriovnicswitch -NEUTRON_ML2_TYPE_DRIVERS=managed_flat,managed_vlan,managed_vxlan -NEUTRON_ML2_TENANT_NETWORK_TYPES=vlan,vxlan -NEUTRON_ML2_SRIOV_AGENT_REQUIRED=False -NEUTRON_HOST_DRIVER=neutron.plugins.wrs.drivers.host.DefaultHostDriver -NEUTRON_FM_DRIVER=neutron.plugins.wrs.drivers.fm.DefaultFmDriver -NEUTRON_NETWORK_SCHEDULER=neutron.scheduler.dhcp_host_agent_scheduler.HostChanceScheduler -NEUTRON_ROUTER_SCHEDULER=neutron.scheduler.l3_host_agent_scheduler.HostChanceScheduler +VSWITCH_TYPE=ovs-dpdk [cSECURITY] [cREGION] diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.lag.vlan.result b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.lag.vlan.result index 828e924edf..1d8f7b406e 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.lag.vlan.result +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.lag.vlan.result @@ -59,7 +59,7 @@ EXTERNAL_OAM_0_ADDRESS = 10.10.10.3 EXTERNAL_OAM_1_ADDRESS = 10.10.10.4 [cNETWORK] -VSWITCH_TYPE = avs +VSWITCH_TYPE = ovs-dpdk [cREGION] REGION_CONFIG = True diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.security.result b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.security.result index 6d5d8be8df..c50f422d34 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.security.result +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.security.result @@ -37,7 +37,7 @@ EXTERNAL_OAM_0_ADDRESS = 10.10.10.3 EXTERNAL_OAM_1_ADDRESS = 10.10.10.4 [cNETWORK] -VSWITCH_TYPE = avs +VSWITCH_TYPE = ovs-dpdk [cREGION] REGION_CONFIG = True diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.simple.result b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.simple.result index 6d5d8be8df..c50f422d34 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.simple.result +++ b/controllerconfig/controllerconfig/controllerconfig/tests/files/region_config.simple.result @@ -37,7 +37,7 @@ EXTERNAL_OAM_0_ADDRESS = 10.10.10.3 EXTERNAL_OAM_1_ADDRESS = 10.10.10.4 [cNETWORK] -VSWITCH_TYPE = avs +VSWITCH_TYPE = ovs-dpdk [cREGION] REGION_CONFIG = True diff --git a/controllerconfig/controllerconfig/controllerconfig/tests/test_region_config.py b/controllerconfig/controllerconfig/controllerconfig/tests/test_region_config.py index 867abe9a3a..60ec25c102 100755 --- a/controllerconfig/controllerconfig/controllerconfig/tests/test_region_config.py +++ b/controllerconfig/controllerconfig/controllerconfig/tests/test_region_config.py @@ -831,9 +831,9 @@ def test_region_config_validation(): with pytest.raises(exceptions.ConfigFail): validate(region_config, REGION_CONFIG, None, False) - # Test detection of neutron in wrong region for AVS VSWITCH_TYPE + # Test detection of neutron in wrong region for VSWITCH_TYPE region_config = cr.parse_system_config(nuage_vrs_regionfile) - region_config.set('NETWORK', 'VSWITCH_TYPE', 'AVS') + region_config.set('NETWORK', 'VSWITCH_TYPE', 'ovs-dpdk') with pytest.raises(exceptions.ConfigFail): cr.create_cgcs_config_file(None, region_config, None, None, None, validate_only=True) diff --git a/puppet-manifests/src/hieradata/compute.yaml b/puppet-manifests/src/hieradata/compute.yaml index b704157dc5..5ff5863e21 100644 --- a/puppet-manifests/src/hieradata/compute.yaml +++ b/puppet-manifests/src/hieradata/compute.yaml @@ -1,6 +1,10 @@ # compute specific configuration data --- +# vswitch +vswitch::dpdk::memory_channels: 4 + + # neutron neutron::agents::dhcp::interface_driver: 'openvswitch' neutron::agents::dhcp::enable_isolated_metadata: true @@ -11,6 +15,11 @@ neutron::agents::l3::interface_driver: 'openvswitch' neutron::agents::l3::metadata_port: 80 neutron::agents::l3::agent_mode: 'dvr_snat' +neutron::agents::ml2::ovs::manage_vswitch: false +neutron::agents::ml2::ovs::datapath_type: 'netdev' +neutron::agents::ml2::ovs::vhostuser_socket_dir: '/var/run/openvswitch' +neutron::agents::ml2::ovs::firewall_driver: 'noop' + neutron::agents::ml2::sriov::manage_service: true neutron::agents::ml2::sriov::polling_interval: 5 @@ -46,6 +55,10 @@ nova::network::neutron::neutron_user_domain_name: 'Default' nova::network::neutron::neutron_project_domain_name: 'Default' nova::network::neutron::neutron_region_name: RegionOne +nova::compute::neutron::libvirt_vif_driver: 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver' + +openstack::nova::compute::compute_monitors: "cpu.virt_driver" + # ceilometer ceilometer::agent::polling::central_namespace: false diff --git a/puppet-manifests/src/hieradata/controller.yaml b/puppet-manifests/src/hieradata/controller.yaml index d9c8b5b634..bf7ad9c2a1 100644 --- a/puppet-manifests/src/hieradata/controller.yaml +++ b/puppet-manifests/src/hieradata/controller.yaml @@ -133,6 +133,10 @@ sysinv::api::keystone_project_domain: 'Default' sysinv::conductor::enabled: false +# nfvi +nfv::nfvi::infrastructure_rest_api_data_port_fault_handling_enabled: false + + # keystone keystone::service::enabled: false keystone::token_provider: 'fernet' @@ -235,12 +239,6 @@ nova_api_proxy::config::eventlet_pool_size: 256 nova::db::sync_api::cellv2_setup: true # neutron -neutron::core_plugin: 'neutron.plugins.ml2.plugin.Ml2Plugin' -neutron::service_plugins: - - 'router' -neutron::allow_overlapping_ips: true -neutron::vlan_transparent: true -neutron::pnet_audit_enabled: true neutron::server::enabled: false neutron::server::database_idle_timeout: 60 diff --git a/puppet-manifests/src/hieradata/global.yaml b/puppet-manifests/src/hieradata/global.yaml index 403720fd19..9bdd4fee37 100644 --- a/puppet-manifests/src/hieradata/global.yaml +++ b/puppet-manifests/src/hieradata/global.yaml @@ -35,12 +35,12 @@ neutron::logging::log_dir: false neutron::logging::verbose: false neutron::logging::debug: false -neutron::core_plugin: 'ml2' +neutron::core_plugin: 'neutron.plugins.ml2.plugin.Ml2Plugin' neutron::service_plugins: - 'router' neutron::allow_overlapping_ips: true neutron::vlan_transparent: true -neutron::pnet_audit_enabled: true +neutron::pnet_audit_enabled: false neutron::verbose: false neutron::root_helper: 'sudo' diff --git a/puppet-manifests/src/modules/openstack/manifests/neutron.pp b/puppet-manifests/src/modules/openstack/manifests/neutron.pp index 0e94dffdf8..db8f698989 100644 --- a/puppet-manifests/src/modules/openstack/manifests/neutron.pp +++ b/puppet-manifests/src/modules/openstack/manifests/neutron.pp @@ -4,9 +4,9 @@ class openstack::neutron::params ( $region_name = undef, $service_name = 'openstack-neutron', $bgp_router_id = undef, - $l3_agent_enabled = true, $service_create = false, - $configure_endpoint = true + $configure_endpoint = true, + $tunnel_csum = undef, ) { } class openstack::neutron @@ -20,7 +20,6 @@ class openstack::neutron class { '::neutron': rabbit_use_ssl => $::platform::amqp::params::ssl_enabled, default_transport_url => $::platform::amqp::params::transport_url, - pnet_audit_enabled => $::platform::params::sdn_enabled ? { true => false, default => true }, } } @@ -139,8 +138,8 @@ class openstack::neutron::bgp class openstack::neutron::sfc ( - $sfc_drivers = undef, - $flowclassifier_drivers = undef, + $sfc_drivers = 'ovs', + $flowclassifier_drivers = 'ovs', $sfc_quota_flow_classifier = undef, $sfc_quota_port_chain = undef, $sfc_quota_port_pair_group = undef, @@ -197,9 +196,6 @@ class openstack::neutron::agents if str2bool($::disable_compute_services) { $pmon_ensure = absent - class {'::neutron::agents::vswitch': - service_ensure => stopped, - } class {'::neutron::agents::l3': enabled => false } @@ -212,6 +208,9 @@ class openstack::neutron::agents class {'::neutron::agents::ml2::sriov': enabled => false } + class {'::neutron::agents::ml2::ovs': + enabled => false + } } else { $pmon_ensure = link @@ -219,12 +218,21 @@ class openstack::neutron::agents metadata_workers => $::platform::params::eng_workers_by_4 } - class { '::neutron::agents::l3': - enabled => $l3_agent_enabled, - } - include ::neutron::agents::dhcp + include ::neutron::agents::l3 include ::neutron::agents::ml2::sriov + include ::neutron::agents::ml2::ovs + } + + if $::platform::params::vswitch_type =~ '^ovs' { + # Ensure bridges and addresses are configured before agent is started + Platform::Vswitch::Ovs::Bridge<||> ~> Service['neutron-ovs-agent-service'] + Platform::Vswitch::Ovs::Address<||> ~> Service['neutron-ovs-agent-service'] + + # Enable/disable tunnel checksum + neutron_agent_ovs { + 'agent/tunnel_csum': value => $tunnel_csum; + } } file { "/etc/pmon.d/neutron-dhcp-agent.conf": diff --git a/puppet-manifests/src/modules/openstack/manifests/nova.pp b/puppet-manifests/src/modules/openstack/manifests/nova.pp index 9e5d4c3ba4..925c527528 100644 --- a/puppet-manifests/src/modules/openstack/manifests/nova.pp +++ b/puppet-manifests/src/modules/openstack/manifests/nova.pp @@ -136,6 +136,7 @@ class openstack::nova::compute ( $migration_key_type, $pci_pt_whitelist = [], $pci_sriov_whitelist = undef, + $compute_monitors, $iscsi_initiator_name = undef, ) inherits ::openstack::nova::params { include ::nova::pci @@ -145,6 +146,7 @@ class openstack::nova::compute ( include ::platform::network::infra::params include ::nova::keystone::auth include ::nova::keystone::authtoken + include ::nova::compute::neutron include ::openstack::nova::sshd @@ -268,8 +270,6 @@ class openstack::nova::compute ( $libvirt_images_type = "default" } - $compute_monitors = "cpu.virt_driver" - class { '::nova::compute::libvirt': libvirt_virt_type => $libvirt_virt_type, vncserver_listen => $libvirt_vnc_bind_host, @@ -336,11 +336,6 @@ class openstack::nova::compute ( match => '^cgroup_controllers = .*', } - class { '::nova::compute::neutron': - libvirt_vif_driver => 'nova.virt.libvirt.vif.LibvirtGenericVIFDriver', - libvirt_qemu_dpdk_options => 'type=secondary,prefix=vs,channels=4,cpu=0', - } - # The pci_passthrough option in the nova::compute class is not sufficient. # In particular, it sets the pci_passthrough_whitelist in nova.conf to an # empty string if the list is empty, causing the nova-compute process to fail. diff --git a/puppet-manifests/src/modules/platform/manifests/config.pp b/puppet-manifests/src/modules/platform/manifests/config.pp index a813b0df1f..52dacd6154 100644 --- a/puppet-manifests/src/modules/platform/manifests/config.pp +++ b/puppet-manifests/src/modules/platform/manifests/config.pp @@ -72,10 +72,12 @@ class platform::config::file { } } - file_line { "${platform_conf} vswitch_type": - path => $platform_conf, - line => "vswitch_type=${::platform::params::vswitch_type}", - match => '^vswitch_type=', + if $::platform::params::vswitch_type { + file_line { "${platform_conf} vswitch_type": + path => $platform_conf, + line => "vswitch_type=${::platform::params::vswitch_type}", + match => '^vswitch_type=', + } } if $::platform::params::system_type { diff --git a/puppet-manifests/src/modules/platform/manifests/ntp.pp b/puppet-manifests/src/modules/platform/manifests/ntp.pp index 26db63485a..90e1dbbfd3 100644 --- a/puppet-manifests/src/modules/platform/manifests/ntp.pp +++ b/puppet-manifests/src/modules/platform/manifests/ntp.pp @@ -29,8 +29,8 @@ class platform::ntp ( onlyif => "grep -q '^server' /etc/ntp.conf", } - exec { 'systemd-daemon-reload': - command => '/usr/bin/systemctl daemon-reload', + exec { 'ntpdate-systemd-daemon-reload': + command => '/usr/bin/systemctl daemon-reload', } exec { 'stop-ntpdate': @@ -57,7 +57,7 @@ class platform::ntp ( File['ntpdate_tis_override'] -> Exec['enable-ntpdate'] -> Exec['enable-ntpd'] -> - Exec['systemd-daemon-reload'] -> + Exec['ntpdate-systemd-daemon-reload'] -> Exec['stop-ntpdate'] -> Exec['stop-ntpd'] -> Exec['start-ntpdate'] -> diff --git a/puppet-manifests/src/modules/platform/manifests/vswitch.pp b/puppet-manifests/src/modules/platform/manifests/vswitch.pp index 79e502f316..48ab13dbac 100644 --- a/puppet-manifests/src/modules/platform/manifests/vswitch.pp +++ b/puppet-manifests/src/modules/platform/manifests/vswitch.pp @@ -1,35 +1,130 @@ -class platform::vswitch { +class platform::vswitch::params( + $iommu_enabled = true, + $hugepage_dir = '/mnt/huge-1048576kB', + $driver_type = 'vfio-pci', +) { } + + +class platform::vswitch + inherits ::platform::vswitch::params { Class[$name] -> Class['::platform::network'] + Mount[$hugepage_dir] -> Class[$name] - include ::platform::vswitch::ovsdb + $enable_unsafe_noiommu_mode = bool2num(!$iommu_enabled) + + exec {'vfio-iommu-mode': + command => "echo ${enable_unsafe_noiommu_mode} > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode", + require => Kmod::Load[$driver_type], + } + + include ::platform::vswitch::ovs } -class platform::vswitch::ovsdb { - include ::platform::params - - if $::platform::params::sdn_enabled { - $pmon_ensure = 'link' - $service_ensure = 'running' - } else { - $pmon_ensure = 'absent' - $service_ensure = 'stopped' +define platform::vswitch::ovs::device( + $pci_addr, + $driver_type, +) { + exec { "ovs-bind-device: $title": + path => ["/usr/bin", "/usr/sbin", "/usr/share/openvswitch/scripts"], + command => "dpdk-devbind.py --bind=${driver_type} ${pci_addr}" } - - # ensure pmon soft link - file { "/etc/pmon.d/ovsdb-server.conf": - ensure => $pmon_ensure, - target => "/etc/openvswitch/ovsdb-server.pmon.conf", - owner => 'root', - group => 'root', - mode => '0755', - } - - # service management (start ovsdb-server) - service { "openvswitch": - ensure => $service_ensure, - enable => $::platform::params::sdn_enabled, - } - +} + + +define platform::vswitch::ovs::bridge( + $datapath_type = 'netdev', +) { + exec { "ovs-add-br: ${title}": + command => template("platform/ovs.add-bridge.erb") + } -> + exec { "ovs-link-up: ${title}": + command => "ip link set ${name} up", + } +} + + +define platform::vswitch::ovs::port( + $type = 'port', + $bridge, + $attributes = [], + $interfaces, +) { + exec { "ovs-add-port: ${title}": + command => template("platform/ovs.add-port.erb"), + logoutput => true + } +} + + +define platform::vswitch::ovs::address( + $ifname, + $address, + $prefixlen, +) { + exec { "ovs-add-address: ${title}": + command => "ip addr replace ${address}/${prefixlen} dev ${ifname}", + } +} + + +class platform::vswitch::ovs( + $devices = {}, + $bridges = {}, + $ports = {}, + $addresses = {}, +) inherits ::platform::vswitch::params { + + if $::platform::params::vswitch_type == 'ovs' { + include ::vswitch::ovs + } elsif $::platform::params::vswitch_type == 'ovs-dpdk' { + include ::vswitch::dpdk + + Exec['vfio-iommu-mode'] -> + Platform::Vswitch::Ovs::Device<||> -> + Platform::Vswitch::Ovs::Bridge<||> + + create_resources('platform::vswitch::ovs::device', $devices, { + driver_type => $driver_type, + before => Service['openvswitch'] + }) + + $dpdk_configs = { + 'other_config:dpdk-hugepage-dir' => { value => $hugepage_dir }, + } + + $dpdk_dependencies = { + wait => false, + require => Service['openvswitch'], + notify => Vs_config['other_config:dpdk-init'], + } + + create_resources ('vs_config', $dpdk_configs, $dpdk_dependencies) + } + + if $::platform::params::vswitch_type =~ '^ovs' { + + # clean bridges and ports before applying current configuration + exec { "ovs-clean": + command => template("platform/ovs.clean.erb"), + provider => shell, + require => Service['openvswitch'] + } -> + + Platform::Vswitch::Ovs::Bridge<||> -> Platform::Vswitch::Ovs::Port<||> + Platform::Vswitch::Ovs::Bridge<||> -> Platform::Vswitch::Ovs::Address<||> + } + + create_resources('platform::vswitch::ovs::bridge', $bridges, { + require => Service['openvswitch'] + }) + + create_resources('platform::vswitch::ovs::port', $ports, { + require => Service['openvswitch'] + }) + + create_resources('platform::vswitch::ovs::address', $addresses, { + require => Service['openvswitch'] + }) } diff --git a/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb b/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb new file mode 100644 index 0000000000..59fdc07af0 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb @@ -0,0 +1,2 @@ +ovs-vsctl --timeout 10 -- --may-exist add-br <%= @name -%> + -- set bridge <%= @name -%> datapath_type=<%= @datapath_type -%> diff --git a/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb b/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb new file mode 100644 index 0000000000..80d66de163 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb @@ -0,0 +1,16 @@ +ovs-vsctl --timeout 10 -- --may-exist add-<%= @type -%> <%= @bridge -%> <%= @name -%> +<%- if @type == 'bond' -%> +<%- @interfaces.each do |interface| -%> + <%= interface['name'] -%> +<%- end -%> +<%- end -%> +<%- @attributes.each do |attribute| -%> + <%= attribute -%> +<%- end -%> +<%- @interfaces.each do |interface| -%> + -- set Interface <%= interface['name'] -%> + type=<%= interface['type'] -%> + <%- interface['attributes'].each do |attribute| -%> + <%= attribute -%> + <%- end -%> +<%- end -%> diff --git a/puppet-manifests/src/modules/platform/templates/ovs.clean.erb b/puppet-manifests/src/modules/platform/templates/ovs.clean.erb new file mode 100644 index 0000000000..5ec03ba2ad --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/ovs.clean.erb @@ -0,0 +1,7 @@ +# clean provider network ports and bridges +for bridge in $(ovs-vsctl --timeout 10 list-br|grep '^br-phy'); do + for port in $(ovs-vsctl --timeout 10 list-ports $bridge); do + ovs-vsctl --timeout 10 del-port $bridge $port + done + ovs-vsctl --timeout 10 del-br $bridge +done diff --git a/puppet-manifests/src/modules/platform/templates/ovsdb.clean.erb b/puppet-manifests/src/modules/platform/templates/ovsdb.clean.erb new file mode 100644 index 0000000000..600a21c605 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/ovsdb.clean.erb @@ -0,0 +1,7 @@ +# delete manager +ovs-vsctl -t ovsdb-server --no-wait del-manager + +# delete all bridges +for bridge in $(ovs-vsctl -t ovsdb-server --timeout 10 list-br); do + ovs-vsctl -t ovsdb-server --timeout 10 --no-wait del-br $bridge +done diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory.py index 7869a23a4c..80ada455ac 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory.py @@ -12,8 +12,8 @@ from cgtsclient import exc CREATION_ATTRIBUTES = ['ihost_uuid', 'memtotal_mib', 'memavail_mib', 'platform_reserved_mib', 'hugepages_configured', - 'avs_hugepages_size_mib', 'avs_hugepages_reqd', - 'avs_hugepages_nr', 'avs_hugepages_avail', + 'vswitch_hugepages_size_mib', 'vswitch_hugepages_reqd', + 'vswitch_hugepages_nr', 'vswitch_hugepages_avail', 'vm_hugepages_nr_2M_pending', 'vm_hugepages_nr_1G_pending', 'vm_hugepages_nr_2M', 'vm_hugepages_avail_2M', 'vm_hugepages_nr_1G', 'vm_hugepages_avail_1G', @@ -21,6 +21,7 @@ CREATION_ATTRIBUTES = ['ihost_uuid', 'memtotal_mib', 'memavail_mib', 'vm_hugepages_possible_2M', 'vm_hugepages_possible_1G', 'capabilities', 'numa_node', 'minimum_platform_reserved_mib'] + class imemory(base.Resource): def __repr__(self): return "" % self._info diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory_shell.py index 5907e9adc4..640f3fe563 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/imemory_shell.py @@ -12,17 +12,17 @@ from cgtsclient.common import utils from cgtsclient import exc -from collections import OrderedDict from cgtsclient.v1 import ihost as ihost_utils + def _print_imemory_show(imemory): fields = ['memtotal_mib', 'platform_reserved_mib', 'memavail_mib', 'hugepages_configured', - 'avs_hugepages_size_mib', - 'avs_hugepages_nr', - 'avs_hugepages_avail', + 'vswitch_hugepages_size_mib', + 'vswitch_hugepages_nr', + 'vswitch_hugepages_avail', 'vm_hugepages_nr_4K', 'vm_hugepages_nr_2M', 'vm_hugepages_nr_2M_pending', @@ -36,9 +36,9 @@ def _print_imemory_show(imemory): ' Platform (MiB)', ' Available (MiB)', 'Huge Pages Configured', - 'AVS Huge Pages: Size (MiB)', - ' Total', - ' Available', + 'vSwitch Huge Pages: Size (MiB)', + ' Total', + ' Available', 'VM Pages (4K): Total', 'VM Huge Pages (2M): Total', ' Total Pending', @@ -110,9 +110,9 @@ def do_host_memory_list(cc, args): 'platform_reserved_mib', 'memavail_mib', 'hugepages_configured', - 'avs_hugepages_size_mib', - 'avs_hugepages_nr', - 'avs_hugepages_avail', + 'vswitch_hugepages_size_mib', + 'vswitch_hugepages_nr', + 'vswitch_hugepages_avail', 'vm_hugepages_nr_4K', 'vm_hugepages_nr_2M', 'vm_hugepages_avail_2M', @@ -123,21 +123,21 @@ def do_host_memory_list(cc, args): 'vm_hugepages_use_1G'] field_labels = ['processor', - 'mem_total(MiB)', - 'mem_platform(MiB)', - 'mem_avail(MiB)', - 'hugepages(hp)_configured', - 'avs_hp_size(MiB)', - 'avs_hp_total', - 'avs_hp_avail', - 'vm_total_4K', - 'vm_hp_total_2M', - 'vm_hp_avail_2M', - 'vm_hp_pending_2M', - 'vm_hp_total_1G', - 'vm_hp_avail_1G', - 'vm_hp_pending_1G', - 'vm_hp_use_1G'] + 'mem_total(MiB)', + 'mem_platform(MiB)', + 'mem_avail(MiB)', + 'hugepages(hp)_configured', + 'vs_hp_size(MiB)', + 'vs_hp_total', + 'vs_hp_avail', + 'vm_total_4K', + 'vm_hp_total_2M', + 'vm_hp_avail_2M', + 'vm_hp_pending_2M', + 'vm_hp_total_1G', + 'vm_hp_avail_1G', + 'vm_hp_pending_1G', + 'vm_hp_use_1G'] utils.print_list(imemorys, fields, field_labels, sortby=1) diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/isystem_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/isystem_shell.py index f5aad3d083..c50c6b4aac 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/isystem_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/isystem_shell.py @@ -41,7 +41,12 @@ def _print_isystem_show(isystem): fields.append('distributed_cloud_role') setattr(isystem, 'distributed_cloud_role', isystem.distributed_cloud_role) - + + if isystem.capabilities.get('vswitch_type') is not None: + fields.append('vswitch_type') + setattr(isystem, 'vswitch_type', + isystem.capabilities.get('vswitch_type')) + data = dict(list([(f, getattr(isystem, f, '')) for f in fields])) utils.print_dict(data) @@ -78,7 +83,10 @@ def do_show(cc, args): metavar='', choices=['true', 'false'], help='The HTTPS enabled or disabled flag') - +@utils.arg('-v', '--vswitch_type', + metavar='', + choices=['ovs-dpdk'], + help='The vswitch type for the system') def do_modify(cc, args): """Modify system attributes.""" @@ -126,7 +134,7 @@ def do_modify(cc, args): print 'Please follow the admin guide to complete the reconfiguration.' field_list = ['name', 'system_mode', 'description', 'location', 'contact', - 'timezone', 'sdn_enabled','https_enabled'] + 'timezone', 'sdn_enabled','https_enabled', 'vswitch_type'] # use field list as filter user_fields = dict((k, v) for (k, v) in vars(args).items() diff --git a/sysinv/sysinv/centos/sysinv.spec b/sysinv/sysinv/centos/sysinv.spec index c19d740ade..c163c5321e 100644 --- a/sysinv/sysinv/centos/sysinv.spec +++ b/sysinv/sysinv/centos/sysinv.spec @@ -74,9 +74,8 @@ install -m 644 -p -D scripts/sysinv-conductor.service %{buildroot}%{_unitdir}/sy install -d -m 755 %{buildroot}%{local_bindir} install -p -D -m 755 sysinv/cmd/partition_info.sh %{buildroot}%{local_bindir}/partition_info.sh - -install -d -m 755 %{buildroot}%{local_bindir} install -p -D -m 755 sysinv/cmd/manage-partitions %{buildroot}%{local_bindir}/manage-partitions +install -p -D -m 755 sysinv/cmd/query_pci_id %{buildroot}%{local_bindir}/query_pci_id %clean echo "CLEAN CALLED" diff --git a/sysinv/sysinv/sysinv/sysinv/agent/node.py b/sysinv/sysinv/sysinv/sysinv/agent/node.py index 543665efbf..19561ba23f 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/node.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/node.py @@ -29,11 +29,10 @@ import tsconfig.tsconfig as tsc LOG = logging.getLogger(__name__) -# Defines per-socket AVS memory requirements (in MB) for both real and virtual -# deployments -# -AVS_REAL_MEMORY_MB = 1024 -AVS_VBOX_MEMORY_MB = 512 +# Defines per-socket vswitch memory requirements (in MB) for both real and +# virtual deployments +VSWITCH_REAL_MEMORY_MB = 1024 +VSWITCH_VIRTUAL_MEMORY_MB = 512 class CPU: @@ -286,32 +285,32 @@ class NodeOperator(object): return [name for name in listdir(dir) if os.path.isdir(join(dir, name))] - def _set_default_avs_hugesize(self): - ''' - Set the default memory size for avs hugepages when it must fallback to - 2MB pages because there are no 1GB pages. In a virtual environment we - set a smaller amount of memory because AVS is configured to use a - smaller mbuf pool. In non-virtual environments we use the same amount - of memory as we would if 1GB pages were available. - ''' + def _set_default_vswitch_hugesize(self): + """ + Set the default memory size for vswitch hugepages when it must fallback + to 2MB pages because there are no 1GB pages. In a virtual environment + we set a smaller amount of memory because vswitch is configured to use + a smaller mbuf pool. In non-virtual environments we use the same + amount of memory as we would if 1GB pages were available. + """ hugepage_size = 2 if utils.is_virtual(): - avs_hugepages_nr = AVS_VBOX_MEMORY_MB / hugepage_size + vswitch_hugepages_nr = VSWITCH_VIRTUAL_MEMORY_MB / hugepage_size else: - avs_hugepages_nr = AVS_REAL_MEMORY_MB / hugepage_size + vswitch_hugepages_nr = VSWITCH_REAL_MEMORY_MB / hugepage_size ## Create a new set of dict attributes - hp_attr = {'avs_hugepages_size_mib': hugepage_size, - 'avs_hugepages_nr': avs_hugepages_nr, - 'avs_hugepages_avail': 0} + hp_attr = {'vswitch_hugepages_size_mib': hugepage_size, + 'vswitch_hugepages_nr': vswitch_hugepages_nr, + 'vswitch_hugepages_avail': 0} return hp_attr def _inode_get_memory_hugepages(self): - '''Collect hugepage info, including avs, and vm. + """Collect hugepage info, including vswitch, and vm. Collect platform reserved if config. :param self :returns list of memory nodes and attributes - ''' + """ imemory = [] Ki = 1024 @@ -339,7 +338,7 @@ class NodeOperator(object): Total_HP_MiB = 0 # Total memory (MiB) currently configured in HPs Free_HP_MiB = 0 - # Check AVS and Libvirt memory + # Check vswitch and libvirt memory # Loop through configured hugepage sizes of this node and record # total number and number free hugepages = "/sys/devices/system/node/node%d/hugepages" % node @@ -352,7 +351,7 @@ class NodeOperator(object): sizesplit = subdir.split('-') # role via size; also from /etc/nova/compute_reserved.conf if sizesplit[1].startswith("1048576kB"): - hugepages_role = "avs" + hugepages_role = "vswitch" size = int(SZ_1G_Ki / Ki) else: hugepages_role = "vm" @@ -377,27 +376,27 @@ class NodeOperator(object): # Libvirt hugepages can now be 1G and 2M, can't only look # at 2M pages - if hugepages_role == "avs": - avs_hugepages_nr = AVS_REAL_MEMORY_MB / size + if hugepages_role == "vswitch": + vswitch_hugepages_nr = VSWITCH_REAL_MEMORY_MB / size hp_attr = { - 'avs_hugepages_size_mib': size, - 'avs_hugepages_nr': avs_hugepages_nr, - 'avs_hugepages_avail': 0, + 'vswitch_hugepages_size_mib': size, + 'vswitch_hugepages_nr': vswitch_hugepages_nr, + 'vswitch_hugepages_avail': 0, 'vm_hugepages_nr_1G': - (nr_hugepages - avs_hugepages_nr), + (nr_hugepages - vswitch_hugepages_nr), 'vm_hugepages_avail_1G': free_hugepages, 'vm_hugepages_use_1G': 'True' } else: if len(subdirs) == 1: - hp_attr = self._set_default_avs_hugesize() + hp_attr = self._set_default_vswitch_hugesize() hp_attr.update({'vm_hugepages_use_1G': 'False'}) - avs_hugepages_nr = hp_attr.get('avs_hugepages_nr', 0) + vswitch_hugepages_nr = hp_attr.get('vswitch_hugepages_nr', 0) hp_attr.update({ 'vm_hugepages_avail_2M': free_hugepages, 'vm_hugepages_nr_2M': - (nr_hugepages - avs_hugepages_nr) + (nr_hugepages - vswitch_hugepages_nr) }) attr.update(hp_attr) @@ -503,8 +502,8 @@ class NodeOperator(object): Eng_KiB = node_total_kib - base_mem_MiB * Ki - vswitch_mem_kib = (attr.get('avs_hugepages_size_mib', 0) * - attr.get('avs_hugepages_nr', 0) * Ki) + vswitch_mem_kib = (attr.get('vswitch_hugepages_size_mib', 0) * + attr.get('vswitch_hugepages_nr', 0) * Ki) VM_KiB = (Eng_KiB - vswitch_mem_kib) diff --git a/sysinv/sysinv/sysinv/sysinv/agent/pci.py b/sysinv/sysinv/sysinv/sysinv/agent/pci.py index 8a5b9238f5..1f3b48741a 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/pci.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/pci.py @@ -463,19 +463,13 @@ class PCIOperator(object): try: with open(os.devnull, "w") as fnull: - """ - query_pci_id is from dpdk (avs/cgcs-dpdk/files/query_pci_id). - DPDK is removed as part of AVS. - Need add it back later. Then enable this code again. - """ - LOG.error("******ERROR: unable to determine DPDK support or not due to lack DPDK package.******") - # subprocess.check_call(["query_pci_id", "-v " + str(vendor), - # "-d " + str(device)], - # stdout=fnull, stderr=fnull) - # dpdksupport = True - # LOG.debug("DPDK does support NIC " - # "(vendor: %s device: %s)", - # vendor, device) + subprocess.check_call(["query_pci_id", "-v " + str(vendor), + "-d " + str(device)], + stdout=fnull, stderr=fnull) + dpdksupport = True + LOG.debug("DPDK does support NIC " + "(vendor: %s device: %s)", + vendor, device) except subprocess.CalledProcessError as e: dpdksupport = False if e.returncode == '1': diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ethernet_port.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ethernet_port.py index 5745ddec18..8ac1c86aab 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ethernet_port.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ethernet_port.py @@ -141,7 +141,7 @@ class EthernetPort(base.APIBase): "Represent whether the port is a boot port" dpdksupport = bool - "Represent whether or not the port supported AVS acceleration" + "Represent whether or not the port supports DPDK acceleration" host_uuid = types.uuid "Represent the UUID of the host the port belongs to" diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index 3974962354..cc7907b7f9 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -3143,10 +3143,6 @@ class HostController(rest.RestController): Perform semantic checks against data interfaces to ensure validity of the node configuration prior to unlocking it. """ - vswitch_type = utils.get_vswitch_type() - if vswitch_type != constants.VSWITCH_TYPE_AVS: - return - ihost_iinterfaces = ( pecan.request.dbapi.iinterface_get_by_ihost(ihost['uuid'])) data_interface_configured = False @@ -3172,10 +3168,6 @@ class HostController(rest.RestController): controller will be confused on won't know how to map the VXLAN VTEP endpoints. """ - vswitch_type = utils.get_vswitch_type() - if vswitch_type != constants.VSWITCH_TYPE_AVS: - return - sdn_enabled = utils.get_sdn_enabled() if not sdn_enabled: return @@ -3431,7 +3423,7 @@ class HostController(rest.RestController): memtotal = m.node_memtotal_mib allocated = m.platform_reserved_mib if m.hugepages_configured: - allocated += m.avs_hugepages_nr * m.avs_hugepages_size_mib + allocated += m.vswitch_hugepages_nr * m.vswitch_hugepages_size_mib if m.vm_hugepages_nr_2M_pending is not None: allocated += constants.MIB_2M * m.vm_hugepages_nr_2M_pending pending_2M_memory = True @@ -3499,7 +3491,7 @@ class HostController(rest.RestController): vm_hugepages_4K = \ (m.node_memtotal_mib - m.platform_reserved_mib) vm_hugepages_4K -= \ - (m.avs_hugepages_nr * m.avs_hugepages_size_mib) + (m.vswitch_hugepages_nr * m.vswitch_hugepages_size_mib) vm_hugepages_4K -= \ (constants.MIB_2M * vm_hugepages_nr_2M) vm_hugepages_4K -= \ diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py index dee6c04e23..accad777e9 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py @@ -878,8 +878,6 @@ def _valid_network_types(): vswitch_type = utils.get_vswitch_type() system_mode = utils.get_system_mode() - if vswitch_type != constants.VSWITCH_TYPE_AVS: - valid_types -= set([constants.NETWORK_TYPE_DATA]) if vswitch_type != constants.VSWITCH_TYPE_NUAGE_VRS: valid_types -= set([constants.NETWORK_TYPE_DATA_VRS]) if system_mode == constants.SYSTEM_MODE_SIMPLEX: @@ -1200,7 +1198,7 @@ def _check_interface_data(op, interface, ihost, existing_interface): # check mode/pool combinations and transitions for validity _check_address_mode(op, interface, ihost, existing_interface) - # Make sure txhashpolicy for data is layer2 ... all that AVS supports + # Make sure txhashpolicy for data is layer2 aemode = interface['aemode'] txhashpolicy = interface['txhashpolicy'] @@ -1821,15 +1819,16 @@ def _neutron_host_extension_supported(): necessary or not. If it is not supported then this is an indication that we are running against a vanilla openstack installation. """ - return bool(utils.get_vswitch_type() == constants.VSWITCH_TYPE_AVS) - ## TODO: Rather than key off of the vswitch type this should be looking at - ## the neutron extension list, but because our config file is not setup - ## properly to have a different region on a per service basis we cannot. - ## The code should like something like this: - ## - ## extensions = pecan.request.rpcapi.neutron_extension_list( - ## pecan.request.context) - ## return bool(constants.NEUTRON_HOST_ALIAS in extensions) + return True + # TODO: This should be looking at the neutron extension list, but because + # our config file is not setup properly to have a different region on a per + # service basis we cannot. + # + # The code should like something like this: + # + # extensions = pecan.request.rpcapi.neutron_extension_list( + # pecan.request.context) + # return bool(constants.NEUTRON_HOST_ALIAS in extensions) def _neutron_providernet_extension_supported(): @@ -1839,15 +1838,16 @@ def _neutron_providernet_extension_supported(): necessary or not. If it is not supported then this is an indication that we are running against a vanilla openstack installation. """ - return bool(utils.get_vswitch_type() == constants.VSWITCH_TYPE_AVS) - ## TODO: Rather than key off of the vswitch type this should be looking at - ## the neutron extension list, but because our config file is not setup - ## properly to have a different region on a per service basis we cannot. - ## The code should like something like this: - ## - ## extensions = pecan.request.rpcapi.neutron_extension_list( - ## pecan.request.context) - ## return bool(constants.NEUTRON_WRS_PROVIDER_ALIAS in extensions) + return True + # TODO: This should be looking at the neutron extension list, but because + # our config file is not setup properly to have a different region on a per + # service basis we cannot. + # + # The code should like something like this: + # + # extensions = pecan.request.rpcapi.neutron_extension_list( + # pecan.request.context) + # return bool(constants.NEUTRON_WRS_PROVIDER_ALIAS in extensions) def _neutron_providernet_list(): diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/memory.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/memory.py index 721a37ab8f..585d063758 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/memory.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/memory.py @@ -94,17 +94,17 @@ class Memory(base.APIBase): hugepages_configured = wtypes.text "Represent whether huge pages are configured" - avs_hugepages_size_mib = int - "Represent the imemory avs huge pages size in MiB" + vswitch_hugepages_size_mib = int + "Represent the imemory vswitch huge pages size in MiB" - avs_hugepages_reqd = int - "Represent the imemory avs required number of hugepages" + vswitch_hugepages_reqd = int + "Represent the imemory vswitch required number of hugepages" - avs_hugepages_nr = int - "Represent the imemory avs number of hugepages" + vswitch_hugepages_nr = int + "Represent the imemory vswitch number of hugepages" - avs_hugepages_avail = int - "Represent the imemory avs number of hugepages available" + vswitch_hugepages_avail = int + "Represent the imemory vswitch number of hugepages available" vm_hugepages_nr_2M_pending = int "Represent the imemory vm number of hugepages pending (2M pages)" @@ -182,9 +182,9 @@ class Memory(base.APIBase): if not expand: memory.unset_fields_except(['uuid', 'memtotal_mib', 'memavail_mib', 'platform_reserved_mib', 'hugepages_configured', - 'avs_hugepages_size_mib', 'avs_hugepages_nr', - 'avs_hugepages_reqd', - 'avs_hugepages_avail', + 'vswitch_hugepages_size_mib', 'vswitch_hugepages_nr', + 'vswitch_hugepages_reqd', + 'vswitch_hugepages_avail', 'vm_hugepages_nr_2M', 'vm_hugepages_nr_1G', 'vm_hugepages_use_1G', 'vm_hugepages_nr_2M_pending', @@ -576,10 +576,10 @@ def _check_memory(rpc_port, ihost, platform_reserved_mib=None, mem_alloc += int(rpc_port['vm_hugepages_nr_1G']) * 1000 LOG.debug("vm total=%s" % (mem_alloc)) - avs_hp_size = rpc_port['avs_hugepages_size_mib'] - avs_hp_nr = rpc_port['avs_hugepages_nr'] - mem_alloc += avs_hp_size * avs_hp_nr - LOG.debug("avs_hp_nr=%s avs_hp_size=%s" % (avs_hp_nr, avs_hp_size)) + vs_hp_size = rpc_port['vswitch_hugepages_size_mib'] + vs_hp_nr = rpc_port['vswitch_hugepages_nr'] + mem_alloc += vs_hp_size * vs_hp_nr + LOG.debug("vs_hp_nr=%s vs_hp_size=%s" % (vs_hp_nr, vs_hp_size)) LOG.debug("memTotal %s mem_alloc %s" % (node_memtotal_mib, mem_alloc)) # Initial configuration defaults mem_alloc to consume 100% of 2M pages, diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/port.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/port.py index a59f70a992..c76db7a6ef 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/port.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/port.py @@ -117,7 +117,7 @@ class Port(base.APIBase): "Represent the interface_id the port belongs to" dpdksupport = bool - "Represent whether or not the port supported AVS acceleration" + "Represent whether or not the port supports DPDK acceleration" host_uuid = types.uuid "Represent the UUID of the host the port belongs to" diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py index b7762949ea..22bbc11b0f 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py @@ -48,6 +48,8 @@ from sysinv.openstack.common.gettextutils import _ LOG = log.getLogger(__name__) +VALID_VSWITCH_TYPES = [constants.VSWITCH_TYPE_OVS_DPDK] + class System(base.APIBase): """API representation of a system. @@ -353,6 +355,8 @@ class SystemController(rest.RestController): change_https = False change_sdn = False change_dc_role = False + vswitch_type = None + # prevent description field from being updated for p in jsonpatch.JsonPatch(patch): if p['path'] == '/software_version': @@ -412,6 +416,10 @@ class SystemController(rest.RestController): distributed_cloud_role = p['value'] patch.remove(p) + if p['path'] == '/vswitch_type': + vswitch_type = p['value'] + patch.remove(p) + try: patched_system = jsonpatch.apply_patch(system_dict, jsonpatch.JsonPatch(patch)) @@ -454,6 +462,15 @@ class SystemController(rest.RestController): raise wsme.exc.ClientSideError(_("distributed_cloud_role is already set " " as %s" % rpc_isystem['distributed_cloud_role'])) + if 'vswitch_type' in updates: + if vswitch_type not in VALID_VSWITCH_TYPES: + raise wsme.exc.ClientSideError(_("unsupported vswitch_type: %s" + % vswitch_type)) + if vswitch_type == rpc_isystem['capabilities']['vswitch_type']: + raise wsme.exc.ClientSideError(_("vswitch_type is already set" + " as %s" % vswitch_type)) + patched_system['capabilities']['vswitch_type'] = vswitch_type + # Update only the fields that have changed name = "" contact = "" @@ -502,12 +519,16 @@ class SystemController(rest.RestController): pecan.request.context) if capabilities: if change_sdn: - LOG.info("update sdn capabilities to %s" % capabilities) + LOG.info("update sdn to %s" % capabilities) pecan.request.rpcapi.update_sdn_enabled(pecan.request.context) if change_https: - LOG.info("update capabilities / https to %s" % capabilities) + LOG.info("update https to %s" % capabilities) pecan.request.rpcapi.configure_system_https( pecan.request.context) + if vswitch_type: + LOG.info("update vswitch_type to %s" % capabilities) + pecan.request.rpcapi.update_vswitch_type( + pecan.request.context) if distributed_cloud_role and change_dc_role: LOG.info("update distributed cloud role to %s" % distributed_cloud_role) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py index 78c9119305..346f357e82 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py @@ -248,17 +248,9 @@ def is_aio_simplex_host_unlocked(host): host['invprovision'] != constants.PROVISIONING) -# cache the result of the vswitch type to avoid having to query the system -# for each access to this system attribute -_vswitch_type = None - - def get_vswitch_type(): - global _vswitch_type - if _vswitch_type is None: - system = pecan.request.dbapi.isystem_get_one() - _vswitch_type = system.capabilities.get('vswitch_type') - return _vswitch_type + system = pecan.request.dbapi.isystem_get_one() + return system.capabilities.get('vswitch_type') def get_https_enabled(): diff --git a/sysinv/sysinv/sysinv/sysinv/cmd/query_pci_id b/sysinv/sysinv/sysinv/sysinv/cmd/query_pci_id new file mode 100755 index 0000000000..a86d6c5bc0 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/cmd/query_pci_id @@ -0,0 +1,61 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import sys +import json +import subprocess +from argparse import ArgumentParser + + +def main(): + ''' The goal of this script is to discover if the supplied PCI device is + supported by the vswitch as an accelerated NIC.''' + + parser = ArgumentParser(description="Query vswitch NIC support") + + parser.add_argument("-v", "--vendor", dest="vid", + help="Vendor ID", + type=lambda x: hex(int(x, 0)), + action='store', metavar="HEX", + required=True) + parser.add_argument("-d", "--device", dest="did", + help="Device ID", + type=lambda x: hex(int(x, 0)), + action='store', metavar="HEX", + required=True) + args = parser.parse_args() + + cmd = 'python /usr/share/openvswitch/scripts/dpdk-pmdinfo.py ' \ + '-r /usr/sbin/ovs-vswitchd' + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) + out, err = p.communicate() + result = out.split('\n') + + for line in result: + if not line: + continue + + pmd_info = json.loads(line) + supported_devices = pmd_info['pci_ids'] + + for supported_device in supported_devices: + vid = hex(supported_device[0]) + did = hex(supported_device[1]) + + if vid == args.vid and did == args.did: + print("Vendor ID: %s Device ID: %s is supported" % + (args.vid, args.did)) + return 0 + + print("Vendor ID: %s Device ID: %s is not supported" % + (args.vid, args.did)) + return 1 + + +if __name__ == "__main__": + ret = main() + sys.exit(ret) diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 2c749e6265..ecedf8d163 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -244,7 +244,7 @@ NEUTRON_PROVIDERNET_VXLAN = "vxlan" NEUTRON_PROVIDERNET_VLAN = "vlan" # Supported compute node vswitch types -VSWITCH_TYPE_AVS = "avs" +VSWITCH_TYPE_OVS_DPDK = "ovs-dpdk" VSWITCH_TYPE_NUAGE_VRS = "nuage_vrs" # Partition default sizes diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 6cf3a8b3c4..07701d02b6 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -1744,7 +1744,7 @@ class ConductorManager(service.PeriodicService): mtu = constants.DEFAULT_MTU port = None # ignore port if no MAC address present, this will - # occur for data port after they are configured via AVS + # occur for data port after they are configured via DPDK driver if not inic['mac']: continue try: @@ -6645,11 +6645,6 @@ class ConductorManager(service.PeriodicService): # Apply Neutron manifest on Controller(this # will update the SNAT rules for the SDN controllers) - # Ideally we would also like to apply the vswitch manifest - # on Compute so as to write the vswitch.ini however AVS - # cannot resync on the fly, so mark the Compute node as - # config-out-of-date - self._config_update_hosts(context, [constants.COMPUTE], reboot=True) config_uuid = self._config_update_hosts(context, @@ -6679,6 +6674,30 @@ class ConductorManager(service.PeriodicService): personalities = [constants.COMPUTE] self._config_update_hosts(context, personalities, reboot=True) + def update_vswitch_type(self, context): + """Update the system vswitch type. + + :param context: an admin context. + """ + LOG.info("update_vswitch_type") + + personalities = [constants.CONTROLLER] + config_dict = { + "personalities": personalities, + "classes": ['platform::sysctl::controller::runtime', + 'platform::nfv::runtime', + 'openstack::neutron::server::runtime'] + } + config_uuid = self._config_update_hosts(context, personalities) + self._config_apply_runtime_manifest(context, config_uuid, config_dict) + + if tsc.system_type == constants.TIS_AIO_BUILD: + personalities = [constants.CONTROLLER] + else: + personalities = [constants.COMPUTE] + + self._config_update_hosts(context, personalities, reboot=True) + def _update_hosts_file(self, hostname, address, active=True): """Update or add an entry to the /etc/hosts configuration file diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index 454af0d022..6a6e0101a7 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -1226,6 +1226,14 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): return self.call(context, self.make_msg('update_sdn_enabled')) + def update_vswitch_type(self, context): + """Synchronously, have the conductor update the system vswitch type + + :param context: request context. + """ + return self.call(context, + self.make_msg('update_vswitch_type')) + def configure_keystore_account(self, context, service_name, username, password): """Synchronously, have a conductor configure a ks(keyring) account. diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/068_memory_column_rename.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/068_memory_column_rename.py new file mode 100644 index 0000000000..b5d2228656 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/068_memory_column_rename.py @@ -0,0 +1,29 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2013-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sqlalchemy import MetaData, Table + +ENGINE = 'InnoDB' +CHARSET = 'utf8' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + memory = Table('i_imemory', meta, autoload=True) + memory.c.avs_hugepages_size_mib.alter(name="vswitch_hugepages_size_mib") + memory.c.avs_hugepages_reqd.alter(name="vswitch_hugepages_reqd") + memory.c.avs_hugepages_nr.alter(name="vswitch_hugepages_nr") + memory.c.avs_hugepages_avail.alter(name="vswitch_hugepages_avail") + return True + + +def downgrade(migrate_engine): + # As per other openstack components, downgrade is + # unsupported in this release. + raise NotImplementedError('SysInv database downgrade is unsupported.') diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py index 406b92668e..d768cdb58b 100755 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py @@ -293,10 +293,10 @@ class imemory(Base): hugepages_configured = Column(Boolean, default=False) - avs_hugepages_size_mib = Column(Integer) - avs_hugepages_reqd = Column(Integer) - avs_hugepages_nr = Column(Integer) - avs_hugepages_avail = Column(Integer) + vswitch_hugepages_size_mib = Column(Integer) + vswitch_hugepages_reqd = Column(Integer) + vswitch_hugepages_nr = Column(Integer) + vswitch_hugepages_avail = Column(Integer) vm_hugepages_nr_2M_pending = Column(Integer) vm_hugepages_nr_1G_pending = Column(Integer) diff --git a/sysinv/sysinv/sysinv/sysinv/objects/memory.py b/sysinv/sysinv/sysinv/sysinv/objects/memory.py index 5958c019ac..d446680871 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/memory.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/memory.py @@ -34,10 +34,10 @@ class Memory(base.SysinvObject): 'hugepages_configured': utils.str_or_none, - 'avs_hugepages_size_mib': utils.int_or_none, - 'avs_hugepages_reqd': utils.int_or_none, - 'avs_hugepages_nr': utils.int_or_none, - 'avs_hugepages_avail': utils.int_or_none, + 'vswitch_hugepages_size_mib': utils.int_or_none, + 'vswitch_hugepages_reqd': utils.int_or_none, + 'vswitch_hugepages_nr': utils.int_or_none, + 'vswitch_hugepages_avail': utils.int_or_none, 'vm_hugepages_nr_2M_pending': utils.int_or_none, 'vm_hugepages_nr_1G_pending': utils.int_or_none, diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/__init__.py b/sysinv/sysinv/sysinv/sysinv/puppet/__init__.py index e69de29bb2..7653b2c8ef 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/__init__.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2018 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import yaml + + +class quoted_str(str): + pass + + +# force strings to be single-quoted to avoid interpretation as numeric values +def quoted_presenter(dumper, data): + return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style="'") + + +yaml.add_representer(quoted_str, quoted_presenter) diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/base.py b/sysinv/sysinv/sysinv/sysinv/puppet/base.py index 173cdf2b0d..9865316769 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/base.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/base.py @@ -16,6 +16,8 @@ from sysinv.common import constants from sysinv.common import utils from sysinv.common import exception +from . import quoted_str + @six.add_metaclass(abc.ABCMeta) class BasePuppet(object): @@ -45,6 +47,10 @@ class BasePuppet(object): def context(self): return self._operator.context + @staticmethod + def quoted_str(value): + return quoted_str(value) + @staticmethod def _generate_random_password(length=16): suffix = "Ti0*" @@ -79,6 +85,13 @@ class BasePuppet(object): system = self._get_system() return system.capabilities.get('region_config', False) + def _vswitch_type(self): + if self.dbapi is None: + return False + + system = self._get_system() + return system.capabilities.get('vswitch_type', None) + def _distributed_cloud_role(self): if self.dbapi is None: return None @@ -160,6 +173,14 @@ class BasePuppet(object): cpus.append(c) return cpus + def _get_vswitch_cpu_list(self, host): + cpus = self._get_host_cpu_list(host, constants.VSWITCH_FUNCTION) + return sorted(cpus, key=lambda c: c.cpu) + + def _get_platform_cpu_list(self, host): + cpus = self._get_host_cpu_list(host, constants.PLATFORM_FUNCTION) + return sorted(cpus, key=lambda c: c.cpu) + def _get_service_parameters(self, service=None): service_parameters = [] if self.dbapi is None: diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py index 17e3d3edc4..a97c317bba 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py @@ -12,18 +12,16 @@ import uuid from netaddr import IPAddress from netaddr import IPNetwork -from netaddr import EUI -from netaddr import mac_unix - from sysinv.common import constants from sysinv.common import exception from sysinv.common import utils +from sysinv.conductor import openstack from sysinv.openstack.common import log + from . import base LOG = log.getLogger(__name__) -MAC_ADDRESS_UL_BIT_VALUE = 2 PLATFORM_NETWORK_TYPES = [constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_MGMT, @@ -59,6 +57,16 @@ ADDRESS_CONFIG_RESOURCE = 'platform::addresses::address_config' class InterfacePuppet(base.BasePuppet): """Class to encapsulate puppet operations for interface configuration""" + def __init__(self, *args, **kwargs): + super(InterfacePuppet, self).__init__(*args, **kwargs) + self._openstack = None + + @property + def openstack(self): + if not self._openstack: + self._openstack = openstack.OpenStackOperator(self.dbapi) + return self._openstack + def get_host_config(self, host): """ Generate the hiera data for the puppet network config and route config @@ -125,7 +133,7 @@ class InterfacePuppet(base.BasePuppet): 'networks': self._get_network_type_index(), 'gateways': self._get_gateway_index(), 'floatingips': self._get_floating_ip_index(), - 'providernets': {}, + 'providernets': self._get_provider_networks(host), } return context @@ -306,6 +314,18 @@ class InterfacePuppet(base.BasePuppet): return floating_ips + def _get_provider_networks(self, host): + # TODO(alegacy): this will not work as intended for upgrades of AIO-SX + # and -DX. The call to get_providernetworksdict will return an empty + # dictionary because the neutron endpoint is not available yet. Since + # we do not currently support SDN/OVS over upgrades we will need to + # deal with this in a later commit. + pnets = {} + if (self.openstack and + constants.COMPUTE in utils.get_personalities(host)): + pnets = self.openstack.get_providernetworksdict(quiet=True) + return pnets + def is_platform_network_type(iface): networktype = utils.get_primary_network_type(iface) @@ -733,7 +753,7 @@ def needs_interface_config(context, iface): if is_a_mellanox_device(context, iface): # Check for Mellanox data interfaces. We must set the MTU sizes of # Mellanox data interfaces in case it is not the default. Normally - # data interfaces are owned by AVS, they are not managed through + # data interfaces are owned by DPDK, they are not managed through # Linux but in the Mellanox case, the interfaces are still visible # in Linux so in case one needs to set jumbo frames, it has to be # set in Linux as well. We only do this for combined nodes or @@ -744,21 +764,6 @@ def needs_interface_config(context, iface): return False -def needs_vswitch_config(context, iface): - """ - Determine whether an interface needs to be configured as a vswitch - interface. This is true if the interface is a data interface, is required - by a platform interface (i.e., a platform VLAN over a data interface), is - required by a data interface (i.e., a data AE member, a VLAN lower - interface). - """ - if not is_compute_subfunction(context): - return False - elif is_data_interface(context, iface): - return True - return False - - def get_basic_network_config(ifname, ensure='present', method='manual', onboot='true', hotplug='false', family='inet', @@ -1001,167 +1006,6 @@ def generate_network_config(context, config, iface): }) -class CustomMacDialect(mac_unix): - word_fmt = '%.2x' - - -def _set_local_admin_bit(value): - """ - Assert the locally administered bit in the MAC address in order to avoid - conflicting with the real port that this interface is associated with. - """ - mac = EUI(value, dialect=CustomMacDialect) - mac.__setitem__(0, (mac.words[0] | MAC_ADDRESS_UL_BIT_VALUE)) - return str(mac) - - -def get_vswitch_ethernet_command(context, iface): - """ - Produce the cli command to add a single ethernet interface to vswitch. - """ - port = get_interface_port(context, iface) - attributes = {'ifname': get_interface_os_ifname(context, iface) + '-avp', - 'port_uuid': port['uuid'], - 'iface_uuid': iface['uuid'], - 'mtu': iface['imtu']} - if is_dpdk_compatible(context, iface): - command = ("ethernet add %(port_uuid)s %(iface_uuid)s " - "%(mtu)s\n" % attributes) - else: - # Set the locally administered bit on the MAC address because to run - # providernet connectivity tests we will need to originate packets from - # this interface. Since the other end of the interface is the actual - # avp interface in the linux kernel it will get confused if we are - # sending it packets originated from its' MAC address. - attributes.update({'mac': _set_local_admin_bit(iface['imac']), - 'numa': 0}) - command = ("port add avp-provider %(iface_uuid)s %(mac)s %(numa)s " - "%(mtu)s %(ifname)s\n" % attributes) - return command - - -def get_vswitch_vlan_command(context, iface): - """ - Produce the cli command to add a vlan ethernet interface to vswitch. - """ - lower_iface = get_lower_interface(context, iface) - attributes = {'lower_uuid': lower_iface['uuid'], - 'vlan_id': iface['vlan_id'], - 'iface_uuid': iface['uuid'], - 'mtu': iface['imtu']} - command = ("vlan add %(lower_uuid)s %(vlan_id)s %(iface_uuid)s %(mtu)s" % - attributes) - if is_platform_interface(context, iface): - # If this is a platform VLAN than mark it as a host interface to - # prevent the vswitch bridge input handler from intercepting packets - # destined to the interface MAC. That intercept exists for providernet - # connectivity tests but those are not necessary on platform VLAN - # interfaces. - command += " host" - return command + "\n" - - -def get_vswitch_bond_options(iface): - """ - Return a dictionary of vswitch bond attributes based on the interface - configuration. - """ - monitor_mode = 'link-state' - - ae_mode = iface['aemode'] - if ae_mode in BALANCED_AE_MODES: - distribution_mode = 'hash-mac' - protection_mode = 'loadbalance' - elif ae_mode in LACP_AE_MODES: - distribution_mode = 'hash-mac' - protection_mode = '802.3ad' - else: - protection_mode = 'failover' - distribution_mode = 'none' - - return {'distribution': distribution_mode, - 'protection': protection_mode, - 'monitor': monitor_mode} - - -def get_vswitch_bond_commands(context, iface): - """ - Produce the cli command to add a aggregated ethernet interface to vswitch. - """ - - attributes = {'uuid': iface['uuid'], - 'mtu': iface['imtu']} - attributes.update(get_vswitch_bond_options(iface)) - - # Setup the AE interface - commands = ("ae add %(uuid)s %(mtu)s %(protection)s %(distribution)s " - "%(monitor)s\n" % attributes) - - # Add all lower interfaces as AE member interfaces - for lower_ifname in iface['uses']: - lower_iface = context['interfaces'][lower_ifname] - commands += ("ae attach member %s %s\n" % - (iface['uuid'], lower_iface['uuid'])) - - return commands - - -def get_vswitch_interface_commands(context, iface): - """ - Produce the cli command to add a single interface to vswitch. - """ - if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET: - return get_vswitch_ethernet_command(context, iface) - elif iface['iftype'] == constants.INTERFACE_TYPE_AE: - return get_vswitch_bond_commands(context, iface) - elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN: - return get_vswitch_vlan_command(context, iface) - - -def get_vswitch_address_command(iface, address): - """ - Produce the cli command required to create an interface address. - """ - attributes = {'iface_uuid': iface['uuid'], - 'address': address['address'], - 'prefix': address['prefix']} - return ('interface add addr %(iface_uuid)s %(address)s/%(prefix)s\n' % - attributes) - - -def get_vswitch_route_command(iface, route): - """ - Produce the vswitch cli command required to create a route table entry for - a given interface. - """ - attributes = {'iface_uuid': iface['uuid'], - 'network': route['network'], - 'prefix': route['prefix'], - 'gateway': route['gateway'], - 'metric': route['metric']} - return ('route append %(network)s/%(prefix)s %(iface_uuid)s %(gateway)s ' - '%(metric)s\n' % attributes) - - -def get_vswitch_commands(context, iface): - """ - Produce the vswitch cli commands required for configuring the logical - interfaces in vswitch for this particular interface. - """ - commands = get_vswitch_interface_commands(context, iface) - - networktype = utils.get_primary_network_type(iface) - if networktype in DATA_NETWORK_TYPES: - # Add complementary commands (if needed) - for address in context['addresses'].get(iface['ifname'], []): - if address['networktype'] == networktype: - commands += get_vswitch_address_command(iface, address) - for route in context['routes'].get(iface['ifname'], []): - commands += get_vswitch_route_command(iface, route) - - return commands - - def find_interface_by_type(context, networktype): """ Lookup an interface based on networktype. This is only intended for diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/neutron.py b/sysinv/sysinv/sysinv/sysinv/puppet/neutron.py index 99ed1546cd..61303939b9 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/neutron.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/neutron.py @@ -65,8 +65,6 @@ class NeutronPuppet(openstack.OpenstackBasePuppet): ksuser = self._get_service_user_name(self.SERVICE_NAME) - sdn_l3_mode_enabled = self._get_sdn_l3_mode_enabled() - config = { 'neutron::server::notifications::auth_url': self._keystone_identity_uri(), @@ -85,8 +83,6 @@ class NeutronPuppet(openstack.OpenstackBasePuppet): 'neutron::agents::metadata::metadata_ip': self._get_management_address(), - 'neutron::agents::vswitch::sdn_manage_external_networks': - not sdn_l3_mode_enabled, 'neutron::keystone::authtoken::auth_url': self._keystone_identity_uri(), @@ -113,8 +109,6 @@ class NeutronPuppet(openstack.OpenstackBasePuppet): 'openstack::neutron::params::region_name': self.get_region_name(), - 'openstack::neutron::params::l3_agent_enabled': - not sdn_l3_mode_enabled, 'openstack::neutron::params::service_create': self._to_create_services(), } @@ -163,33 +157,19 @@ class NeutronPuppet(openstack.OpenstackBasePuppet): controller_config } - def _get_sdn_l3_mode_enabled(self): - try: - sdn_l3_mode = self.dbapi.service_parameter_get_one( - service=constants.SERVICE_TYPE_NETWORK, - section=constants.SERVICE_PARAM_SECTION_NETWORK_DEFAULT, - name=constants.SERVICE_PARAM_NAME_DEFAULT_SERVICE_PLUGINS) - if not sdn_l3_mode: - return False - allowed_vals = constants.SERVICE_PLUGINS_SDN - return (any(sp in allowed_vals - for sp in sdn_l3_mode.value.split(','))) - except: - return False - def get_host_config(self, host): - interface_mappings = [] + device_mappings = [] for iface in self.context['interfaces'].values(): if (utils.get_primary_network_type(iface) == constants.NETWORK_TYPE_PCI_SRIOV): port = interface.get_interface_port(self.context, iface) providernets = interface.get_interface_providernets(iface) for net in providernets: - interface_mappings.append("%s:%s" % (net, port['name'])) + device_mappings.append("%s:%s" % (net, port['name'])) config = { 'neutron::agents::ml2::sriov::physical_device_mappings': - interface_mappings, + device_mappings, } if host.personality == constants.CONTROLLER: diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/nfv.py b/sysinv/sysinv/sysinv/sysinv/puppet/nfv.py index 20d76ff376..f5fa3652e5 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/nfv.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/nfv.py @@ -26,11 +26,9 @@ class NfvPuppet(openstack.OpenstackBasePuppet): system = self._get_system() if system.system_mode == constants.SYSTEM_MODE_SIMPLEX: - data_port_fault_handling_enabled = False single_hypervisor = True single_controller = True else: - data_port_fault_handling_enabled = True single_hypervisor = False single_controller = False @@ -52,8 +50,6 @@ class NfvPuppet(openstack.OpenstackBasePuppet): self._get_management_address(), 'nfv::nfvi::host_listener_host': self._get_management_address(), - 'nfv::nfvi::infrastructure_rest_api_data_port_fault_handling_enabled': - data_port_fault_handling_enabled, 'nfv::nfvi::openstack_username': self._operator.keystone.get_admin_user_name(), diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py b/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py new file mode 100644 index 0000000000..842a15504c --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py @@ -0,0 +1,277 @@ +# +# Copyright (c) 2018 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sysinv.common import constants +from sysinv.common import utils + +from . import base +from . import interface + + +class OVSPuppet(base.BasePuppet): + """Class to encapsulate puppet operations for vswitch configuration""" + + def __init__(self, *args, **kwargs): + super(OVSPuppet, self).__init__(*args, **kwargs) + + def get_host_config(self, host): + config = {} + if (constants.COMPUTE in utils.get_personalities(host) and + self._vswitch_type() == constants.VSWITCH_TYPE_OVS_DPDK): + config.update(self._get_cpu_config(host)) + config.update(self._get_memory_config(host)) + config.update(self._get_port_config(host)) + config.update(self._get_virtual_config(host)) + config.update(self._get_neutron_config(host)) + return config + + def _get_port_config(self, host): + ovs_devices = {} + ovs_bridges = {} + ovs_ports = {} + ovs_addresses = {} + + index = 0 + for iface in sorted(self.context['interfaces'].values(), + key=interface.interface_sort_key): + if interface.is_data_network_type(iface): + # create a separate bridge for every configured data interface + brname = 'br-phy%d' % index + ovs_bridges[brname] = {} + + # save the associated bridge for provider network mapping + iface['_ovs_bridge'] = brname + + if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET: + port, devices = self._get_ethernet_port( + host, iface, brname, index) + elif iface['iftype'] == constants.INTERFACE_TYPE_AE: + port, devices = self._get_bond_port( + host, iface, brname, index) + elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN: + port, devices = self._get_vlan_port( + host, iface, brname, index) + else: + raise Exception("unsupported interface type: %s" % + iface['iftype']) + + ovs_ports.update({port['name']: port}) + ovs_devices.update({d['pci_addr']: d for d in devices}) + + index += 1 + + # currently only one provider network is supported per + # interface, therefore obtain first entry + providernet = interface.get_interface_providernets(iface)[0] + + # setup tunnel address if assigned provider network is vxlan + if self._is_vxlan_providernet(providernet): + address = interface.get_interface_primary_address( + self.context, iface) + if address: + ovs_addresses[brname] = { + 'ifname': brname, + 'address': address['address'], + 'prefixlen': address['prefix'], + } + + return { + 'platform::vswitch::ovs::devices': ovs_devices, + 'platform::vswitch::ovs::bridges': ovs_bridges, + 'platform::vswitch::ovs::ports': ovs_ports, + 'platform::vswitch::ovs::addresses': ovs_addresses, + } + + def _get_ethernet_device(self, iface): + port = interface.get_interface_port(self.context, iface) + + pci_addr = self.quoted_str(port.pciaddr) + + return { + 'pci_addr': pci_addr + } + + def _get_ethernet_interface(self, host, iface, ifname): + + port = interface.get_interface_port(self.context, iface) + + rxq_count = len(self.context["_ovs_cpus"]) + + attributes = [ + "options:dpdk-devargs=%s" % str(port.pciaddr), + "options:n_rxq=%d" % rxq_count, + "mtu_request=%d" % iface['imtu'] + ] + + # TODO(mpeters): set other_config:pmd-rxq-affinity to pin receive + # queues to specific PMD cores + + iftype = 'dpdk' + + return { + 'name': ifname, + 'type': iftype, + 'attributes': attributes, + } + + def _get_ethernet_port(self, host, iface, bridge, index): + devices = [] + interfaces = [] + + ifname = 'eth%d' % index + + devices.append(self._get_ethernet_device(iface)) + interfaces.append(self._get_ethernet_interface(host, iface, ifname)) + + port = { + 'name': ifname, + 'bridge': bridge, + 'interfaces': interfaces, + } + + return port, devices + + def _get_bond_port(self, host, iface, bridge, index): + devices = [] + interfaces = [] + attributes = [] + + ifname = 'bond%d' % index + + # TODO(mpeters): OVS can support balance-tcp if interface txhashpolicy + # is set to layer3+4 (currently restricted at API for data interfaces) + ae_mode = iface['aemode'] + if ae_mode in interface.ACTIVE_STANDBY_AE_MODES: + attributes.append("bond_mode=active-backup") + if ae_mode in interface.BALANCED_AE_MODES: + attributes.append("bond_mode=balance-slb") + elif ae_mode in interface.LACP_AE_MODES: + attributes.append("lacp=active") + attributes.append("bond_mode=balance-slb") + attributes.append("other_config:lacp-time=fast") + + for member, lower_ifname in enumerate(iface['uses']): + lower_iface = self.context['interfaces'][lower_ifname] + member_ifname = '%s.%d' % (ifname, member) + + devices.append(self._get_ethernet_device(lower_iface)) + interfaces.append(self._get_ethernet_interface( + host, lower_iface, member_ifname)) + + port = { + 'type': 'bond', + 'name': ifname, + 'bridge': bridge, + 'attributes': attributes, + 'interfaces': interfaces, + } + + return port, devices + + def _get_vlan_port(self, host, iface, bridge, index): + devices = [] + interfaces = [] + + ifname = 'vlan%d' % iface['vlan_id'] + attributes = [ + "tag=%d" % iface['vlan_id'] + ] + + lower_iface = interface.get_lower_interface(self.context, iface) + + devices.append(self._get_ethernet_device(lower_iface)) + interfaces.append(self._get_ethernet_interface( + host, lower_iface, ifname)) + + port = { + 'name': ifname, + 'bridge': bridge, + 'attributes': attributes, + 'interfaces': interfaces, + } + + return port, devices + + def _get_cpu_config(self, host): + platform_cpus = self._get_platform_cpu_list(host) + vswitch_cpus = self._get_vswitch_cpu_list(host) + + host_cpus = platform_cpus[:1] + vswitch_cpus[:] + + host_core_list = self.quoted_str( + ','.join([str(c.cpu) for c in host_cpus])) + pmd_core_list = self.quoted_str( + ','.join([str(c.cpu) for c in vswitch_cpus])) + + # save the assigned CPUs for port assignment + self.context["_ovs_cpus"] = [c.cpu for c in vswitch_cpus] + + return { + 'vswitch::dpdk::host_core_list': host_core_list, + 'vswitch::dpdk::pmd_core_list': pmd_core_list, + } + + def _get_memory_config(self, host): + vswitch_memory = [] + + host_memory = self.dbapi.imemory_get_by_ihost(host.id) + for memory in host_memory: + vswitch_size = memory.vswitch_hugepages_size_mib + vswitch_pages = memory.vswitch_hugepages_nr + vswitch_memory.append(str(vswitch_size * vswitch_pages)) + + dpdk_socket_mem = self.quoted_str(','.join(vswitch_memory)) + + return { + 'vswitch::dpdk::socket_mem': dpdk_socket_mem + } + + def _get_virtual_config(self, host): + config = {} + if utils.is_virtual() or utils.is_virtual_compute(host): + config.update({ + 'platform::vswitch::params::iommu_enabled': False, + 'platform::vswitch::params::hugepage_dir': '/mnt/huge-2048kB', + + 'openstack::neutron::params::tunnel_csum': True, + }) + return config + + def _get_neutron_config(self, host): + local_ip = None + tunnel_types = set() + bridge_mappings = [] + for iface in self.context['interfaces'].values(): + if interface.is_data_network_type(iface): + # obtain the assigned bridge for interface + brname = iface.get('_ovs_bridge') + if brname: + providernets = interface.get_interface_providernets(iface) + for providernet in providernets: + if self._is_vxlan_providernet(providernet): + address = interface.get_interface_primary_address( + self.context, iface) + if address: + local_ip = address['address'] + tunnel_types.add( + constants.NEUTRON_PROVIDERNET_VXLAN) + else: + bridge_mappings.append('%s:%s' % + (providernet, brname)) + + return { + 'neutron::agents::ml2::ovs::local_ip': local_ip, + 'neutron::agents::ml2::ovs::tunnel_types': list(tunnel_types), + 'neutron::agents::ml2::ovs::bridge_mappings': bridge_mappings + } + + def _get_providernet_type(self, name): + if name in self.context['providernets']: + return self.context['providernets'][name]['type'] + + def _is_vxlan_providernet(self, name): + providernet_type = self._get_providernet_type(name) + return bool(providernet_type == constants.NEUTRON_PROVIDERNET_VXLAN) diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py index 66eb924a62..a3f57eaa8f 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py @@ -89,6 +89,7 @@ class PlatformPuppet(base.BasePuppet): 'platform::params::security_profile': system.security_profile, 'platform::config::params::timezone': system.timezone, + 'platform::params::vswitch_type': self._vswitch_type(), } def _get_hosts_config(self): @@ -586,12 +587,12 @@ class PlatformPuppet(base.BasePuppet): total_hugepages_2M = vm_hugepages_nr_2M total_hugepages_1G = vm_hugepages_nr_1G - if memory.avs_hugepages_size_mib == constants.MIB_2M: - total_hugepages_2M += memory.avs_hugepages_nr - vswitch_2M_page += memory.avs_hugepages_nr - elif memory.avs_hugepages_size_mib == constants.MIB_1G: - total_hugepages_1G += memory.avs_hugepages_nr - vswitch_1G_page += memory.avs_hugepages_nr + if memory.vswitch_hugepages_size_mib == constants.MIB_2M: + total_hugepages_2M += memory.vswitch_hugepages_nr + vswitch_2M_page += memory.vswitch_hugepages_nr + elif memory.vswitch_hugepages_size_mib == constants.MIB_1G: + total_hugepages_1G += memory.vswitch_hugepages_nr + vswitch_1G_page += memory.vswitch_hugepages_nr vswitch_2M_pages.append(vswitch_2M_page) vswitch_1G_pages.append(vswitch_1G_page) diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/puppet.py b/sysinv/sysinv/sysinv/sysinv/puppet/puppet.py index 94e9367f9f..fe5a5deabc 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/puppet.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/puppet.py @@ -40,6 +40,7 @@ from . import networking from . import neutron from . import nfv from . import nova +from . import ovs from . import panko from . import patching from . import platform @@ -89,6 +90,7 @@ class PuppetOperator(object): self.neutron = neutron.NeutronPuppet(self) self.nfv = nfv.NfvPuppet(self) self.nova = nova.NovaPuppet(self) + self.ovs = ovs.OVSPuppet(self) self.panko = panko.PankoPuppet(self) self.patching = patching.PatchingPuppet(self) self.platform = platform.PlatformPuppet(self) @@ -215,6 +217,7 @@ class PuppetOperator(object): config.update(self.panko.get_system_config()) config.update(self.dcmanager.get_system_config()) config.update(self.dcorch.get_system_config()) + # service_parameter must be last to permit overrides config.update(self.service_parameter.get_system_config()) filename = 'system.yaml' @@ -274,6 +277,7 @@ class PuppetOperator(object): config = {} config.update(self.platform.get_host_config(host, config_uuid)) config.update(self.interface.get_host_config(host)) + config.update(self.ovs.get_host_config(host)) config.update(self.networking.get_host_config(host)) config.update(self.storage.get_host_config(host)) config.update(self.ldap.get_host_config(host)) @@ -283,6 +287,7 @@ class PuppetOperator(object): config.update(self.device.get_host_config(host)) config.update(self.nova.get_host_config(host)) config.update(self.neutron.get_host_config(host)) + # service_parameter must be last to permit overrides config.update(self.service_parameter.get_host_config(host)) self._write_host_config(host, config) @@ -298,14 +303,16 @@ class PuppetOperator(object): config = {} config.update(self.platform.get_host_config(host, config_uuid)) config.update(self.interface.get_host_config(host)) + config.update(self.ovs.get_host_config(host)) config.update(self.networking.get_host_config(host)) config.update(self.storage.get_host_config(host)) config.update(self.ceph.get_host_config(host)) config.update(self.device.get_host_config(host)) config.update(self.nova.get_host_config(host)) config.update(self.neutron.get_host_config(host)) - config.update(self.service_parameter.get_host_config(host)) config.update(self.ldap.get_host_config(host)) + # service_parameter must be last to permit overrides + config.update(self.service_parameter.get_host_config(host)) self._write_host_config(host, config) except Exception: @@ -323,8 +330,9 @@ class PuppetOperator(object): config.update(self.networking.get_host_config(host)) config.update(self.storage.get_host_config(host)) config.update(self.ceph.get_host_config(host)) - config.update(self.service_parameter.get_host_config(host)) config.update(self.ldap.get_host_config(host)) + # service_parameter must be last to permit overrides + config.update(self.service_parameter.get_host_config(host)) self._write_host_config(host, config) except Exception: diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py index 2611824ef2..aa93c37242 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py @@ -1596,7 +1596,8 @@ class TestCpePost(InterfaceTestCase): # Expected error: Unexpected interface network type list data @mock.patch.object(api_if_v1, '_neutron_providernet_extension_supported') - def test_create_invalid_non_avs(self, mock_providernet_extension_supported): + def test_create_invalid_non_vswitch(self, + mock_providernet_extension_supported): mock_providernet_extension_supported.return_value = False self._create_ethernet('data0', networktype=constants.NETWORK_TYPE_DATA, diff --git a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py index dae01d9ca3..7e24e6ed04 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py @@ -179,7 +179,8 @@ def get_test_isystem(**kw): 'capabilities': kw.get('capabilities', {"cinder_backend": constants.CINDER_BACKEND_LVM, - "vswitch_type": constants.VSWITCH_TYPE_AVS, + "vswitch_type": + constants.VSWITCH_TYPE_OVS_DPDK, "region_config": False, "sdn_enabled": True, "shared_services": "[]"}), @@ -367,10 +368,10 @@ def get_test_imemory(**kw): 'hugepages_configured': kw.get('hugepages_configured', False), - 'avs_hugepages_size_mib': kw.get('avs_hugepages_size_mib', 2), - 'avs_hugepages_reqd': kw.get('avs_hugepages_reqd'), - 'avs_hugepages_nr': kw.get('avs_hugepages_nr', 256), - 'avs_hugepages_avail': kw.get('avs_hugepages_avail', 0), + 'vswitch_hugepages_size_mib': kw.get('vswitch_hugepages_size_mib', 2), + 'vswitch_hugepages_reqd': kw.get('vswitch_hugepages_reqd'), + 'vswitch_hugepages_nr': kw.get('vswitch_hugepages_nr', 256), + 'vswitch_hugepages_avail': kw.get('vswitch_hugepages_avail', 0), 'vm_hugepages_nr_2M_pending': kw.get('vm_hugepages_nr_2M_pending'), 'vm_hugepages_nr_1G_pending': kw.get('vm_hugepages_nr_1G_pending'), diff --git a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py index a99c133411..5e84646b8f 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 Wind River Systems, Inc. +# Copyright (c) 2017-2018 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -400,7 +400,11 @@ class BaseTestCase(dbbase.DbTestCase): @puppet.puppet_context def _update_context(self): - self.context = self.operator.interface._create_interface_context(self.host) + self.context = \ + self.operator.interface._create_interface_context(self.host) + + # Update the puppet context with generated interface context + self.operator.context.update(self.context) def _setup_context(self): self._setup_configuration() @@ -1363,166 +1367,6 @@ class InterfaceTestCase(BaseTestCase): return port, iface -class InterfaceVswitchTestCase(BaseTestCase): - def _setup_configuration(self): - # Create a single port/interface for basic function testing - self._create_test_common() - self._create_test_host(constants.COMPUTE) - self.port, self.iface = ( - self._create_ethernet_test('data0', - constants.NETWORK_TYPE_DATA)) - - def _update_context(self): - # ensure DB entries are updated prior to updating the context which - # will re-read the entries from the DB. - self.host.save(self.admin_context) - self.port.save(self.admin_context) - self.iface.save(self.admin_context) - super(InterfaceVswitchTestCase, self)._update_context() - - def setUp(self): - super(InterfaceVswitchTestCase, self).setUp() - self._setup_context() - - def test_needs_vswitch_config_false_on_controller(self): - self.iface['networktype'] = constants.NETWORK_TYPE_DATA - self.host['personality'] = constants.CONTROLLER - self.host['subfunctions'] = constants.CONTROLLER - self._update_context() - needed = interface.needs_vswitch_config(self.context, self.iface) - self.assertFalse(needed) - - def test_needs_vswitch_config_true_on_compute(self): - self.iface['networktype'] = constants.NETWORK_TYPE_DATA - needed = interface.needs_vswitch_config(self.context, self.iface) - self.assertTrue(needed) - - def test_needs_vswitch_config_false_for_platform(self): - vlan = self._create_vlan_test('infra0', - constants.NETWORK_TYPE_INFRA, 1) - self.host['personality'] = constants.COMPUTE - self._update_context() - needed = interface.needs_vswitch_config(self.context, vlan) - self.assertFalse(needed) - - def test_get_vswitch_ethernet_command(self): - cmd = interface.get_vswitch_ethernet_command(self.context, self.iface) - expected = ("ethernet add %(port_uuid)s %(iface_uuid)s %(mtu)s\n" % - {'port_uuid': self.port['uuid'], - 'iface_uuid': self.iface['uuid'], - 'mtu': self.iface['imtu']}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_ethernet_command_slow_data(self): - self.port['dpdksupport'] = False - self._update_context() - cmd = interface.get_vswitch_ethernet_command(self.context, self.iface) - expected = ( - "port add avp-provider %(uuid)s %(mac)s 0 %(mtu)s %(ifname)s\n" % - {'uuid': self.iface['uuid'], - 'mtu': self.iface['imtu'], - 'mac': interface._set_local_admin_bit(self.iface['imac']), - 'ifname': self.port['name'] + '-avp'}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_vlan_command(self): - vlan = self._create_vlan_test( - 'data1', constants.NETWORK_TYPE_DATA, 1, self.iface) - self._update_context() - cmd = interface.get_vswitch_vlan_command(self.context, vlan) - expected = ("vlan add %(lower_uuid)s %(vlan_id)s %(uuid)s %(mtu)s\n" % - {'lower_uuid': self.iface['uuid'], - 'vlan_id': vlan['vlan_id'], - 'uuid': vlan['uuid'], - 'mtu': vlan['imtu']}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_vlan_command_for_platform(self): - vlan = self._create_vlan_test( - 'infra', constants.NETWORK_TYPE_INFRA, 1, self.iface) - self._update_context() - cmd = interface.get_vswitch_vlan_command(self.context, vlan) - expected = ( - "vlan add %(lower_uuid)s %(vlan_id)s %(uuid)s %(mtu)s host\n" % - {'lower_uuid': self.iface['uuid'], - 'vlan_id': vlan['vlan_id'], - 'uuid': vlan['uuid'], - 'mtu': vlan['imtu']}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_address_command(self): - address = self.context['addresses'].get(self.iface['ifname'])[0] - cmd = interface.get_vswitch_address_command(self.iface, address) - expected = ( - "interface add addr %(iface_uuid)s %(address)s/%(prefix)s\n" % - {'iface_uuid': self.iface['uuid'], - 'address': address['address'], - 'prefix': address['prefix']}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_route_command(self): - route = self.context['routes'].get(self.iface['ifname'])[0] - cmd = interface.get_vswitch_route_command(self.iface, route) - expected = ( - "route append %(network)s/%(prefix)s %(iface_uuid)s %(gateway)s " - "%(metric)s\n" % - {'iface_uuid': self.iface['uuid'], - 'network': route['network'], - 'gateway': route['gateway'], - 'prefix': route['prefix'], - 'metric': route['metric']}) - self.assertEqual(expected, cmd) - - def test_get_vswitch_bond_options_balanced(self): - bond = self._create_bond_test('data1', constants.NETWORK_TYPE_DATA) - self._update_context() - bond['aemode'] = 'balanced' - options = interface.get_vswitch_bond_options(bond) - expected = {'distribution': 'hash-mac', - 'protection': 'loadbalance', - 'monitor': 'link-state'} - self.assertEqual(options, expected) - - def test_get_vswitch_bond_options_8023ad(self): - bond = self._create_bond_test('data1', constants.NETWORK_TYPE_DATA) - self._update_context() - bond['aemode'] = '802.3ad' - options = interface.get_vswitch_bond_options(bond) - expected = {'distribution': 'hash-mac', - 'protection': '802.3ad', - 'monitor': 'link-state'} - self.assertEqual(options, expected) - - def test_get_vswitch_bond_options_active_backup(self): - bond = self._create_bond_test('data1', constants.NETWORK_TYPE_DATA) - self._update_context() - bond['aemode'] = 'active_backup' - options = interface.get_vswitch_bond_options(bond) - expected = {'distribution': 'none', - 'protection': 'failover', - 'monitor': 'link-state'} - self.assertEqual(options, expected) - - def test_get_vswitch_bond_commands(self): - bond = self._create_bond_test('data1', constants.NETWORK_TYPE_DATA) - self._update_context() - bond['aemode'] = '802.3ad' - options = interface.get_vswitch_bond_options(bond) - attributes = {'uuid': bond['uuid'], - 'mtu': bond['imtu']} - attributes.update(options) - for index, lower_ifname in enumerate(bond['uses']): - lower_iface = self.context['interfaces'][lower_ifname] - attributes['member%s_uuid' % index] = lower_iface['uuid'] - expected = ( - "ae add %(uuid)s %(mtu)s %(protection)s %(distribution)s %(monitor)s\n" - "ae attach member %(uuid)s %(member0_uuid)s\n" - "ae attach member %(uuid)s %(member1_uuid)s\n" % - attributes) - cmds = interface.get_vswitch_bond_commands(self.context, bond) - self.assertEqual(cmds, expected) - - class InterfaceHostTestCase(BaseTestCase): def _setup_configuration(self): # Personality is set to compute to avoid issues due to missing OAM @@ -1558,27 +1402,13 @@ class InterfaceHostTestCase(BaseTestCase): class_name = self.__class__.__name__ return os.path.join(hiera_directory, class_name) + ".yaml" - def _create_vswitch_directory(self): - vswitch_path = os.path.join(os.environ['VIRTUAL_ENV'], 'vswitch') - if not os.path.exists(vswitch_path): - os.mkdir(vswitch_path, 0o755) - return vswitch_path - - def _get_vswitch_filename(self, vswitch_directory): - class_name = self.__class__.__name__ - return os.path.join(vswitch_directory, class_name) + ".cmds" - def test_generate_interface_config(self): hieradata_directory = self._create_hieradata_directory() config_filename = self._get_config_filename(hieradata_directory) - vswitch_directory = self._create_vswitch_directory() - vswitch_filename = self._get_vswitch_filename(vswitch_directory) with open(config_filename, 'w') as config_file: config = self.operator.interface.get_host_config(self.host) self.assertIsNotNone(config) yaml.dump(config, config_file, default_flow_style=False) - with open(vswitch_filename, 'w') as commands: - commands.write(config['cgcs_vswitch::vswitch_commands']) def test_create_interface_context(self): context = self.operator.interface._create_interface_context(self.host) @@ -1612,7 +1442,7 @@ class InterfaceHostTestCase(BaseTestCase): for iface in self.interfaces: expected = bool(iface['ifname'] in self.expected_data_interfaces) if interface.is_data_interface(self.context, iface) != expected: - print("iface %s is %sa vswitch interface" % ( + print("iface %s is %sa data interface" % ( iface['ifname'], ('not ' if expected else ''))) self.assertFalse(True) @@ -1676,19 +1506,6 @@ class InterfaceHostTestCase(BaseTestCase): iface['ifname'], ('not ' if expected else ''))) self.assertFalse(True) - def test_needs_vswitch_config(self): - expected_configured = [] - if interface.is_compute_subfunction(self.context): - expected_configured += (self.expected_data_interfaces + - self.expected_slow_interfaces) - for iface in self.interfaces: - expected = bool(iface['ifname'] in expected_configured) - actual = interface.needs_vswitch_config(self.context, iface) - if expected != actual: - print("iface %s is %sconfigured" % ( - iface['ifname'], ('not ' if expected else ''))) - self.assertFalse(True) - class InterfaceControllerEthernet(InterfaceHostTestCase): def _setup_configuration(self): diff --git a/sysinv/sysinv/sysinv/tox.ini b/sysinv/sysinv/sysinv/tox.ini index 2b61fb9292..cf8e3482a4 100644 --- a/sysinv/sysinv/sysinv/tox.ini +++ b/sysinv/sysinv/sysinv/tox.ini @@ -8,7 +8,6 @@ minversion = 1.6 toxworkdir = /tmp/{env:USER}_sysinvtox wrsdir = {toxinidir}/../../../../../../../../.. cgcsdir = {toxinidir}/../../../../.. -avsdir = {toxinidir}/../../../../../../../../wr-avs/layers/avs distshare={toxworkdir}/.tox/distshare [testenv]