Added MidoNet gateway functionality for BGP uplink configuration
MidoNet utilizes the Border Gateway Protocol (BGP) for external connectivity. For production deployments it is strongly recommended to use BGP due to it’s scalability and redundancy. For demo or POC environments, alternatively static routing can be used. This patch adds a new Puppet custom type and provider to let puppet deployers to automatically set up BGP uplink configuration: midonet_gateway { $::hostname: ensure => present, midonet_api_url => 'http://127.0.0.1:8080/midonet-api', username => 'admin', password => 'admin', interface => 'testgateway', local_as => '64512', bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30'}, remote_peers => [{ 'as' => '64513', 'ip' => '198.51.100.1'}, { 'as' => '64513', 'ip' => '203.0.113.1'}], advertise_net => [{ 'net_prefix' => '192.0.2.0', 'net_length' => '24' }] } Change-Id: Id362e519d12eefe5900060d6b02f92fbf6c54e08
This commit is contained in:
parent
6a54710212
commit
0ce39c5271
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@
|
||||
**/pkg/
|
||||
/spec/reports/
|
||||
/spec/fixtures/modules/*
|
||||
/spec/fixtures/manifests/*
|
||||
/test/tmp/
|
||||
/test/version_tmp/
|
||||
/tmp/
|
||||
|
278
lib/puppet/provider/midonet_gateway/midonet_api_caller.rb
Normal file
278
lib/puppet/provider/midonet_gateway/midonet_api_caller.rb
Normal file
@ -0,0 +1,278 @@
|
||||
require 'uri'
|
||||
require 'faraday'
|
||||
|
||||
if RUBY_VERSION == '1.8.7'
|
||||
require 'rubygems'
|
||||
require 'json'
|
||||
end
|
||||
|
||||
Puppet::Type.type(:midonet_gateway).provide(:midonet_api_caller) do
|
||||
|
||||
def create
|
||||
define_connection(resource[:midonet_api_url])
|
||||
# For each remote BGP peer, create a virtual port on
|
||||
# the MidoNet Provider Router that is going to be used
|
||||
# for the BGP communication. Connection to midonet api
|
||||
# is assumed
|
||||
|
||||
router_id = call_get_provider_router()[0]['id']
|
||||
|
||||
message = Hash.new
|
||||
message['portAddress'] = resource[:bgp_port]["port_address"]
|
||||
message['networkAddress'] = resource[:bgp_port]["net_prefix"]
|
||||
message['networkLength'] = resource[:bgp_port]["net_length"].to_i
|
||||
message['type'] = "Router"
|
||||
|
||||
port = call_create_uplink_port(router_id, message)
|
||||
port_id = port[0]['id']
|
||||
|
||||
# Configure BGP on the virtual ports. Port is
|
||||
# assumed created
|
||||
|
||||
resource[:remote_peers].each do |rp|
|
||||
message = Hash.new
|
||||
message['localAS'] = resource[:local_as]
|
||||
message['peerAS'] = rp["as"]
|
||||
message['peerAddr'] = rp["ip"]
|
||||
|
||||
call_add_bgp_to_port(port_id, message)
|
||||
end
|
||||
|
||||
# In order to provide external connectivity for hosted
|
||||
# virtual machines, the floating IP network has to be
|
||||
# advertised to the BGP peers. BGP connection is assumed created
|
||||
bgp_connections = call_get_bgp_connections(port_id)
|
||||
|
||||
#TODO(carmela): make this modification more elegant... or whatever
|
||||
advertise_networks = resource[:advertise_net]
|
||||
if advertise_networks.class == Hash
|
||||
advertise_networks = [advertise_networks]
|
||||
end
|
||||
bgp_connections.each do |bgp_c|
|
||||
advertise_networks.each do |net|
|
||||
message = Hash.new
|
||||
message['nwPrefix'] = net["net_prefix"]
|
||||
message['prefixLength'] = net["net_length"]
|
||||
|
||||
bgp_id = bgp_c["id"]
|
||||
call_advertise_route_to_bgp(bgp_id, message)
|
||||
end
|
||||
end
|
||||
|
||||
# Bind the MidoNet Provider Router’s virtual ports to
|
||||
# the physical network interfaces on the Gateway Nodes.
|
||||
# Host and port are assumed created. Interface name should
|
||||
# be an string
|
||||
host_id = call_get_host()[0]['id']
|
||||
|
||||
message = Hash.new
|
||||
message['interfaceName'] = resource[:interface]
|
||||
message['portId'] = port_id
|
||||
|
||||
call_bind_port_to_interface(host_id, message)
|
||||
|
||||
# Configure a stateful port group
|
||||
spg = call_get_stateful_port_group()
|
||||
if spg.empty?
|
||||
message = Hash.new
|
||||
message['name'] = "uplink-spg"
|
||||
message['stateful'] = "true"
|
||||
#TODO(carmela): replace with keystone call and tenant id
|
||||
message['tenantId'] = "4486908d-8e15-4f01-b3b4-86f9def0fa04"
|
||||
|
||||
spg = call_create_stateful_port_group(message)
|
||||
end
|
||||
|
||||
# Add the ports to the port group
|
||||
message = Hash.new
|
||||
message['portId'] = port_id
|
||||
|
||||
call_add_ports_to_port_group(spg[0]["id"], message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
define_connection(resource[:midonet_api_url])
|
||||
|
||||
router_id = call_get_provider_router()[0]['id']
|
||||
port_address = resource[:bgp_port]['port_address']
|
||||
|
||||
port = call_get_uplink_port(router_id, port_address)
|
||||
port_id = port[0]["id"]
|
||||
|
||||
# Delete the stateful port group
|
||||
# TODO(carmela): delete only in case is the last port on the port group
|
||||
# port_group_id = call_get_stateful_port_group()[0]['id']
|
||||
# call_delete_stateful_port_group(port_group_id)
|
||||
|
||||
# Delete uplink port
|
||||
call_delete_uplink_port(port_id)
|
||||
end
|
||||
|
||||
def exists?
|
||||
define_connection(resource[:midonet_api_url])
|
||||
|
||||
router = call_get_provider_router()
|
||||
if router.empty?
|
||||
raise 'MidoNet Provider Router does not exist. We cannot create uplink ports'
|
||||
end
|
||||
|
||||
host = call_get_host()
|
||||
if host.empty?
|
||||
raise 'There is no MidoNet agent running on this host'
|
||||
end
|
||||
|
||||
uplink_port = call_get_uplink_port(router[0]['id'], resource[:bgp_port]['port_address'])
|
||||
if uplink_port.empty?
|
||||
return false
|
||||
end
|
||||
|
||||
return 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.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)
|
||||
return output.select { |name| name['name'] == resource[:router]}
|
||||
end
|
||||
|
||||
def call_get_stateful_port_group()
|
||||
res = @connection.get do |req|
|
||||
req.url "/midonet-api/port_groups"
|
||||
end
|
||||
output = JSON.parse(res.body)
|
||||
return output.select { |name| name['name'] == 'uplink-spg'}
|
||||
|
||||
end
|
||||
|
||||
def call_get_uplink_port(router_id, port_address)
|
||||
res = @connection.get do |req|
|
||||
req.url "/midonet-api/routers/#{router_id}/ports"
|
||||
end
|
||||
output = JSON.parse(res.body)
|
||||
return output.select { |port| port['portAddress'] == port_address }
|
||||
|
||||
end
|
||||
|
||||
def call_get_host()
|
||||
res = @connection.get do |req|
|
||||
req.url "/midonet-api/hosts"
|
||||
end
|
||||
output = JSON.parse(res.body)
|
||||
return output.select{ |host| host['name'] == resource[:hostname].to_s }
|
||||
end
|
||||
|
||||
def call_create_uplink_port(router_id, message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/routers/#{router_id}/ports"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.Port-v2+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
return call_get_uplink_port(router_id, message["portAddress"])
|
||||
end
|
||||
|
||||
def call_delete_uplink_port(port_id)
|
||||
res = @connection.delete do |req|
|
||||
req.url "/midonet-api/ports/#{port_id}"
|
||||
end
|
||||
end
|
||||
|
||||
def call_add_bgp_to_port(port_id, message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/ports/#{port_id}/bgps"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.Bgp-v1+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def call_get_bgp_connections(port_id)
|
||||
res = @connection.get do |req|
|
||||
req.url "/midonet-api/ports/#{port_id}/bgps"
|
||||
end
|
||||
output = JSON.parse(res.body)
|
||||
return output
|
||||
end
|
||||
|
||||
def call_advertise_route_to_bgp(bgp_id, message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/bgps/#{bgp_id}/ad_routes"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.AdRoute-v1+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def call_bind_port_to_interface(host_id, message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/hosts/#{host_id}/ports"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.HostInterfacePort-v1+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def call_create_stateful_port_group(message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/port_groups"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.PortGroup-v1+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
return call_get_stateful_port_group()
|
||||
end
|
||||
|
||||
def call_add_ports_to_port_group(port_group_id, message)
|
||||
res = @connection.post do |req|
|
||||
req.url "/midonet-api/port_groups/#{port_group_id}/ports"
|
||||
req.headers['Content-Type'] = "application/vnd.org.midonet.PortGroupPort-v1+json"
|
||||
req.body = message.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def call_delete_stateful_port_group(port_group_id)
|
||||
res = @connection.delete do |req|
|
||||
req.url "/midonet-api/port_groups/#{port_group_id}"
|
||||
end
|
||||
end
|
||||
|
||||
private :call_add_bgp_to_port
|
||||
:call_add_ports_to_port_group
|
||||
:call_advertise_route_to_bgp
|
||||
:call_bind_port_to_interface
|
||||
:call_create_stateful_port_group
|
||||
:call_create_uplink_port
|
||||
:call_delete_stateful_port_group
|
||||
:call_delete_uplink_port
|
||||
:call_get_bgp_connections
|
||||
:call_get_host
|
||||
:call_get_stateful_port_group
|
||||
:call_get_provider_router
|
||||
:call_get_uplink_port
|
||||
:define_connection
|
||||
end
|
155
lib/puppet/type/midonet_gateway.rb
Normal file
155
lib/puppet/type/midonet_gateway.rb
Normal file
@ -0,0 +1,155 @@
|
||||
require 'uri'
|
||||
require 'facter'
|
||||
|
||||
Puppet::Type.newtype(:midonet_gateway) do
|
||||
@doc = %q{BGP Uplink Configuration
|
||||
|
||||
Example:
|
||||
|
||||
midonet_gateway {'hostname':
|
||||
midonet_api_url => 'http://controller:8080',
|
||||
username => 'admin',
|
||||
password => 'admin',
|
||||
tenant_name => 'admin',
|
||||
interface => 'eth1',
|
||||
local_as => '64512',
|
||||
bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30'},
|
||||
remote_peers => [ { 'as' => '64513', 'ip' => '198.51.100.1' },
|
||||
{ 'as' => '64513', 'ip' => '203.0.113.1' } ],
|
||||
advertise_net => [ { 'net_prefix' => '192.0.2.0', 'net_length' => '24' } ]
|
||||
}
|
||||
}
|
||||
|
||||
ensurable
|
||||
|
||||
autorequire(:package) do ['midolman'] end
|
||||
|
||||
newparam(:hostname, :namevar => true) do
|
||||
desc 'Hostname of the host that will act as gateway in a MidoNet managed cloud'
|
||||
# Regex obtained from StackOverflow question:
|
||||
# http://stackoverflow.com/questions/1418423/the-hostname-regex
|
||||
validate do |value|
|
||||
unless value =~ /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/
|
||||
raise ArgumentError, "'%s' is not a valid hostname" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:midonet_api_url) do
|
||||
desc 'MidoNet API endpoint to connect to'
|
||||
validate do |value|
|
||||
unless value =~ /\A#{URI::regexp(['http', 'https'])}\z/
|
||||
raise ArgumentError, "'%s' is not a valid URI" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:username) do
|
||||
desc 'Username of the admin user in keystone'
|
||||
defaultto 'admin'
|
||||
validate do |value|
|
||||
unless value =~ /\w+$/
|
||||
raise ArgumentError, "'%s' is not a valid username" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:password) do
|
||||
desc 'Password of the admin user in keystone'
|
||||
defaultto 'admin'
|
||||
validate do |value|
|
||||
unless value =~ /\w+$/
|
||||
raise ArgumentError, "'%s' is not a valid password" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:tenant_name) do
|
||||
desc 'Tenant name of the admin user'
|
||||
defaultto 'admin'
|
||||
validate do |value|
|
||||
unless value =~ /\w+$/
|
||||
raise ArgumentError, "'%s' is not a tenant name" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:interface) do
|
||||
desc "Physical interface where the MidoNet Provider Router's port is binded to"
|
||||
defaultto 'eth0'
|
||||
validate do |value|
|
||||
unless value =~ /\w+$/
|
||||
raise ArgumentError, "'%s' is not a valid interface" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:local_as) do
|
||||
desc "Local AS number"
|
||||
validate do |value|
|
||||
unless value =~ /\d{5}/
|
||||
raise ArgumentError, "'%s' is not a valid AS" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:remote_peers) do
|
||||
desc "#to be filled"
|
||||
validate do |value|
|
||||
value.each do |rp|
|
||||
unless rp["as"] =~ /\d{5}/
|
||||
raise ArgumentError, "'%s' is not a valid AS name" % rp["as"]
|
||||
end
|
||||
unless rp["ip"] =~ /^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$/
|
||||
raise ArgumentError, "'%s' is not a valid IP address" % rp["ip"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:bgp_port) do
|
||||
desc "#to be filled"
|
||||
validate do |value|
|
||||
[:port_address, :net_prefix, :net_length].all? {|key| value.key? key}
|
||||
unless value["port_address"] =~ /^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$/
|
||||
raise ArgumentError, "'%s' is not a valid IP address" % value["port_address"]
|
||||
end
|
||||
unless value["net_prefix"] =~ /^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$/
|
||||
raise ArgumentError, "'%s' is not a valid IPv4 network address" % value["net_prefix"]
|
||||
end
|
||||
unless value["net_length"] =~ /\d{2}/
|
||||
raise ArgumentError, "'%s' is not a valid network prefix length" % value["net_length"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:router) do
|
||||
desc "The MidoNet's internal Provider router that acts as the gateway router of the cloud"
|
||||
defaultto 'MidoNet Provider Router'
|
||||
validate do |value|
|
||||
unless value =~ /\w+$/
|
||||
raise ArgumentError, "'%s' is not a valid router name" % value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:advertise_net) do
|
||||
desc 'Floating IP network to be avertised to the BGP peers'
|
||||
defaultto []
|
||||
validate do |value|
|
||||
if value.class == Hash
|
||||
value = [value]
|
||||
end
|
||||
value.each do |an|
|
||||
unless an["net_prefix"] =~ /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/
|
||||
raise ArgumentError, "'%s' is not a valid network prefix" % an["net_prefix"]
|
||||
end
|
||||
unless an["net_length"] =~ /\d{2}/
|
||||
raise ArgumentError, "'%s' is not a valid network prefix length" % an["net_length"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2,15 +2,29 @@ require 'spec_helper_acceptance'
|
||||
|
||||
describe 'midonet all-in-one' do
|
||||
|
||||
context 'default parameters' do
|
||||
it 'should work with no errors' do
|
||||
pp = <<-EOS
|
||||
class { 'midonet': }
|
||||
EOS
|
||||
context 'default parameters' do
|
||||
it 'should work with no errors' do
|
||||
pp = <<-EOS
|
||||
class { 'midonet': } ->
|
||||
exec { "/sbin/ip tuntap add mode tap testgateway": } ->
|
||||
exec { "/usr/bin/midonet-cli -e 'create router name \\"MidoNet Provider Router\\"'": } ->
|
||||
midonet_gateway { $::hostname:
|
||||
ensure => present,
|
||||
midonet_api_url => 'http://127.0.0.1:8080/midonet-api',
|
||||
username => 'admin',
|
||||
password => 'admin',
|
||||
interface => 'testgateway',
|
||||
local_as => '64512',
|
||||
bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30'},
|
||||
remote_peers => [{ 'as' => '64513', 'ip' => '198.51.100.1'},
|
||||
{ 'as' => '64513', 'ip' => '203.0.113.1'}],
|
||||
advertise_net => [{ 'net_prefix' => '192.0.2.0', 'net_length' => '24' }]
|
||||
}
|
||||
EOS
|
||||
|
||||
# Run it twice for test the idempotency
|
||||
apply_manifest(pp)
|
||||
apply_manifest(pp)
|
||||
end
|
||||
end
|
||||
# Run it twice for test the idempotency
|
||||
apply_manifest(pp)
|
||||
apply_manifest(pp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,140 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Puppet::Type.type(:midonet_gateway).provider(:midonet_api_caller) do
|
||||
|
||||
let(:provider) { described_class.new(resource) }
|
||||
|
||||
let(:resource) { Puppet::Type.type(:midonet_gateway).new(
|
||||
{
|
||||
:ensure => :present,
|
||||
:hostname => 'compute.midonet',
|
||||
:midonet_api_url => 'http://controller:8080',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:interface => 'eth0',
|
||||
:local_as => '64512',
|
||||
:bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30' },
|
||||
:remote_peers => [ { 'as' => '64513', 'ip' => '198.51.100.1' },
|
||||
{ 'as' => '64513', 'ip' => '203.0.113.1' } ],
|
||||
:advertise_net => [ { 'net_prefix' => '192.0.2.0', 'net_length' => '24' } ]
|
||||
}
|
||||
)}
|
||||
|
||||
describe 'BGP configuration happy path' do
|
||||
# - Create virtual ports for each remote BGP peer
|
||||
# - Configure BGP on the virtual ports or remove config if needed
|
||||
# - Advertise routes
|
||||
# - Bind virtual ports to physical network interfaces
|
||||
# - Configure stateful port group and delete it if needed
|
||||
# - Add ports to the port group and remove them if needed
|
||||
|
||||
let(:routers) {
|
||||
[
|
||||
{
|
||||
"id" => "e6a53892-03bf-4f16-8212-e4d76ad204e3",
|
||||
"name" => "MidoNet Provider Router"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:ports) {
|
||||
[
|
||||
{
|
||||
"hostId" => "b3f2f63e-02a6-459a-af0f-44eeac441a09",
|
||||
"interfaceName" => "eth0",
|
||||
"id" => "20b169b1-ec0a-4639-8479-44b63e016935"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:bgps) {
|
||||
[
|
||||
{
|
||||
"id" => "4a5e4356-3417-4c60-9cf8-7516aedb7067",
|
||||
"localAS" => "64512",
|
||||
"peerAS" => "64513",
|
||||
"peerAddr" => "198.51.100.1",
|
||||
"portId" => "f9e61b88-0a26-4d56-8f47-eb5da37225e0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:port_groups) {
|
||||
[
|
||||
{
|
||||
"id" => "711401b7-bf6f-4afd-8ab2-94b4342a0310",
|
||||
"name" => "uplink-spg",
|
||||
"stateful" => "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:hosts) {
|
||||
[
|
||||
{
|
||||
"id" => "b3f2f63e-02a6-459a-af0f-44eeac441a09",
|
||||
"name" => "compute.midonet"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
before :each do
|
||||
allow(provider).to receive(:call_create_uplink_port).and_return(ports)
|
||||
allow(provider).to receive(:call_get_provider_router).and_return(routers)
|
||||
allow(provider).to receive(:call_get_host_id).and_return(hosts[0]['id'])
|
||||
allow(provider).to receive(:call_get_host).and_return(hosts)
|
||||
allow(provider).to receive(:call_add_bgp_to_port)
|
||||
allow(provider).to receive(:call_get_bgp_connections).and_return(bgps)
|
||||
allow(provider).to receive(:call_advertise_route_to_bgp)
|
||||
allow(provider).to receive(:call_bind_port_to_interface)
|
||||
allow(provider).to receive(:call_get_stateful_port_group).and_return(port_groups)
|
||||
allow(provider).to receive(:call_add_ports_to_port_group)
|
||||
allow(provider).to receive(:call_delete_uplink_port).and_return(ports)
|
||||
allow(provider).to receive(:call_unbind_port_from_interface)
|
||||
allow(provider).to receive(:call_remove_ports_from_port_group)
|
||||
allow(provider).to receive(:call_get_uplink_port).and_return(ports)
|
||||
allow(provider).to receive(:call_get_token).and_return('thisisafaketoken')
|
||||
end
|
||||
|
||||
it 'creates virtual ports for each remote BGP peer, advertises routes,
|
||||
binds virtual ports, configures stateful port group and adds ports to it' do
|
||||
# Expectations over the 'create' call
|
||||
expect(provider).to receive(:call_get_provider_router)
|
||||
expect(provider).to receive(:call_create_uplink_port).with(routers[0]['id'], {'portAddress' => resource[:bgp_port]['port_address'],
|
||||
'networkAddress' => resource[:bgp_port]['net_prefix'],
|
||||
'networkLength' => resource[:bgp_port]['net_length'].to_i,
|
||||
'type' => 'Router'})
|
||||
expect(provider).to receive(:call_get_bgp_connections).with(ports[0]['id'])
|
||||
expect(provider).to receive(:call_add_bgp_to_port).with(ports[0]['id'], {'localAS' => resource[:local_as],
|
||||
'peerAS' => resource[:remote_peers][0]['as'],
|
||||
'peerAddr' => resource[:remote_peers][0]['ip']}).once
|
||||
expect(provider).to receive(:call_add_bgp_to_port).with(ports[0]['id'], {'localAS' => resource[:local_as],
|
||||
'peerAS' => resource[:remote_peers][1]['as'],
|
||||
'peerAddr' => resource[:remote_peers][1]['ip']}).once
|
||||
expect(provider).to receive(:call_advertise_route_to_bgp).with(bgps[0]['id'], {'nwPrefix' => resource[:advertise_net][0]['net_prefix'],
|
||||
'prefixLength' => resource[:advertise_net][0]['net_length']}).once
|
||||
expect(provider).to receive(:call_bind_port_to_interface).with(hosts[0]['id'], {'interfaceName' => resource[:interface],
|
||||
'portId' => '20b169b1-ec0a-4639-8479-44b63e016935'})
|
||||
expect(provider).not_to receive(:call_create_stateful_port_group)
|
||||
expect(provider).to receive(:call_get_stateful_port_group)
|
||||
expect(provider).to receive(:call_add_ports_to_port_group).with(port_groups[0]['id'], {'portId' => '20b169b1-ec0a-4639-8479-44b63e016935'})
|
||||
provider.create
|
||||
end
|
||||
|
||||
it 'deletes uplink port, port_group, and unconfigures BGP' do
|
||||
# Expectations over the 'destroy' call
|
||||
expect(provider).to receive(:call_get_provider_router)
|
||||
expect(provider).to receive(:call_get_uplink_port)
|
||||
expect(provider).to receive(:call_delete_uplink_port).with(ports[0]['id'])
|
||||
provider.destroy
|
||||
end
|
||||
|
||||
it 'exists with default method returns' do
|
||||
# Expectations over the 'exists' call
|
||||
expect(provider).to receive(:call_get_provider_router).and_return(routers)
|
||||
expect(provider).to receive(:call_get_host).and_return(hosts)
|
||||
expect(provider).to receive(:call_get_uplink_port).with(routers[0]['id'], resource[:bgp_port]['port_address']).and_return(ports)
|
||||
expect(provider.exists?).to eq true
|
||||
end
|
||||
end
|
||||
end
|
@ -16,7 +16,6 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
)}
|
||||
|
||||
|
||||
describe 'host registry happy path' do
|
||||
# - Single tunnelzone zones
|
||||
# - Host registered
|
||||
@ -62,7 +61,6 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
provider.create
|
||||
end
|
||||
|
||||
|
||||
it 'unregisters the host successfully' do
|
||||
# Expectations over the 'destroy' call
|
||||
expect(provider).to receive(:call_get_tunnelzone)
|
||||
@ -83,6 +81,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:tzones) {
|
||||
[
|
||||
{
|
||||
@ -92,6 +91,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it 'creates the tunnelzone and the host' do
|
||||
allow(provider).to receive(:call_get_tunnelzone).and_return([])
|
||||
allow(provider).to receive(:call_create_tunnelzone).and_return(tzones)
|
||||
@ -116,6 +116,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:host_to_unregister) {
|
||||
[
|
||||
{
|
||||
@ -124,6 +125,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:host_left_in_tunnelzone) {
|
||||
[
|
||||
{
|
||||
@ -182,6 +184,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it 'should raise an exception' do
|
||||
allow(provider).to receive(:call_get_tunnelzone).and_return(tzones)
|
||||
allow(provider).to receive(:call_get_token).and_return('thisisafaketoken')
|
||||
@ -210,6 +213,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it 'should not fail' do
|
||||
allow(provider).to receive(:call_get_tunnelzone).and_return(tzones)
|
||||
allow(provider).to receive(:call_get_host).and_return([])
|
||||
@ -231,6 +235,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let(:hosts) {
|
||||
[
|
||||
{
|
||||
@ -239,6 +244,7 @@ describe Puppet::Type.type(:midonet_host_registry).provider(:midonet_api_caller)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it 'should not fail' do
|
||||
allow(provider).to receive(:call_get_tunnelzone).and_return(tzones)
|
||||
allow(provider).to receive(:call_get_host).and_return(hosts)
|
||||
|
174
spec/unit/puppet/type/midonet_gateway_spec.rb
Normal file
174
spec/unit/puppet/type/midonet_gateway_spec.rb
Normal file
@ -0,0 +1,174 @@
|
||||
require 'spec_helper'
|
||||
require 'puppet'
|
||||
require 'puppet/type/midonet_gateway'
|
||||
require 'facter'
|
||||
|
||||
describe Puppet::Type::type(:midonet_gateway) do
|
||||
|
||||
context 'on default values' do
|
||||
let(:resource) do
|
||||
Puppet::Type::type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://controller:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:interface => 'eth0',
|
||||
:local_as => '64512',
|
||||
:bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30'},
|
||||
:remote_peers => [ { 'as' => '64513', 'ip' => '198.51.100.1' },
|
||||
{ 'as' => '64513', 'ip' => '203.0.113.1' } ],
|
||||
:advertise_net => [ { 'net_prefix' => '192.0.2.0', 'net_length' => '24' } ])
|
||||
|
||||
end
|
||||
|
||||
it 'assign the default values' do
|
||||
expect(resource[:username]).to eq 'admin'
|
||||
expect(resource[:password]).to eq 'admin'
|
||||
expect(resource[:tenant_name]).to eq 'admin'
|
||||
expect(resource[:interface]).to eq 'eth0'
|
||||
expect(resource[:router]).to eq 'MidoNet Provider Router'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid hostname' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => '_invalid_hostname.local',
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin')
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid api url' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => '87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin')
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on tenant_name valid value' do
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:tenant_name => 'midokura')
|
||||
end
|
||||
|
||||
it 'assign to it' do
|
||||
expect(resource[:tenant_name]).to eq 'midokura'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on valid interface name' do
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:interface => 'eth0')
|
||||
end
|
||||
|
||||
it 'assign to it' do
|
||||
expect(resource[:interface]).to eq 'eth0'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on valid local AS name' do
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:local_as => '64512')
|
||||
end
|
||||
|
||||
it 'assign to it' do
|
||||
expect(resource[:local_as]).to eq '64512'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid local AS name' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:local_as => 'fake')
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid BGP port' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:bgp_port => { 'port_address' => '_198.51.100.2',
|
||||
'net_prefix' => '198.51.100.0',
|
||||
'net_length' => '30' })
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid remote BGP peers AS name and IP' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:remote_peers => [ { 'as' => 'fake',
|
||||
'ip' => '_198.51.100.1' } ])
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on valid router name' do
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:router => 'MidoNet Provider Router')
|
||||
end
|
||||
|
||||
it 'assign to it' do
|
||||
expect(resource[:router]).to eq 'MidoNet Provider Router'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on invalid advertise network' do
|
||||
it do
|
||||
expect {
|
||||
Puppet::Type.type(:midonet_gateway).new(
|
||||
:hostname => Facter['hostname'].value,
|
||||
:midonet_api_url => 'http://87.23.43.2:8080/midonet-api',
|
||||
:username => 'admin',
|
||||
:password => 'admin',
|
||||
:advertise_net => [ { 'net_prefix' => '_192.0.2.0',
|
||||
'net_length' => '24' } ] )
|
||||
}.to raise_error(Puppet::ResourceError)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user