-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
F OpenNebula/one#6280: Add the VRouter service
This is a refactor of the previously used VNF service (to be obsolete). All the main features have been included: - DHCP4 server (kea + onelease) - DNS forwarder (unbound) - Router4 - NAT4 - SDNAT4 - LVS (keepalived's built-in, static + onegate) - HAProxy (static + onegate) - Keepalived with failover capability (via one-failover service) Also works: - It's *mostly* compatible with the VNF's interface. - Each feature is presented as a simple OpenRC service. - Failover is now implemented via unix pipes instead of script hooks. - Load-balancers (LVS and HAProxy) work now in both VROUTER and OneFlow modes. - Re-contextualization is fully supported. - Lots of unit tests (rspec).
- Loading branch information
Showing
38 changed files
with
5,345 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../vrouter.rb' | ||
|
||
module Service | ||
module DHCP4 | ||
extend self | ||
|
||
DEPENDS_ON = %w[Service::Failover] | ||
|
||
ONEAPP_VNF_DHCP4_ENABLED = env :ONEAPP_VNF_DHCP4_ENABLED, 'NO' | ||
|
||
ONEAPP_VNF_DHCP4_AUTHORITATIVE = env :ONEAPP_VNF_DHCP4_AUTHORITATIVE, 'YES' | ||
|
||
ONEAPP_VNF_DHCP4_MAC2IP_ENABLED = env :ONEAPP_VNF_DHCP4_MAC2IP_ENABLED, 'YES' | ||
ONEAPP_VNF_DHCP4_MAC2IP_MACPREFIX = env :ONEAPP_VNF_DHCP4_MAC2IP_MACPREFIX, '02:00' | ||
|
||
ONEAPP_VNF_DHCP4_LEASE_TIME = env :ONEAPP_VNF_DHCP4_LEASE_TIME, '3600' | ||
|
||
ONEAPP_VNF_DHCP4_GATEWAY = env :ONEAPP_VNF_DHCP4_GATEWAY, nil | ||
ONEAPP_VNF_DHCP4_DNS = env :ONEAPP_VNF_DHCP4_DNS, nil | ||
|
||
ONEAPP_VNF_DHCP4_INTERFACES = env :ONEAPP_VNF_DHCP4_INTERFACES, '' # nil -> none, empty -> all | ||
|
||
attr_reader :interfaces, :mgmt | ||
|
||
@interfaces = parse_interfaces ONEAPP_VNF_DHCP4_INTERFACES | ||
@mgmt = detect_mgmt_interfaces | ||
|
||
def parse_env | ||
interfaces = @interfaces.keys - @mgmt | ||
|
||
n2a = addrs_to_nics(interfaces, family: %w[inet]).to_h do |a, n| | ||
[n.first, a] | ||
end | ||
|
||
a2s = addrs_to_subnets(interfaces, family: %w[inet]).to_h do |a, s| | ||
[a.split('/').first, s] | ||
end | ||
|
||
s2r = subnets_to_ranges(a2s.values) | ||
|
||
interfaces.each_with_object({}) do |nic, vars| | ||
p = env("ONEAPP_VNF_DHCP4_#{nic.upcase}", nil)&.split(':')&.map(&:strip) | ||
|
||
vars[nic] = { | ||
address: n2a[nic], | ||
subnet: if p.nil? then a2s[n2a[nic]] else p[0] end, | ||
range: if p.nil? then s2r[a2s[n2a[nic]]] else p[1] end, | ||
gateway: env("ONEAPP_VNF_DHCP4_#{nic.upcase}_GATEWAY", ONEAPP_VNF_DHCP4_GATEWAY), | ||
dns: env("ONEAPP_VNF_DHCP4_#{nic.upcase}_DNS", ONEAPP_VNF_DHCP4_DNS), | ||
mtu: env("ONEAPP_VNF_DHCP4_#{nic.upcase}_MTU", ip_link_show(nic)['mtu']), | ||
} | ||
end | ||
end | ||
|
||
def install(initdir: '/etc/init.d') | ||
msg :info, 'DHCP4::install' | ||
|
||
onelease4_apk = File.join File.dirname(__FILE__), 'kea-hook-onelease4-1.1.1-r0.apk' | ||
|
||
puts bash <<~SCRIPT | ||
apk --no-cache add ruby kea-dhcp4 | ||
apk --no-cache --allow-untrusted add '#{onelease4_apk}' | ||
SCRIPT | ||
|
||
file "#{initdir}/one-dhcp4", <<~SERVICE, mode: 'u=rwx,g=rx,o=' | ||
#!/sbin/openrc-run | ||
source /run/one-context/one_env | ||
command="/usr/bin/ruby" | ||
command_args="-r /etc/one-appliance/lib/helpers.rb -r #{__FILE__}" | ||
output_log="/var/log/one-appliance/one-dhcp4.log" | ||
error_log="/var/log/one-appliance/one-dhcp4.log" | ||
depend() { | ||
after net firewall keepalived | ||
} | ||
start_pre() { | ||
rc-service kea-dhcp4 start --nodeps | ||
} | ||
start() { :; } | ||
stop() { :; } | ||
stop_post() { | ||
rc-service kea-dhcp4 stop --nodeps | ||
} | ||
SERVICE | ||
|
||
toggle [:update] | ||
end | ||
|
||
def configure(basedir: '/etc/kea') | ||
msg :info, 'DHCP4::configure' | ||
|
||
if ONEAPP_VNF_DHCP4_ENABLED | ||
dhcp4_vars = parse_env | ||
|
||
config = { 'Dhcp4' => { | ||
'interfaces-config' => { 'interfaces' => dhcp4_vars.keys }, | ||
'authoritative' => ONEAPP_VNF_DHCP4_AUTHORITATIVE, | ||
'option-data' => [], | ||
'subnet4' => dhcp4_vars.map do |nic, vars| | ||
data = [] | ||
data << { 'name' => 'routers', 'data' => vars[:gateway] } unless vars[:gateway].nil? | ||
data << { 'name' => 'domain-name-servers', 'data' => vars[:dns] } unless vars[:dns].nil? | ||
data << { 'name' => 'interface-mtu', 'data' => vars[:mtu].to_s } unless vars[:mtu].nil? || nic == 'lo' | ||
{ 'subnet' => vars[:subnet], | ||
'pools' => [ { 'pool' => vars[:range] } ], | ||
'option-data' => data, | ||
'reservations' => [ | ||
{ 'flex-id' => "'DO-NOT-LEASE-#{vars[:address]}'", | ||
'ip-address' => vars[:address] } ], | ||
'reservation-mode' => 'all' } | ||
end, | ||
'lease-database' => { | ||
'type' => 'memfile', | ||
'persist' => true, | ||
'lfc-interval' => 2 * ONEAPP_VNF_DHCP4_LEASE_TIME.to_i | ||
}, | ||
'sanity-checks' => { 'lease-checks' => 'fix-del' }, | ||
'valid-lifetime' => ONEAPP_VNF_DHCP4_LEASE_TIME.to_i, | ||
'calculate-tee-times' => true, | ||
'loggers' => [ | ||
{ 'name' => 'kea-dhcp4', | ||
'output_options' => [ { 'output' => '/var/log/kea/kea-dhcp4.log' } ], | ||
'severity' => 'INFO', | ||
'debuglevel' => 0 } | ||
], | ||
'hooks-libraries' => if ONEAPP_VNF_DHCP4_MAC2IP_ENABLED then | ||
[ { 'library' => '/usr/lib/kea/hooks/libkea-onelease-dhcp4.so', | ||
'parameters' => { | ||
'enabled' => true, | ||
'byte-prefix' => ONEAPP_VNF_DHCP4_MAC2IP_MACPREFIX, | ||
'logger-name' => 'onelease-dhcp4', | ||
'debug' => false, | ||
'debug-logfile' => '/var/log/kea/onelease-dhcp4-debug.log' } } ] | ||
else [] end | ||
} } | ||
|
||
file "#{basedir}/kea-dhcp4.conf", JSON.pretty_generate(config), owner: 'kea', | ||
group: 'kea', | ||
mode: 'u=rw,g=r,o=', | ||
overwrite: true | ||
toggle [:enable] | ||
else | ||
toggle [:stop, :disable] | ||
end | ||
end | ||
|
||
def toggle(operations) | ||
operations.each do |op| | ||
msg :debug, "DHCP4::toggle([:#{op}])" | ||
case op | ||
when :reload | ||
puts bash 'rc-service kea-dhcp4 reload' | ||
when :enable | ||
puts bash 'rc-update add kea-dhcp4 default' | ||
puts bash 'rc-update add one-dhcp4 default' | ||
when :disable | ||
puts bash 'rc-update del kea-dhcp4 default ||:' | ||
puts bash 'rc-update del one-dhcp4 default ||:' | ||
when :update | ||
puts bash 'rc-update -u' | ||
else | ||
puts bash "rc-service one-dhcp4 #{op.to_s}" | ||
end | ||
end | ||
end | ||
|
||
def bootstrap | ||
msg :info, 'DHCP4::bootstrap' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rspec' | ||
|
||
def clear_env | ||
ENV.delete_if { |name| name.include?('VNF') || name.include?('VROUTER') } | ||
end | ||
|
||
RSpec.describe self do | ||
it 'should provide and parse all env vars' do | ||
clear_env | ||
|
||
ENV['ONEAPP_VNF_DHCP4_ENABLED'] = 'YES' | ||
ENV['ONEAPP_VNF_DHCP4_AUTHORITATIVE'] = 'YES' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_MAC2IP_ENABLED'] = 'YES' | ||
ENV['ONEAPP_VNF_DHCP4_MAC2IP_MACPREFIX'] = '02:00' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_LEASE_TIME'] = '3600' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_GATEWAY'] = '1.2.3.4' | ||
ENV['ONEAPP_VNF_DHCP4_DNS'] = '1.1.1.1' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_INTERFACES'] = 'lo/127.0.0.1 eth0 eth1 eth2 eth3' | ||
ENV['ETH0_VROUTER_MANAGEMENT'] = 'YES' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_ETH2'] = '30.0.0.0/8:30.40.50.64-30.40.50.68' | ||
ENV['ONEAPP_VNF_DHCP4_ETH2_GATEWAY'] = '30.40.50.1' | ||
ENV['ONEAPP_VNF_DHCP4_ETH2_DNS'] = '8.8.8.8' | ||
|
||
ENV['ONEAPP_VNF_DHCP4_ETH3_GATEWAY'] = '40.50.60.1' | ||
ENV['ONEAPP_VNF_DHCP4_ETH3_DNS'] = '8.8.4.4' | ||
|
||
load './main.rb'; include Service::DHCP4 | ||
|
||
allow(Service::DHCP4).to receive(:ip_addr_list).and_return([ | ||
{ 'ifname' => 'lo', | ||
'addr_info' => [ { 'family' => 'inet', | ||
'local' => '127.0.0.1', | ||
'prefixlen' => 8 } ] }, | ||
|
||
{ 'ifname' => 'eth0', | ||
'addr_info' => [ { 'family' => 'inet', | ||
'local' => '10.20.30.40', | ||
'prefixlen' => 24 } ] }, | ||
|
||
{ 'ifname' => 'eth1', | ||
'addr_info' => [ { 'family' => 'inet', | ||
'local' => '20.30.40.50', | ||
'prefixlen' => 16 } ] }, | ||
|
||
{ 'ifname' => 'eth2', | ||
'addr_info' => [ { 'family' => 'inet', | ||
'local' => '30.40.50.60', | ||
'prefixlen' => 8 } ] }, | ||
|
||
{ 'ifname' => 'eth3', | ||
'addr_info' => [ { 'family' => 'inet', | ||
'local' => '40.50.60.70', | ||
'prefixlen' => 24 } ] }, | ||
]) | ||
|
||
allow(Service::DHCP4).to receive(:ip_link_show).and_return( | ||
{ 'mtu' => 1111 }, | ||
{ 'mtu' => 2222 }, | ||
{ 'mtu' => 3333 }, | ||
{ 'mtu' => 4444 }, | ||
) | ||
|
||
expect(Service::DHCP4.parse_env).to eq ({ | ||
'lo' => { | ||
address: '127.0.0.1', | ||
dns: '1.1.1.1', | ||
gateway: '1.2.3.4', | ||
mtu: 1111, | ||
range: '127.0.0.2-127.255.255.254', | ||
subnet: '127.0.0.0/8', | ||
}, | ||
'eth1' => { | ||
address: '20.30.40.50', | ||
dns: '1.1.1.1', | ||
gateway: '1.2.3.4', | ||
mtu: 2222, | ||
range: '20.30.0.2-20.30.255.254', | ||
subnet: '20.30.0.0/16', | ||
}, | ||
'eth2' => { | ||
address: '30.40.50.60', | ||
dns: '8.8.8.8', | ||
gateway: '30.40.50.1', | ||
mtu: 3333, | ||
range: '30.40.50.64-30.40.50.68', | ||
subnet: '30.0.0.0/8', | ||
}, | ||
'eth3' => { | ||
address: '40.50.60.70', | ||
dns: '8.8.4.4', | ||
gateway: '40.50.60.1', | ||
mtu: 4444, | ||
range: '40.50.60.2-40.50.60.254', | ||
subnet: '40.50.60.0/24', | ||
}, | ||
}) | ||
end | ||
end |
Oops, something went wrong.