358 lines
12 KiB
Ruby
358 lines
12 KiB
Ruby
if RUBY_VERSION == '1.8.7'
|
|
require 'rubygems'
|
|
end
|
|
|
|
require 'uri'
|
|
require 'faraday'
|
|
require 'json'
|
|
require 'ipaddr'
|
|
|
|
Puppet::Type.type(:midonet_gateway_bgp).provide(:midonet_api_caller) do
|
|
|
|
def create
|
|
|
|
if resource[:bgp_neighbors].class == Hash
|
|
bgp_neighbors_resource = [resource[:bgp_neighbors]]
|
|
else
|
|
bgp_neighbors_resource = resource[:bgp_neighbors]
|
|
end
|
|
|
|
if resource[:bgp_advertised_networks].class == String
|
|
bgp_advertised_networks_resource = [resource[:bgp_advertised_networks]]
|
|
else
|
|
bgp_advertised_networks_resource = resource[:bgp_advertised_networks]
|
|
end
|
|
|
|
define_connection(resource[:midonet_api_url])
|
|
|
|
entered_neighbors_parsed = bgp_neighbors_resource.collect { |x| {'ip_address' => x['ip_address'], 'remote_asn' => x['remote_asn'].to_i} }
|
|
|
|
# Get the edge router uuid
|
|
provider_router = call_get_provider_router()[0]
|
|
provider_router_id = provider_router['id']
|
|
|
|
# Assign local ASN to the provider router
|
|
asn = provider_router['asNumber']
|
|
call_assign_asn(provider_router, resource[:bgp_local_as_number]) unless asn.to_i == resource[:bgp_local_as_number].to_i
|
|
|
|
# Sync BGP peers
|
|
bgp_neighbors = call_get_bgp_peers(provider_router_id)
|
|
m = Array.new
|
|
bgp_neighbors.each do |bgp_neighbor|
|
|
n = { "ip_address" => bgp_neighbor["address"],
|
|
"remote_asn" => bgp_neighbor["asNumber"] }
|
|
m << n
|
|
end
|
|
tbd_peers = m - entered_neighbors_parsed
|
|
tba_peers = entered_neighbors_parsed - m
|
|
|
|
tba_peers.each { |a| call_add_bgp_peer(provider_router_id, a['ip_address'], a['remote_asn']) }
|
|
tbd_peers.each do |d|
|
|
bgp_peer_id = bgp_neighbors.select { |bgp_neighbor| bgp_neighbor['asNumber'] == d['remote_asn'] }[0]["id"]
|
|
call_delete_bgp_peer(bgp_peer_id)
|
|
end
|
|
|
|
# Advertise floating IP networks
|
|
bgp_advertised_networks = call_get_bgp_networks(provider_router_id)
|
|
j = Array.new
|
|
bgp_advertised_networks.each do |bgp_advertised_network|
|
|
k = [ bgp_advertised_network["subnetAddress"], bgp_advertised_network["subnetLength"] ].join("/")
|
|
j << k
|
|
end
|
|
|
|
tbd_bgp_networks = j - bgp_advertised_networks_resource
|
|
tba_bgp_networks = bgp_advertised_networks_resource - j
|
|
tbd_bgp_networks.each do |d|
|
|
bgp_network_id = bgp_advertised_networks.select { |bgp_advertised_network| bgp_advertised_network['subnetAddress'] == d.split("/")[0] && bgp_advertised_network['subnetLength'] == d.split("/")[1] }[0]["id"]
|
|
call_delete_bgp_network(bgp_network_id)
|
|
end
|
|
tba_bgp_networks.each { |a| call_advertise_bgp_network(provider_router_id, a) }
|
|
|
|
# Get routes and see which ones do we need to actually create
|
|
dup_routes = Array.new
|
|
existing_bgp_routes = call_get_bgp_routes(provider_router_id)
|
|
neighbors_remote_net = bgp_neighbors_resource.map { |x| x['remote_net'] }.uniq
|
|
existing_bgp_routes.each do |r|
|
|
dup_routes << r if neighbors_remote_net.select { |net| net == "#{r['dstNetworkAddr']}/#{r['dstNetworkLength']}" }
|
|
end
|
|
# Add routes to remote BGP peers
|
|
tba_routes = neighbors_remote_net - dup_routes.map { |dr| "#{dr['dstNetworkAddr']}/#{dr['dstNetworkLength']}" }
|
|
tba_routes.each do |r|
|
|
call_add_bgp_route(provider_router_id, r)
|
|
end
|
|
|
|
# Add default routes
|
|
dup_routes = Array.new
|
|
existing_bgp_routes = call_get_bgp_routes(provider_router_id)
|
|
neighbors_remote_ips = bgp_neighbors_resource.map { |x| x['ip_address'] }.uniq
|
|
existing_bgp_routes.each do |r|
|
|
dup_routes << r if neighbors_remote_ips.select { |ip| ip == "#{r['nextHopGateway']}" }
|
|
end
|
|
# Add routes to remote BGP peers
|
|
tba_routes = neighbors_remote_ips - dup_routes.map { |dr| "#{dr['nextHopGateway']}" }
|
|
tba_routes.each do |ip|
|
|
b = bgp_neighbors_resource.select { |neighbor| neighbor['ip_address'] == ip }
|
|
call_add_default_routes(provider_router_id, bgp_neighbors_resource.select { |neighbor| neighbor['ip_address'] == ip }[0])
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
|
|
define_connection(resource[:midonet_api_url])
|
|
|
|
# Get the edge router uuid
|
|
provider_router_id = call_get_provider_router()[0]['id']
|
|
|
|
# "Unset" asNumber by setting it to -1 (default value)
|
|
call_assign_asn(provider_router_id, "-1")
|
|
|
|
# Remove BGP peers from router
|
|
bgp_peers = call_get_bgp_peers(provider_router_id)
|
|
bgp_peers.each do |bgp_peer|
|
|
call_delete_bgp_peer(bgp_peer["id"])
|
|
end
|
|
|
|
# De-advertise floating IP networks
|
|
bgp_networks = call_get_bgp_networks(provider_router_id)
|
|
bgp_networks.each do |bgp_network|
|
|
call_delete_bgp_network(bgp_network["id"])
|
|
end
|
|
end
|
|
|
|
def exists?
|
|
|
|
if resource[:bgp_neighbors].class == Hash
|
|
bgp_neighbors_resource = [resource[:bgp_neighbors]]
|
|
else
|
|
bgp_neighbors_resource = resource[:bgp_neighbors]
|
|
end
|
|
|
|
if resource[:bgp_advertised_networks].class == String
|
|
bgp_advertised_networks_resource = [resource[:bgp_advertised_networks]]
|
|
else
|
|
bgp_advertised_networks_resource = resource[:bgp_advertised_networks]
|
|
end
|
|
|
|
define_connection(resource[:midonet_api_url])
|
|
|
|
entered_neighbors_parsed = bgp_neighbors_resource.collect { |x| {'ip_address' => x['ip_address'], 'remote_asn' => x['remote_asn'].to_i} }
|
|
|
|
# Get the edge router uuid
|
|
provider_router = call_get_provider_router()[0]
|
|
provider_router_id = provider_router['id']
|
|
result_array = Array.new
|
|
|
|
# Check if local ASN is the same
|
|
result_array.push(provider_router["asNumber"].to_i == resource[:bgp_local_as_number].to_i)
|
|
# Check if BGP neighbors are the same
|
|
bgp_neighbors = call_get_bgp_peers(provider_router_id)
|
|
m = Array.new
|
|
bgp_neighbors.each do |bgp_neighbor|
|
|
n = { "ip_address" => bgp_neighbor["address"],
|
|
"remote_asn" => bgp_neighbor["asNumber"] }
|
|
m << n
|
|
end
|
|
result_array.push(m == entered_neighbors_parsed)
|
|
# Check if advertised networks are the same
|
|
bgp_advertised_networks = call_get_bgp_networks(provider_router_id)
|
|
j = Array.new
|
|
bgp_advertised_networks.each do |bgp_advertised_network|
|
|
k = [ bgp_advertised_network["subnetAddress"], bgp_advertised_network["subnetLength"] ].join("/")
|
|
j << k
|
|
end
|
|
result_array.push(j == bgp_advertised_networks_resource)
|
|
|
|
# Test if all tests are positive
|
|
return result_array.uniq == [true]
|
|
|
|
end
|
|
|
|
def define_connection(url)
|
|
|
|
@connection = Faraday.new(:url => url,
|
|
:ssl => { :verify =>false }) do |builder|
|
|
builder.request(:retry, {
|
|
:max => 5,
|
|
:interval => 0.05,
|
|
:exceptions => [
|
|
Faraday::Error::TimeoutError,
|
|
Faraday::ConnectionFailed,
|
|
Errno::ETIMEDOUT,
|
|
'Timeout::Error',
|
|
],
|
|
})
|
|
builder.request(:basic_auth, resource[:username], resource[:password])
|
|
builder.adapter(:net_http)
|
|
end
|
|
|
|
@connection.headers['X-Auth-Token'] = call_get_token()
|
|
end
|
|
|
|
def call_get_token()
|
|
res = @connection.get do |req|
|
|
req.headers['X-Auth-Project'] = resource[:tenant_name]
|
|
req.url "/midonet-api/login"
|
|
end
|
|
return JSON.parse(res.body)['key']
|
|
end
|
|
|
|
def call_get_provider_router()
|
|
res = @connection.get do |req|
|
|
req.url "/midonet-api/routers"
|
|
end
|
|
output = JSON.parse(res.body)
|
|
provider_router = output.select { |r| r['name'] == resource[:router]}
|
|
raise "Router #{resource[:router]} does not exist" if provider_router.empty?
|
|
return provider_router
|
|
end
|
|
|
|
def call_assign_asn( provider_router, bgp_local_as_number )
|
|
res = @connection.put do |req|
|
|
req.url "/midonet-api/routers/#{provider_router['id']}"
|
|
req.headers['Content-Type'] = "application/vnd.org.midonet.Router-v3+json"
|
|
req.body = {
|
|
'asNumber' => bgp_local_as_number,
|
|
'adminStateUp' => provider_router['adminStateUp'],
|
|
'inboundFilterId' => provider_router['inboundFilterId'],
|
|
'inboundMirrorIds' => provider_router['inboundMirrorIds'],
|
|
'name' => provider_router['name'],
|
|
'outboundFilterId' => provider_router['outboundFilterId'],
|
|
'outboundMirrorIds' => provider_router['outboundMirrorIds'],
|
|
'tenantId' => provider_router['tenantId']
|
|
}.to_json
|
|
end
|
|
end
|
|
|
|
def call_add_bgp_peer( provider_router_id, ip_address, remote_asn )
|
|
res = @connection.post do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/bgp_peers"
|
|
req.headers['Content-Type'] = "application/vnd.org.midonet.BgpPeer-v1+json"
|
|
req.body = { 'address' => ip_address,
|
|
'asNumber' => remote_asn }.to_json
|
|
end
|
|
end
|
|
|
|
def call_add_bgp_route( provider_router_id, remote_network )
|
|
net_address, net_length = remote_network.split("/")
|
|
port = ''
|
|
router_ports = call_get_router_ports(provider_router_id)
|
|
router_ports.each do |rp|
|
|
port = rp if IPAddr.new("#{remote_network}").include?(rp['portAddress'])
|
|
end
|
|
unless port.empty?
|
|
res = @connection.post do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/routes"
|
|
req.headers['Content-Type'] = "application/vnd.org.midonet.Route-v1+json"
|
|
req.body = {
|
|
'dstNetworkAddr' => net_address,
|
|
'dstNetworkLength' => net_length,
|
|
'nextHopPort' => port['id'],
|
|
'srcNetworkAddr' => '0.0.0.0',
|
|
'srcNetworkLength' => '0',
|
|
'type' => 'Normal',
|
|
'weight' => '71'
|
|
}.to_json
|
|
end
|
|
end
|
|
end
|
|
|
|
def call_add_default_routes( provider_router_id, bgp_neighbor )
|
|
remote_ip = bgp_neighbor['remote_net']
|
|
remote_network = bgp_neighbor['remote_net']
|
|
net_address, net_length = remote_network.split("/")
|
|
port = ''
|
|
router_ports = call_get_router_ports(provider_router_id)
|
|
router_ports.each do |rp|
|
|
port = rp if IPAddr.new("#{remote_network}").include?(rp['portAddress'])
|
|
end
|
|
unless port.empty?
|
|
res = @connection.post do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/routes"
|
|
req.headers['Content-Type'] = "application/vnd.org.midonet.Route-v1+json"
|
|
req.body = {
|
|
'dstNetworkAddr' => '0.0.0.0',
|
|
'dstNetworkLength' => '0',
|
|
'nextHopPort' => port['id'],
|
|
'nextHopGateway' => bgp_neighbor['ip_address'],
|
|
'srcNetworkAddr' => '0.0.0.0',
|
|
'srcNetworkLength' => '0',
|
|
'type' => 'Normal',
|
|
'weight' => '100'
|
|
}.to_json
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
def call_advertise_bgp_network( provider_router_id, bgp_advertised_network )
|
|
subnet_address, subnet_length = bgp_advertised_network.split("/")
|
|
res = @connection.post do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/bgp_networks"
|
|
req.headers['Content-Type'] = "application/vnd.org.midonet.BgpNetwork-v1+json"
|
|
req.body = { 'subnetAddress' => subnet_address,
|
|
'subnetLength' => subnet_length }.to_json
|
|
end
|
|
end
|
|
|
|
def call_delete_bgp_peer(bgp_peer_id)
|
|
res = @connection.delete do |req|
|
|
req.url "/midonet-api/bgp_peers/#{bgp_peer_id}"
|
|
end
|
|
end
|
|
|
|
def call_delete_bgp_network(bgp_network)
|
|
res = @connection.delete do |req|
|
|
req.url "/midonet-api/bgp_networks/#{bgp_network}"
|
|
end
|
|
end
|
|
|
|
def call_delete_bgp_route(route)
|
|
res = @connection.delete do |req|
|
|
req.url "/midonet-api/routes/#{route}"
|
|
end
|
|
end
|
|
|
|
def call_get_bgp_peers(provider_router_id)
|
|
res = @connection.get do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/bgp_peers"
|
|
end
|
|
output = JSON.parse(res.body)
|
|
return output
|
|
end
|
|
|
|
def call_get_router_ports(provider_router_id)
|
|
res = @connection.get do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/ports"
|
|
end
|
|
output = JSON.parse(res.body)
|
|
return output
|
|
end
|
|
|
|
def call_get_bgp_routes(provider_router_id)
|
|
res = @connection.get do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/routes"
|
|
end
|
|
output = JSON.parse(res.body)
|
|
return output
|
|
end
|
|
|
|
def call_get_bgp_networks(provider_router_id)
|
|
res = @connection.get do |req|
|
|
req.url "/midonet-api/routers/#{provider_router_id}/bgp_networks"
|
|
end
|
|
output = JSON.parse(res.body)
|
|
return output
|
|
end
|
|
|
|
private :call_get_provider_router
|
|
:define_connection
|
|
:call_assign_asn
|
|
:call_add_bgp_peer
|
|
:call_get_bgp_networks
|
|
:call_get_bgp_peers
|
|
:call_delete_bgp_network
|
|
:call_delete_bgp_peer
|
|
:call_advertise_bgp_network
|
|
|
|
end
|