cookbooks/fb_network_scripts/recipes/default.rb (178 lines of code) (raw):
#
# Cookbook Name:: fb_network_scripts
# Recipe:: default
#
# Copyright (c) 2012-present, Facebook, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
net_svc_name = value_for_platform(
['centos', 'redhat', 'fedora', 'arista_eos'] => { 'default' => 'network' },
)
unless net_svc_name
fail "fb_network_scripts: unsupported platform #{node['platform']}, cannot " +
'continue'
end
# Action is nothing because we don't need chef to make sure this is running
# it's just here so we can call restart
service 'network' do
only_if { node.nw_changes_allowed? }
service_name net_svc_name
supports :restart => true
action :nothing
end
fb_helpers_request_nw_changes 'manage' do
action :nothing
delayed_action :cleanup_signal_files_when_no_change_required
end
template '/etc/sysconfig/network' do
only_if { ['rhel', 'fedora'].include?(node['platform_family']) }
source 'network.erb'
owner 'root'
group 'root'
mode '0644'
if node.firstboot_any_phase?
notifies :restart, 'service[network]'
end
end
include_recipe 'fb_network_scripts::packages'
fb_modprobe_module 'br_netfilter' do
only_if { node['fb_network_scripts']['enable_bridge_filter'] }
action :load
end
if node.centos?
directory '/dev/net' do
only_if { node['fb_network_scripts']['enable_tun'] }
owner 'root'
group 'root'
mode '0755'
end
execute 'create_dev_net_tun' do
only_if { node['fb_network_scripts']['enable_tun'] }
not_if { File.chardev?('/dev/net/tun') }
creates '/dev/net/tun'
command 'mknod /dev/net/tun c 10 200'
end
else # Not a centos box
whyrun_safe_ruby_block 'test tun sanity' do
only_if { node['fb_network_scripts']['enable_tun'] }
block do
fail 'fb_network_scripts: Tunneling is only supported on CentOS'
end
end
end
# Workaround for https://github.com/fedora-sysv/initscripts/issues/296
cookbook_file '/sbin/ifup-pre-local' do
source 'ifup-pre-local'
owner 'root'
group 'root'
mode '0755'
end
# For interfaces where we could read the ring buffer settings, check that
# they are maxed out and increase them if necessary. Only eth interfaces are
# supported and we skip them if they're not up. mlx nics can hang unless the
# driver is >= v1.5.10 (newer kernels) so skip ones that are unsafe.
whyrun_safe_ruby_block 'setup ring params' do
only_if { node.linux? }
block do
node['fb_network_scripts']['ring_params'].to_hash.each do |iface, config|
next unless config
node.default['fb_network_scripts']['ifup']['ethtool'] << {
'interface' => iface,
'subcommand' => '-G',
'field' => 'rx',
'check_field' => 'RX',
'check_pipe' => 'grep Current -A 100',
'value' => config['max_rx'],
}
node.default['fb_network_scripts']['ifup']['ethtool'] << {
'interface' => iface,
'subcommand' => '-G',
'field' => 'tx',
'check_field' => 'TX',
'check_pipe' => 'grep Current -A 100',
'value' => config['max_tx'],
}
end
end
end
execute 'configure pause settings for primary interface' do
not_if do
FB::Hardware.primary_interface_pause_configured?(node)
end
command lazy {
interface = node['fb_network_scripts']['primary_interface']
interface = interface == 'br0' ? 'eth0' : interface
cmd = "ethtool --pause #{interface}"
node['fb_network_scripts']['pause'].each do |pause_type, value|
next if value.nil?
s = nil
if value.is_a?(TrueClass)
s = 'on'
elsif value.is_a?(FalseClass)
s = 'off'
else
fail 'Invalid value'
end
cmd += " #{pause_type} #{s}"
end
cmd
}
# ethtool returns 78 if there was nothing to change in pause params
# We did check the settings need changing but on Multi-Host NICs
# the link settings are global so we may race with another host.
returns [0, 78]
action :run
end
if node.centos?
%w{
NetworkManager
}.each do |pkg|
service pkg do
action [:disable, :stop]
end
end
service 'rdisc' do
action [:stop, :disable]
end
end
whyrun_safe_ruby_block 'validate v6 secondaries' do
block do
node['fb_network_scripts']['interfaces'].to_hash.each do |iface, conf|
conf['v6secondaries']&.each do |addr|
unless addr.include?('/')
fail 'fb_network_scripts: You must specify a netmask to address' +
" in v6secondaries (#{iface})"
end
end
end
end
end
# Setups up networking files, and will internally bounce interfaces
# that need it, unless we plan to restart networking
fb_network_scripts 'interface_files'
# This dance is so that other recipes that might be adding ifup commands are
# able to force a reload when their scripts are updated
whyrun_safe_ruby_block 'trigger re-run of ifup-local' do
block do
node.default['fb_network_scripts']['_rerun_ifup-local'] = true
end
action :nothing
end
# This has to be processed *after* the fb_network_scripts provider as it reads
# the node['fb_network_scripts']['ifup']['sysctl'] attribute, which is set by
# the provider.
template '/sbin/ifup-local' do
source 'ifup-local.erb'
owner 'root'
group 'root'
mode '0755'
notifies :run, 'whyrun_safe_ruby_block[trigger re-run of ifup-local]',
:immediately
end
execute 're-run ifup-local' do
only_if { node['fb_network_scripts']['_rerun_ifup-local'] }
command '/sbin/ifup-local all'
end
# This is done in ifup-local above, (which runs both at boot and whenever
# the script changes), but it's still possible to miss hosts (e.g., if someone
# manually changes the setting). So keep this block in just in case
node['network']['interfaces'].to_hash.each_key do |iface|
next if iface == 'lo'
execute "set RX/TX ring parameters for #{iface}" do
only_if do
# network is only available on Linux
node.linux? &&
# make sure this is a physical interface in the up state
node['network']['interfaces'][iface] &&
node['network']['interfaces'][iface]['flags'] &&
node['network']['interfaces'][iface]['flags'].include?('UP') &&
# only run if something needs to be changed
node['network']['interfaces'][iface]['ring_params'] &&
node['fb_network_scripts']['ring_params'][iface] &&
(node['fb_network_scripts']['ring_params'][iface]['max_rx'] !=
node['network']['interfaces'][iface]['ring_params']['current_rx'] ||
node['fb_network_scripts']['ring_params'][iface]['max_tx'] !=
node['network']['interfaces'][iface]['ring_params']['current_tx'])
end
# this is done at runtime because not all interfaces have ring_params, so
# it should only be evaluated after the only_if clears
command lazy {
"ethtool -G #{iface} " +
"rx #{node['fb_network_scripts']['ring_params'][iface]['max_rx']} " +
"tx #{node['fb_network_scripts']['ring_params'][iface]['max_tx']} "
}
# ethtool returns 80 if there was nothing to change
returns [0, 80]
end
end
# Conditionally fail if a dynamic address was found on one of the interfaces.
# Examples of dynamic addresses include SLAAC or DHCP(v6).
whyrun_safe_ruby_block 'validate dynamic address' do
not_if { node['fb_network_scripts']['allow_dynamic_addresses'] }
block { node.validate_and_fail_on_dynamic_addresses }
end