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 <matt.peters@windriver.com>
This commit is contained in:
Matt Peters 2018-06-07 17:47:42 -05:00
parent 69365bb834
commit 9fafe08b65
56 changed files with 873 additions and 770 deletions

View File

@ -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

View File

@ -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]")

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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":

View File

@ -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.

View File

@ -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 {

View File

@ -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'] ->

View File

@ -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']
})
}

View File

@ -0,0 +1,2 @@
ovs-vsctl --timeout 10 -- --may-exist add-br <%= @name -%>
-- set bridge <%= @name -%> datapath_type=<%= @datapath_type -%>

View File

@ -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 -%>

View File

@ -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

View File

@ -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

View File

@ -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 "<imemory %s>" % self._info

View File

@ -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)

View File

@ -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='<https_enabled>',
choices=['true', 'false'],
help='The HTTPS enabled or disabled flag')
@utils.arg('-v', '--vswitch_type',
metavar='<vswitch_type>',
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()

View File

@ -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"

View File

@ -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)

View File

@ -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':

View File

@ -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"

View File

@ -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 -= \

View File

@ -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():

View File

@ -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,

View File

@ -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"

View File

@ -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)

View File

@ -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():

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.')

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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(),

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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,

View File

@ -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'),

View File

@ -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):

View File

@ -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]