Adds GW type/class (both BGP and static uplinks)
Change-Id: I52a6cce14566469d67c07fd488972a245bc7b73f
This commit is contained in:
parent
05023f16bb
commit
f0324311a0
369
files/gateway/functions
Normal file
369
files/gateway/functions
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2016 Midokura SARL
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# Useful common functions are defined here. Some of them were shamelessly
|
||||||
|
# stolen from devstack.
|
||||||
|
|
||||||
|
# Save trace setting
|
||||||
|
XTRACE=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
|
||||||
|
# Control Functions
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
# Prints line number and "message" in warning format
|
||||||
|
# warn $LINENO "message"
|
||||||
|
function warn {
|
||||||
|
local exitcode=$?
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
local msg="[WARNING] ${BASH_SOURCE[2]}:$1 $2"
|
||||||
|
echo $msg 1>&2;
|
||||||
|
if [[ -n ${LOGDIR} ]]; then
|
||||||
|
echo $msg >> "${LOGDIR}/error.log"
|
||||||
|
fi
|
||||||
|
$xtrace
|
||||||
|
return $exitcode
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints backtrace info
|
||||||
|
# filename:lineno:function
|
||||||
|
# backtrace level
|
||||||
|
function backtrace {
|
||||||
|
local level=$1
|
||||||
|
local deep=$((${#BASH_SOURCE[@]} - 1))
|
||||||
|
echo "[Call Trace]"
|
||||||
|
while [ $level -le $deep ]; do
|
||||||
|
echo "${BASH_SOURCE[$deep]}:${BASH_LINENO[$deep-1]}:${FUNCNAME[$deep-1]}"
|
||||||
|
deep=$((deep - 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints line number and "message" in error format
|
||||||
|
# err $LINENO "message"
|
||||||
|
function err {
|
||||||
|
local exitcode=$?
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
local msg="[ERROR] ${BASH_SOURCE[2]}:$1 $2"
|
||||||
|
echo $msg 1>&2;
|
||||||
|
if [[ -n ${LOGDIR} ]]; then
|
||||||
|
echo $msg >> "${LOGDIR}/error.log"
|
||||||
|
fi
|
||||||
|
$xtrace
|
||||||
|
return $exitcode
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints line number and "message" then exits
|
||||||
|
# die $LINENO "message"
|
||||||
|
function die {
|
||||||
|
local exitcode=$?
|
||||||
|
set +o xtrace
|
||||||
|
local line=$1; shift
|
||||||
|
if [ $exitcode == 0 ]; then
|
||||||
|
exitcode=1
|
||||||
|
fi
|
||||||
|
backtrace 2
|
||||||
|
err $line "$*"
|
||||||
|
# Give buffers a second to flush
|
||||||
|
sleep 1
|
||||||
|
exit $exitcode
|
||||||
|
}
|
||||||
|
|
||||||
|
# Checks an environment variable is not set or has length 0 OR if the
|
||||||
|
# exit code is non-zero and prints "message" and exits
|
||||||
|
# NOTE: env-var is the variable name without a '$'
|
||||||
|
# die_if_not_set $LINENO env-var "message"
|
||||||
|
function die_if_not_set {
|
||||||
|
local exitcode=$?
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
local line=$1; shift
|
||||||
|
local evar=$1; shift
|
||||||
|
if ! is_set $evar || [ $exitcode != 0 ]; then
|
||||||
|
die $line "$*"
|
||||||
|
fi
|
||||||
|
$xtrace
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test if the named environment variable is set and not zero length
|
||||||
|
# is_set env-var
|
||||||
|
function is_set {
|
||||||
|
local var=\$"$1"
|
||||||
|
eval "[ -n \"$var\" ]" # For ex.: sh -c "[ -n \"$var\" ]" would be better, but several exercises depends on this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Package Functions
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
# Wrapper for ``apt-get``
|
||||||
|
# apt_get operation package [package ...]
|
||||||
|
function apt_get {
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive \
|
||||||
|
apt-get --option "Dpkg::Options::=--force-confnew" --assume-yes "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update package repository
|
||||||
|
# Uses globals ``NO_UPDATE_REPOS``, ``REPOS_UPDATED``, ``RETRY_UPDATE``
|
||||||
|
# install_package package [package ...]
|
||||||
|
function update_package_repo {
|
||||||
|
NO_UPDATE_REPOS=${NO_UPDATE_REPOS:-False}
|
||||||
|
REPOS_UPDATED=${REPOS_UPDATED:-False}
|
||||||
|
|
||||||
|
if [[ "$NO_UPDATE_REPOS" = "True" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
if [[ "$REPOS_UPDATED" != "True" ]]; then
|
||||||
|
# if there are transient errors pulling the updates, that's fine.
|
||||||
|
# It may be secondary repositories that we don't really care about.
|
||||||
|
apt_get update || /bin/true
|
||||||
|
REPOS_UPDATED=True
|
||||||
|
fi
|
||||||
|
$xtrace
|
||||||
|
}
|
||||||
|
|
||||||
|
# Package installer
|
||||||
|
# install_package package [package ...]
|
||||||
|
function install_package {
|
||||||
|
update_package_repo
|
||||||
|
apt_get install --no-install-recommends "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to tell if a package is installed
|
||||||
|
# is_package_installed package [package ...]
|
||||||
|
function is_package_installed {
|
||||||
|
dpkg -s "$@" > /dev/null 2> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# System Functions
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
# Service wrapper to stop services
|
||||||
|
# stop_service service-name
|
||||||
|
# Checks if kmod is loaded
|
||||||
|
function is_kmod_loaded {
|
||||||
|
lsmod | grep -w $1 >& /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Process Functions
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
function is_screen_running {
|
||||||
|
type -p screen > /dev/null && screen -ls | egrep -q "[0-9]\.$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_screen {
|
||||||
|
local name=$1
|
||||||
|
screen -d -m -S $name -t shell -s /bin/bash
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Set a reasonable status bar
|
||||||
|
SCREEN_HARDSTATUS='%{= .} %-Lw%{= .}%> %n%f %t*%{= .}%+Lw%< %-=%{g}(%{d}%H/%l%{g})'
|
||||||
|
screen -r $name -X hardstatus alwayslastline "$SCREEN_HARDSTATUS"
|
||||||
|
screen -r $name -X setenv PROMPT_COMMAND /bin/true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Helper to launch a process in a named screen
|
||||||
|
# Uses globals ``CURRENT_LOG_TIME``, ``LOGDIR``,
|
||||||
|
# ``SERVICE_DIR``
|
||||||
|
# screen_process name "command-line"
|
||||||
|
# Run a command in a shell in a screen window
|
||||||
|
function screen_process {
|
||||||
|
local name=$1
|
||||||
|
local command="$2"
|
||||||
|
|
||||||
|
SERVICE_DIR=${SERVICE_DIR:-/tmp/status}
|
||||||
|
mkdir -p ${SERVICE_DIR}/${SCREEN_NAME}
|
||||||
|
|
||||||
|
# Append the process to the screen rc file
|
||||||
|
screen_rc ${SCREEN_NAME} "$name" "$command"
|
||||||
|
|
||||||
|
screen -S ${SCREEN_NAME} -X screen -t $name
|
||||||
|
|
||||||
|
if [[ -n ${LOGDIR} ]]; then
|
||||||
|
screen -S ${SCREEN_NAME} -p $name -X logfile ${LOGDIR}/${name}.log.${CURRENT_LOG_TIME}
|
||||||
|
screen -S ${SCREEN_NAME} -p $name -X log on
|
||||||
|
ln -sf ${LOGDIR}/${name}.log.${CURRENT_LOG_TIME} ${LOGDIR}/${name}.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# sleep to allow bash to be ready to be send the command - we are
|
||||||
|
# creating a new window in screen and then sends characters, so if
|
||||||
|
# bash isn't running by the time we send the command, nothing happens
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
NL=`echo -ne '\015'`
|
||||||
|
screen -S ${SCREEN_NAME} -p $name -X stuff "$command & echo \$! >$SERVICE_DIR/${SCREEN_NAME}/${name}.pid; fg || echo \"$name failed to start\" | tee \"$SERVICE_DIR/${SCREEN_NAME}/${name}.failure\"$NL"
|
||||||
|
}
|
||||||
|
|
||||||
|
# _run_process() is designed to be backgrounded by run_process() to simulate a
|
||||||
|
# fork. It includes the dirty work of closing extra filehandles and preparing log
|
||||||
|
# files to produce the same logs as screen_it(). The log filename is derived
|
||||||
|
# from the service name.
|
||||||
|
# _run_process service "command-line"
|
||||||
|
function _run_process {
|
||||||
|
local service=$1
|
||||||
|
local command="$2"
|
||||||
|
|
||||||
|
# Undo logging redirections and close the extra descriptors
|
||||||
|
exec 1>&3
|
||||||
|
exec 2>&3
|
||||||
|
exec 3>&-
|
||||||
|
exec 6>&-
|
||||||
|
|
||||||
|
local real_logfile="${LOGDIR}/${service}.log.${CURRENT_LOG_TIME}"
|
||||||
|
if [[ -n ${LOGDIR} ]]; then
|
||||||
|
exec 1>&"$real_logfile" 2>&1
|
||||||
|
ln -sf "$real_logfile" ${LOGDIR}/${service}.log
|
||||||
|
|
||||||
|
# Hack to get stdout from the Python interpreter for the logs.
|
||||||
|
export PYTHONUNBUFFERED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVICE_DIR=${SERVICE_DIR:-/tmp/status}
|
||||||
|
mkdir -p ${SERVICE_DIR}/${SCREEN_NAME}
|
||||||
|
setsid $command & echo $! > ${SERVICE_DIR}/${SCREEN_NAME}/${service}.pid
|
||||||
|
|
||||||
|
# Just silently exit this process
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run a single service under screen or directly
|
||||||
|
# If the command includes shell metachatacters (;<>*) it must be run using a shell
|
||||||
|
# run_process service "command-line"
|
||||||
|
function run_process {
|
||||||
|
local service=$1
|
||||||
|
local command="$2"
|
||||||
|
|
||||||
|
if [[ "$USE_SCREEN" = "True" ]]; then
|
||||||
|
screen_process "$service" "cd $TOP_DIR && $command"
|
||||||
|
else
|
||||||
|
# Spawn directly without screen
|
||||||
|
cd $TOP_DIR
|
||||||
|
_run_process "$service" "$command" &
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Screen rc file builder
|
||||||
|
# Uses globals ``SCREENRC``
|
||||||
|
# screen_rc service "command-line"
|
||||||
|
function screen_rc {
|
||||||
|
local screen=$1
|
||||||
|
SCREENRC=$DEVMIDO_DIR/$screen-screenrc
|
||||||
|
if [[ ! -e $SCREENRC ]]; then
|
||||||
|
# Name the screen session
|
||||||
|
echo "sessionname $screen" > $SCREENRC
|
||||||
|
# Set a reasonable statusbar
|
||||||
|
echo "hardstatus alwayslastline '$SCREEN_HARDSTATUS'" >> $SCREENRC
|
||||||
|
# Some distributions override PROMPT_COMMAND for the screen terminal type - turn that off
|
||||||
|
echo "setenv PROMPT_COMMAND /bin/true" >> $SCREENRC
|
||||||
|
echo "screen -t shell bash" >> $SCREENRC
|
||||||
|
fi
|
||||||
|
# If this service doesn't already exist in the screenrc file
|
||||||
|
if ! grep $1 $SCREENRC 2>&1 > /dev/null; then
|
||||||
|
NL=`echo -ne '\015'`
|
||||||
|
echo "screen -t $1 bash" >> $SCREENRC
|
||||||
|
echo "stuff \"$2$NL\"" >> $SCREENRC
|
||||||
|
|
||||||
|
if [[ -n ${LOGDIR} ]]; then
|
||||||
|
echo "logfile ${LOGDIR}/${1}.log.${CURRENT_LOG_TIME}" >>$SCREENRC
|
||||||
|
echo "log on" >>$SCREENRC
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set an option in an INI file
|
||||||
|
# iniset config-file section option value
|
||||||
|
function iniset {
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
local file=$1
|
||||||
|
local section=$2
|
||||||
|
local option=$3
|
||||||
|
local value=$4
|
||||||
|
|
||||||
|
[[ -z $section || -z $option ]] && return
|
||||||
|
|
||||||
|
if ! grep -q "^\[$section\]" "$file" 2>/dev/null; then
|
||||||
|
# Add section at the end
|
||||||
|
echo -e "\n[$section]" >>"$file"
|
||||||
|
fi
|
||||||
|
if ! ini_has_option "$file" "$section" "$option"; then
|
||||||
|
# Add it
|
||||||
|
sed -i -e "/^\[$section\]/ a\\
|
||||||
|
$option = $value
|
||||||
|
" "$file"
|
||||||
|
else
|
||||||
|
local sep=$(echo -ne "\x01")
|
||||||
|
# Replace it
|
||||||
|
sed -i -e '/^\['${section}'\]/,/^\[.*\]/ s'${sep}'^\('${option}'[ \t]*=[ \t]*\).*$'${sep}'\1'"${value}"${sep} "$file"
|
||||||
|
fi
|
||||||
|
$xtrace
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determinate is the given option present in the INI file
|
||||||
|
# ini_has_option config-file section option
|
||||||
|
function ini_has_option {
|
||||||
|
local xtrace=$(set +o | grep xtrace)
|
||||||
|
set +o xtrace
|
||||||
|
local file=$1
|
||||||
|
local section=$2
|
||||||
|
local option=$3
|
||||||
|
local line
|
||||||
|
|
||||||
|
line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" "$file")
|
||||||
|
$xtrace
|
||||||
|
[ -n "$line" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop_process {
|
||||||
|
local service=$1
|
||||||
|
|
||||||
|
# Kill via pid if we have one available
|
||||||
|
pkill -F $SERVICE_DIR/$SCREEN_NAME/$service.pid
|
||||||
|
rm $SERVICE_DIR/$SCREEN_NAME/$service.pid
|
||||||
|
screen -S $SCREEN_NAME -p $service -X kill
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# MN Functions
|
||||||
|
# ------------
|
||||||
|
|
||||||
|
# Wrapper for mn-conf command
|
||||||
|
# Uses globals ``ZOOKEEPER_HOSTS``
|
||||||
|
function configure_mn {
|
||||||
|
local value="$2"
|
||||||
|
|
||||||
|
# quote with "" only when necessary. we don't always quote because
|
||||||
|
# mn-conf complains for quoted booleans. eg. "false"
|
||||||
|
if [[ "${value}" =~ ":" || "${value}" = "" ]]; then
|
||||||
|
value="\"${value}\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# In some commands, mn-conf creates a local file, which requires root
|
||||||
|
# access. For simplicity, always call mn-conf with root for now.
|
||||||
|
echo $1 : "${value}" | MIDO_ZOOKEEPER_HOSTS="$ZOOKEEPER_HOSTS" sudo mn-conf set
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restore xtrace
|
||||||
|
$XTRACE
|
@ -1,309 +0,0 @@
|
|||||||
if RUBY_VERSION == '1.8.7'
|
|
||||||
require 'rubygems'
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'uri'
|
|
||||||
require 'faraday'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
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
|
|
||||||
remote_peers = resource[:remote_peers]
|
|
||||||
if remote_peers.class == Hash
|
|
||||||
remote_peers = [remote_peers]
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
||||||
# Add route for 'MidoNet Provider Router' uplink port
|
|
||||||
message = Hash.new
|
|
||||||
message['type'] = "Normal"
|
|
||||||
message['srcNetworkAddr'] = "0.0.0.0"
|
|
||||||
message['srcNetworkLength'] = 0
|
|
||||||
message['dstNetworkAddr'] = resource[:bgp_port]["net_prefix"]
|
|
||||||
message['dstNetworkLength'] = resource[:bgp_port]["net_length"].to_i
|
|
||||||
message['weight'] = 100
|
|
||||||
message['nextHopPort'] = port_id
|
|
||||||
|
|
||||||
call_add_route_for_uplink_port(router_id, message)
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
message['tenantId'] = call_get_tenant()
|
|
||||||
|
|
||||||
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_tenant()
|
|
||||||
res = @connection.get do |req|
|
|
||||||
req.url "/midonet-api/tenants"
|
|
||||||
end
|
|
||||||
return JSON.parse(res.body)[0]['id']
|
|
||||||
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_add_route_for_uplink_port(router_id, message)
|
|
||||||
res = @connection.post do |req|
|
|
||||||
req.url "/midonet-api/routers/#{router_id}/routes"
|
|
||||||
req.headers['Content-Type'] = "application/vnd.org.midonet.Route-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_add_route_for_uplink_port
|
|
||||||
: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_tenant
|
|
||||||
:call_get_uplink_port
|
|
||||||
:define_connection
|
|
||||||
end
|
|
217
lib/puppet/provider/midonet_gateway_bgp/midonet_api_caller.rb
Normal file
217
lib/puppet/provider/midonet_gateway_bgp/midonet_api_caller.rb
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
if RUBY_VERSION == '1.8.7'
|
||||||
|
require 'rubygems'
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'uri'
|
||||||
|
require 'faraday'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
Puppet::Type.type(:midonet_gateway_bgp).provide(:midonet_api_caller) do
|
||||||
|
|
||||||
|
def create
|
||||||
|
|
||||||
|
define_connection(resource[:midonet_api_url])
|
||||||
|
|
||||||
|
# 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_id, resource[:bgp_local_as_number]) unless asn == resource[:bgp_local_as_number]
|
||||||
|
|
||||||
|
# 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 - resource[:bgp_neighbors]
|
||||||
|
tba_peers = resource[:bgp_neighbors] - 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 - resource[:bgp_advertised_networks]
|
||||||
|
tba_bgp_networks = resource[:bgp_advertised_networks] - j
|
||||||
|
tba_bgp_networks.each { |a| call_advertise_bgp_network(provider_router_id, a) }
|
||||||
|
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(provider_router_id, bgp_network_id)
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def exists?
|
||||||
|
|
||||||
|
define_connection(resource[:midonet_api_url])
|
||||||
|
|
||||||
|
# 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"] == resource[:bgp_local_as_number])
|
||||||
|
# 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 == resource[:bgp_neighbors])
|
||||||
|
# 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 == resource[:bgp_advertised_networks])
|
||||||
|
|
||||||
|
# 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.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_id, bgp_local_as_number )
|
||||||
|
res = @connection.post do |req|
|
||||||
|
req.url "/midonet-api/routers"
|
||||||
|
req.headers['Content-Type'] = "application/vnd.org.midonet.Router-v3+json"
|
||||||
|
req.body = { 'id' => provider_router_id,
|
||||||
|
'asNumber' => bgp_local_as_number }.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/#{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_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/#{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_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_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.map { |e| e["id"] }
|
||||||
|
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
|
@ -1,159 +0,0 @@
|
|||||||
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+(.\d+)?$/
|
|
||||||
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+/
|
|
||||||
raise ArgumentError, "'%s' is not a valid AS" % value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
newparam(:remote_peers) do
|
|
||||||
desc "#to be filled"
|
|
||||||
defaultto []
|
|
||||||
validate do |value|
|
|
||||||
if value.class == Hash
|
|
||||||
value = [value]
|
|
||||||
end
|
|
||||||
value.each do |rp|
|
|
||||||
unless rp["as"] =~ /\d+/
|
|
||||||
raise ArgumentError, "'%s' is not a valid AS name" % rp["as"]
|
|
||||||
end
|
|
||||||
unless rp["ip"] =~ /^(([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 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"] =~ /^(([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 IP address" % value["port_address"]
|
|
||||||
end
|
|
||||||
unless value["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 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 IPv4 network address" % 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
|
|
||||||
|
|
150
lib/puppet/type/midonet_gateway_bgp.rb
Normal file
150
lib/puppet/type/midonet_gateway_bgp.rb
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
require 'uri'
|
||||||
|
require 'facter'
|
||||||
|
|
||||||
|
Puppet::Type.newtype(:midonet_gateway_bgp) do
|
||||||
|
@doc = %q{This type is used to configure a gateway node, considering the
|
||||||
|
uplink is of type BGP. What it does:
|
||||||
|
1) Assign an AS number to the edge router
|
||||||
|
2) Assign BGP peers
|
||||||
|
3) Advertise BGP networks
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
midonet_gateway_bgp { 'edge_router':
|
||||||
|
bgp_local_as_number => '65432',
|
||||||
|
bgp_advertised_networks => [
|
||||||
|
'200.0.0.0/24',
|
||||||
|
'12.0.140.0/16',
|
||||||
|
],
|
||||||
|
bgp_neighbors => [
|
||||||
|
{
|
||||||
|
'ip_address' => '200.0.1.1',
|
||||||
|
'remote_asn' => '34512'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'ip_address' => '12.0.140.1',
|
||||||
|
'remote_asn' => '24125'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
midonet_api_url => 'http://controller:8181',
|
||||||
|
username => 'admin',
|
||||||
|
password => 'admin',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensurable
|
||||||
|
|
||||||
|
newparam(:bgp_local_as_number) do
|
||||||
|
desc "AS number of the local BGP autonomous system. If you have an RIR
|
||||||
|
registered AS number, use it. If you don't have an RIR registered AS
|
||||||
|
number, use any of the RFC 1930's reserved AS number. It should be an
|
||||||
|
integer. Only applicable when uplink_type=='bgp'."
|
||||||
|
validate do |value|
|
||||||
|
unless value =~ /^\d+$/
|
||||||
|
raise ArgumentError, "'%s' is not a valid AS number" % value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:bgp_advertised_networks) do
|
||||||
|
desc "Networks advertised to the remote BGP autonomous system. This usually
|
||||||
|
should be the same as floating IP networks defined in neutron. It should
|
||||||
|
be an array of strings, each of which should be a network address
|
||||||
|
in either \"IP/Prefix_Length\" notation (\"192.0.2.0/24\") or
|
||||||
|
\"IP/Subnet_Mask\" nonation(\"192.0.2.0/255.255.255.0\"). Only applicable
|
||||||
|
when uplink_type=='bgp'. A more elaborate example will be:
|
||||||
|
|
||||||
|
[ '192.0.2.128/25', '198.51.100.128/25', '203.0.113.128/25' ]"
|
||||||
|
validate do |value|
|
||||||
|
unless value.class == Array && value.length > 0
|
||||||
|
raise ArgumentError, "#{value} is not an Array"
|
||||||
|
else
|
||||||
|
value.each do |e|
|
||||||
|
unless e.class == String && e =~ /^(([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])\/\d+$/
|
||||||
|
raise ArgumentError, "#{e} is not a valid IP address"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:bgp_neighbors) do
|
||||||
|
desc " An array containing hashes that describe a remote BGP peer. These
|
||||||
|
hashes must have two parameters:
|
||||||
|
- 'ip_address': IP address of the peer
|
||||||
|
- 'remote_asn': Peer's AS number
|
||||||
|
|
||||||
|
A real life example will look like:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'ip_address' => '203.0.113.1',
|
||||||
|
'remote_asn' => '64513'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'ip_address' => '198.51.100.1',
|
||||||
|
'remote_asn' => '64514'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"
|
||||||
|
validate do |value|
|
||||||
|
unless value.class == Array && value.length > 0
|
||||||
|
raise ArgumentError, "'%s' is not an array" % value
|
||||||
|
else
|
||||||
|
value.each do |e|
|
||||||
|
unless e.class == Hash && e.key?("ip_address") && e.key?("remote_asn")
|
||||||
|
raise ArgumentError, "'%s' doesn't contain parameters 'ip_address' and/or 'remote_asn'" % value
|
||||||
|
else
|
||||||
|
unless e["ip_address"] =~ /^(([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, "#{e['ip_address']} is not a valid IP address"
|
||||||
|
end
|
||||||
|
unless e["remote_asn"] =~ /^\d+$/
|
||||||
|
raise ArgumentError, "#{e['remote_asn']} is not a valid AS number"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
autorequire(:package) do ['midolman'] 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(:router, :namevar => true) 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
|
||||||
|
|
||||||
|
end
|
127
manifests/gateway/static.pp
Normal file
127
manifests/gateway/static.pp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# == Class: midonet::gateway::static
|
||||||
|
#
|
||||||
|
# Set up a fake uplink with static routing on a gateway node
|
||||||
|
#
|
||||||
|
# === Parameters
|
||||||
|
#
|
||||||
|
# [*network_id*]
|
||||||
|
# (Mandatory) Name of the bridge that will be created through midonet-cli
|
||||||
|
#
|
||||||
|
# [*cidr*]
|
||||||
|
# (Mandatory) Network that will be assigned to the fake uplink
|
||||||
|
#
|
||||||
|
# [*gateway_ip*]
|
||||||
|
# (Mandatory) Gateway IP through which the packets will go
|
||||||
|
#
|
||||||
|
# [*service_host*]
|
||||||
|
# (Mandatory) Host where the Midonet API runs
|
||||||
|
#
|
||||||
|
# [*service_dir*]
|
||||||
|
# (Mandatory) Folder on which to place the pidfile and some other temporary
|
||||||
|
# files for the well functioning of this script (ex: /tmp/status)
|
||||||
|
#
|
||||||
|
# [*zookeeper_hosts*]
|
||||||
|
# (Mandatory) Comma-separated list of zookeeper hosts. These are a hash consisting of two
|
||||||
|
# fields, 'ip' and 'port'. Example: [ { 'ip' => '12.153.140.2', 'port' => '2181'}]
|
||||||
|
#
|
||||||
|
# [*api_port*]
|
||||||
|
# (Mandatory) Port that the Midonet API binds
|
||||||
|
#
|
||||||
|
# [*scripts_dir*]
|
||||||
|
# (Optional) Path where to place the necessary scripts
|
||||||
|
#
|
||||||
|
# [*ensure_scripts*]
|
||||||
|
# (Optional) Status of the scripts
|
||||||
|
#
|
||||||
|
# [*mido_db_user*]
|
||||||
|
# (Optional) Username to authenticate against the DB
|
||||||
|
#
|
||||||
|
# [*mido_db_password*]
|
||||||
|
# (Optional) Password to authenticate against the DB
|
||||||
|
#
|
||||||
|
# === Examples
|
||||||
|
#
|
||||||
|
# The easiest way to run the class is:
|
||||||
|
#
|
||||||
|
# class { 'midonet::gateway::static':
|
||||||
|
# network_id => 'example_netid',
|
||||||
|
# cidr => '200.0.0.1/24',
|
||||||
|
# gateway_ip => '200.0.0.1',
|
||||||
|
# service_host => '127.0.0.1',
|
||||||
|
# service_dir => '/tmp/status',
|
||||||
|
# zookeeper_hosts => [
|
||||||
|
# {
|
||||||
|
# 'ip'=>'127.0.0.1',
|
||||||
|
# 'port'=>'2181'
|
||||||
|
# }
|
||||||
|
# ],
|
||||||
|
# api_port => '8181'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# === Authors
|
||||||
|
#
|
||||||
|
# Midonet (http://midonet.org)
|
||||||
|
#
|
||||||
|
# === Copyright
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Midokura SARL, 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
class midonet::gateway::static (
|
||||||
|
$network_id,
|
||||||
|
$cidr,
|
||||||
|
$gateway_ip,
|
||||||
|
$service_host,
|
||||||
|
$service_dir,
|
||||||
|
$zookeeper_hosts,
|
||||||
|
$api_port,
|
||||||
|
$scripts_dir = '/tmp',
|
||||||
|
$uplink_script = 'create_fake_uplink_l2.sh',
|
||||||
|
$midorc_script = 'midorc',
|
||||||
|
$functions_script = 'functions',
|
||||||
|
$ensure_scripts = 'present',
|
||||||
|
$mido_db_user = 'admin',
|
||||||
|
$mido_db_password = 'admin',
|
||||||
|
) {
|
||||||
|
|
||||||
|
# Install screen, as it's needed by the script
|
||||||
|
package { 'screen': ensure => installed }
|
||||||
|
|
||||||
|
# Place script and helper files before executing it
|
||||||
|
file { 'fake_uplink_script':
|
||||||
|
ensure => $ensure_scripts,
|
||||||
|
path => "${scripts_dir}/create_fake_uplink_l2.sh",
|
||||||
|
content => template('midonet/gateway/create_fake_uplink_l2.sh.erb'),
|
||||||
|
}
|
||||||
|
file { 'midorc':
|
||||||
|
ensure => $ensure_scripts,
|
||||||
|
path => "${scripts_dir}/midorc",
|
||||||
|
content => template('midonet/gateway/midorc.erb'),
|
||||||
|
}
|
||||||
|
file { 'functions':
|
||||||
|
ensure => $ensure_scripts,
|
||||||
|
path => "${scripts_dir}/functions",
|
||||||
|
source => 'puppet:///modules/midonet/gateway/functions',
|
||||||
|
require => Package['screen'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finally, execute the script
|
||||||
|
exec { "/bin/bash ${scripts_dir}/create_fake_uplink_l2.sh":
|
||||||
|
require => [
|
||||||
|
File['fake_uplink_script', 'midorc', 'functions'],
|
||||||
|
Package['python-midonetclient'],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe 'midonet::cluster' do
|
|
||||||
context 'with parameters' do
|
|
||||||
let :facts do
|
|
||||||
{
|
|
||||||
:osfamily => 'Debian',
|
|
||||||
:lsbdistid => 'Ubuntu',
|
|
||||||
:lsbdistrelease => '16.04',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let :params do
|
|
||||||
{
|
|
||||||
:zookeeper_hosts => ['{ "ip" => "127.0.0.1", "port" => "2181" }'],
|
|
||||||
:cassandra_servers => [ '127.0.0.1' ],
|
|
||||||
:cassandra_rep_factor => '3',
|
|
||||||
:keystone_admin_token => 'ADMIN_TOKEN',
|
|
||||||
:keystone_host => '127.0.0.1',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
it { is_expected.to contain_class('midonet::cluster::install') }
|
|
||||||
it { is_expected.to contain_class('midonet::cluster::run') }
|
|
||||||
end
|
|
||||||
end
|
|
29
spec/classes/midonet_gateway_static_spec.rb
Normal file
29
spec/classes/midonet_gateway_static_spec.rb
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'midonet::gateway::static' do
|
||||||
|
context 'with parameters' do
|
||||||
|
let :facts do
|
||||||
|
{
|
||||||
|
:osfamily => 'Debian',
|
||||||
|
:lsbdistid => 'Ubuntu',
|
||||||
|
:lsbdistrelease => '16.04',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let :params do
|
||||||
|
{
|
||||||
|
:network_id => 'example_netid',
|
||||||
|
:cidr => '200.0.0.1/24',
|
||||||
|
:gateway_ip => '200.0.0.1',
|
||||||
|
:service_host => '127.0.0.1',
|
||||||
|
:service_dir => '/tmp/status',
|
||||||
|
:zookeeper_hosts => [ { 'ip' => '127.0.0.1', 'port' => '2181' } ],
|
||||||
|
:api_port => '8181'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
it { is_expected.to contain_package('screen').with_ensure('installed') }
|
||||||
|
it { is_expected.to contain_file('fake_uplink_script').with_ensure('present') }
|
||||||
|
it { is_expected.to contain_file('midorc').with_ensure('present') }
|
||||||
|
it { is_expected.to contain_file('functions').with_ensure('present') }
|
||||||
|
it { is_expected.to contain_exec('/bin/bash /tmp/create_fake_uplink_l2.sh') }
|
||||||
|
end
|
||||||
|
end
|
@ -1,158 +1,108 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Puppet::Type.type(:midonet_gateway).provider(:midonet_api_caller) do
|
describe Puppet::Type.type(:midonet_gateway_bgp).provider(:midonet_api_caller) do
|
||||||
|
|
||||||
let(:provider) { described_class.new(resource) }
|
let(:provider) { described_class.new(resource) }
|
||||||
|
|
||||||
let(:resource) { Puppet::Type.type(:midonet_gateway).new(
|
let(:resource) do
|
||||||
{
|
Puppet::Type::type(:midonet_gateway_bgp).new(
|
||||||
:ensure => :present,
|
:router => 'edge_router',
|
||||||
:hostname => 'compute.midonet',
|
:midonet_api_url => 'http://controller:8080/midonet-api',
|
||||||
:midonet_api_url => 'http://controller:8080',
|
:username => 'admin',
|
||||||
:username => 'admin',
|
:password => 'admin',
|
||||||
:password => 'admin',
|
:bgp_local_as_number => '64512',
|
||||||
:interface => 'eth0',
|
:bgp_neighbors => [
|
||||||
:local_as => '64512',
|
{
|
||||||
:bgp_port => { 'port_address' => '198.51.100.2', 'net_prefix' => '198.51.100.0', 'net_length' => '30' },
|
'ip_address' => '200.100.98.7',
|
||||||
:remote_peers => [ { 'as' => '64513', 'ip' => '198.51.100.1' },
|
'remote_asn' => '45237',
|
||||||
{ 'as' => '64513', 'ip' => '203.0.113.1' } ],
|
},
|
||||||
:advertise_net => [ { 'net_prefix' => '192.0.2.0', 'net_length' => '24' } ]
|
{
|
||||||
}
|
'ip_address' => '182.24.63.2',
|
||||||
)}
|
'remote_asn' => '45235',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
:bgp_advertised_networks => [ '200.0.0.0/24', '200.0.20.0/24' ] )
|
||||||
|
end
|
||||||
|
|
||||||
describe 'BGP configuration happy path' do
|
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
|
|
||||||
|
|
||||||
|
# 1) Assign AS number to router
|
||||||
|
# 2) Assign BGP neighbors to routers
|
||||||
|
# 3) Advertise floating IP network
|
||||||
|
# 4) Delete BGP neighbors
|
||||||
|
# 5) De-advertise networks
|
||||||
|
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/router.html
|
||||||
let(:routers) {
|
let(:routers) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" => "e6a53892-03bf-4f16-8212-e4d76ad204e3",
|
"id" => "e6a53892-03bf-4f16-8212-e4d76ad204e3",
|
||||||
"name" => "MidoNet Provider Router"
|
"name" => "edge_router"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
let(:ports) {
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/bgp-peer.html
|
||||||
|
let(:bgp_peers) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"hostId" => "b3f2f63e-02a6-459a-af0f-44eeac441a09",
|
"id" => "4a5e4356-3417-4c60-9cf8-7516aedb7067",
|
||||||
"interfaceName" => "eth0",
|
|
||||||
"id" => "20b169b1-ec0a-4639-8479-44b63e016935"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
let(:bgps) {
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/bgp-network.html
|
||||||
|
let(:bgp_networks) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" => "4a5e4356-3417-4c60-9cf8-7516aedb7067",
|
"id" => "4a5e4356-3417-4c60-9cf8-7516abcd1234",
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
let(:tenants) {
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id" => "4486908d-8e15-4f01-b3b4-86f9def0fa04",
|
|
||||||
"name" => "4486908d-8e15-4f01-b3b4-86f9def0fa04"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
before :each do
|
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_add_route_for_uplink_port)
|
|
||||||
allow(provider).to receive(:call_get_token).and_return('thisisafaketoken')
|
allow(provider).to receive(:call_get_token).and_return('thisisafaketoken')
|
||||||
allow(provider).to receive(:call_get_tenant).and_return(tenants)
|
allow(provider).to receive(:call_get_provider_router).and_return(routers)
|
||||||
|
allow(provider).to receive(:call_assign_asn)
|
||||||
|
allow(provider).to receive(:call_add_bgp_peer)
|
||||||
|
allow(provider).to receive(:call_advertise_bgp_network)
|
||||||
|
allow(provider).to receive(:call_get_bgp_peers).and_return(bgp_peers)
|
||||||
|
allow(provider).to receive(:call_delete_bgp_peer)
|
||||||
|
allow(provider).to receive(:call_get_bgp_networks).and_return(bgp_networks.map { |e| e['id'] })
|
||||||
|
allow(provider).to receive(:call_delete_bgp_network)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates virtual ports for each remote BGP peer, advertises routes,
|
it 'follows happy path (BGP)' do
|
||||||
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_get_provider_router)
|
||||||
expect(provider).to receive(:call_create_uplink_port).with(routers[0]['id'], {'portAddress' => resource[:bgp_port]['port_address'],
|
expect(provider).to receive(:call_assign_asn)
|
||||||
'networkAddress' => resource[:bgp_port]['net_prefix'],
|
expect(provider).to receive(:call_add_bgp_peer).with(routers[0]['id'], '200.100.98.7', '45237')
|
||||||
'networkLength' => resource[:bgp_port]['net_length'].to_i,
|
expect(provider).to receive(:call_add_bgp_peer).with(routers[0]['id'], '182.24.63.2', '45235')
|
||||||
'type' => 'Router'})
|
expect(provider).to receive(:call_advertise_bgp_network).with(routers[0]['id'], '200.0.0.0/24')
|
||||||
expect(provider).to receive(:call_get_bgp_connections).with(ports[0]['id'])
|
expect(provider).to receive(:call_advertise_bgp_network).with(routers[0]['id'], '200.0.20.0/24')
|
||||||
expect(provider).to receive(:call_add_bgp_to_port).with(ports[0]['id'], {'localAS' => resource[:local_as],
|
expect(provider).to receive(:call_get_bgp_peers).with(routers[0]['id'])
|
||||||
'peerAS' => resource[:remote_peers][0]['as'],
|
expect(provider).to receive(:call_delete_bgp_peer).with(bgp_peers[0]['id'])
|
||||||
'peerAddr' => resource[:remote_peers][0]['ip']}).once
|
expect(provider).to receive(:call_get_bgp_networks).with(routers[0]['id'])
|
||||||
expect(provider).to receive(:call_add_bgp_to_port).with(ports[0]['id'], {'localAS' => resource[:local_as],
|
expect(provider).to receive(:call_delete_bgp_network).with(bgp_networks[0]['id'])
|
||||||
'peerAS' => resource[:remote_peers][1]['as'],
|
|
||||||
'peerAddr' => resource[:remote_peers][1]['ip']}).once
|
|
||||||
expect(provider).to receive(:call_add_route_for_uplink_port).with(routers[0]['id'], {'type' => 'Normal',
|
|
||||||
'srcNetworkAddr' => '0.0.0.0',
|
|
||||||
'srcNetworkLength' => 0,
|
|
||||||
'dstNetworkAddr' => resource[:bgp_port]['net_prefix'],
|
|
||||||
'dstNetworkLength' => resource[:bgp_port]['net_length'].to_i,
|
|
||||||
'weight' => 100,
|
|
||||||
'nextHopPort' => ports[0]['id']})
|
|
||||||
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
|
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
|
provider.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exists with default method returns' do
|
it 'deletes BGP peers, and stops advertising the floating IP network' do
|
||||||
# Expectations over the 'exists' call
|
expect(provider).to receive(:call_get_provider_router)
|
||||||
expect(provider).to receive(:call_get_provider_router).and_return(routers)
|
expect(provider).to receive(:call_get_bgp_peers).with(routers[0]['id'])
|
||||||
expect(provider).to receive(:call_get_host).and_return(hosts)
|
expect(provider).to receive(:call_delete_bgp_peer).with(bgp_peers[0]['id'])
|
||||||
expect(provider).to receive(:call_get_uplink_port).with(routers[0]['id'], resource[:bgp_port]['port_address']).and_return(ports)
|
expect(provider).to receive(:call_get_bgp_networks).with(routers[0]['id'])
|
||||||
expect(provider.exists?).to eq true
|
expect(provider).to receive(:call_delete_bgp_network).with(bgp_networks[0]['id'])
|
||||||
|
provider.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'checks if a given provider router exists' do
|
||||||
|
expect(provider).to receive(:call_get_provider_router)
|
||||||
|
provider.exists?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Puppet::Type.type(:midonet_gateway_bgp).provider(:midonet_api_caller) do
|
||||||
|
|
||||||
|
let(:provider) { described_class.new(resource) }
|
||||||
|
|
||||||
|
let(:resource) do
|
||||||
|
Puppet::Type::type(:midonet_gateway_bgp).new(
|
||||||
|
:router => 'edge_router',
|
||||||
|
:midonet_api_url => 'http://controller:8080/midonet-api',
|
||||||
|
:username => 'admin',
|
||||||
|
:password => 'admin',
|
||||||
|
:bgp_local_as_number => '64512',
|
||||||
|
:bgp_neighbors => [
|
||||||
|
{
|
||||||
|
'ip_address' => '200.100.98.7',
|
||||||
|
'remote_asn' => '45237',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'ip_address' => '182.24.63.2',
|
||||||
|
'remote_asn' => '45235',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
:bgp_advertised_networks => [ '200.0.0.0/24', '200.0.20.0/24' ] )
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'BGP configuration happy path' do
|
||||||
|
|
||||||
|
# 1) Assign AS number to router
|
||||||
|
# 2) Assign BGP neighbors to routers
|
||||||
|
# 3) Advertise floating IP network
|
||||||
|
# 4) Delete BGP neighbors
|
||||||
|
# 5) De-advertise networks
|
||||||
|
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/router.html
|
||||||
|
let(:routers) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" => "e6a53892-03bf-4f16-8212-e4d76ad204e3",
|
||||||
|
"name" => "edge_router"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/bgp-peer.html
|
||||||
|
let(:bgp_peers) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" => "4a5e4356-3417-4c60-9cf8-7516aedb7067",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
# Other parameters are returned by default, but only this one is needed for
|
||||||
|
# testing purposes
|
||||||
|
# More info: https://docs.midonet.org/docs/latest/rest-api/content/bgp-network.html
|
||||||
|
let(:bgp_networks) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" => "4a5e4356-3417-4c60-9cf8-7516abcd1234",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
allow(provider).to receive(:call_get_token).and_return('thisisafaketoken')
|
||||||
|
allow(provider).to receive(:call_get_provider_router).and_return(routers)
|
||||||
|
allow(provider).to receive(:call_assign_asn)
|
||||||
|
allow(provider).to receive(:call_add_bgp_peer)
|
||||||
|
allow(provider).to receive(:call_advertise_bgp_network)
|
||||||
|
allow(provider).to receive(:call_get_bgp_peers).and_return(bgp_peers)
|
||||||
|
allow(provider).to receive(:call_delete_bgp_peer)
|
||||||
|
allow(provider).to receive(:call_get_bgp_networks).and_return(bgp_networks.map { |e| e['id'] })
|
||||||
|
allow(provider).to receive(:call_delete_bgp_network)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'follows happy path (BGP)' do
|
||||||
|
expect(provider).to receive(:call_get_provider_router)
|
||||||
|
expect(provider).to receive(:call_assign_asn)
|
||||||
|
expect(provider).to receive(:call_add_bgp_peer).with(routers[0]['id'], '200.100.98.7', '45237')
|
||||||
|
expect(provider).to receive(:call_add_bgp_peer).with(routers[0]['id'], '182.24.63.2', '45235')
|
||||||
|
expect(provider).to receive(:call_advertise_bgp_network).with(routers[0]['id'], '200.0.0.0/24')
|
||||||
|
expect(provider).to receive(:call_advertise_bgp_network).with(routers[0]['id'], '200.0.20.0/24')
|
||||||
|
expect(provider).to receive(:call_get_bgp_peers).with(routers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_delete_bgp_peer).with(bgp_peers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_get_bgp_networks).with(routers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_delete_bgp_network).with(bgp_networks[0]['id'])
|
||||||
|
provider.create
|
||||||
|
provider.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes BGP peers, and stops advertising the floating IP network' do
|
||||||
|
expect(provider).to receive(:call_get_provider_router)
|
||||||
|
expect(provider).to receive(:call_get_bgp_peers).with(routers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_delete_bgp_peer).with(bgp_peers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_get_bgp_networks).with(routers[0]['id'])
|
||||||
|
expect(provider).to receive(:call_delete_bgp_network).with(bgp_networks[0]['id'])
|
||||||
|
provider.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'checks if a given provider router exists' do
|
||||||
|
expect(provider).to receive(:call_get_provider_router)
|
||||||
|
provider.exists?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
73
spec/unit/puppet/type/midonet_gateway_bgp_spec.rb
Normal file
73
spec/unit/puppet/type/midonet_gateway_bgp_spec.rb
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require 'puppet'
|
||||||
|
require 'puppet/type/midonet_gateway_bgp'
|
||||||
|
require 'facter'
|
||||||
|
|
||||||
|
describe Puppet::Type::type(:midonet_gateway_bgp) do
|
||||||
|
|
||||||
|
context 'on default values (bgp)' do
|
||||||
|
let(:resource) do
|
||||||
|
Puppet::Type::type(:midonet_gateway_bgp).new(
|
||||||
|
:router => 'edge_router',
|
||||||
|
:midonet_api_url => 'http://controller:8080/midonet-api',
|
||||||
|
:username => 'admin',
|
||||||
|
:password => 'admin',
|
||||||
|
:bgp_local_as_number => '64512',
|
||||||
|
:bgp_neighbors => [
|
||||||
|
{
|
||||||
|
'ip_address' => '200.100.98.7',
|
||||||
|
'remote_asn' => '45237',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'ip_address' => '182.24.63.2',
|
||||||
|
'remote_asn' => '45235',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
:bgp_advertised_networks => [ '200.0.0.0/24', '200.0.20.0/24' ] )
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'assign the default values' do
|
||||||
|
expect(resource[:username]).to eq 'admin'
|
||||||
|
expect(resource[:password]).to eq 'admin'
|
||||||
|
expect(resource[:router]).to eq 'edge_router'
|
||||||
|
expect(resource[:bgp_local_as_number]).to eq '64512'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on invalid api url' do
|
||||||
|
it do
|
||||||
|
expect {
|
||||||
|
Puppet::Type.type(:midonet_gateway_bgp).new(
|
||||||
|
:router => 'edge_router',
|
||||||
|
:midonet_api_url => '87.23.43.2:8080/midonet-api',
|
||||||
|
:username => 'admin',
|
||||||
|
:password => 'admin')
|
||||||
|
}.to raise_error(Puppet::ResourceError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on invalid bgp neighbors' do
|
||||||
|
it do
|
||||||
|
expect {
|
||||||
|
Puppet::Type.type(:midonet_gateway_bgp).new(
|
||||||
|
:router => 'edge_router',
|
||||||
|
:bgp_neighbors => '["12.13.14.15"]',
|
||||||
|
:username => 'admin',
|
||||||
|
:password => 'admin')
|
||||||
|
}.to raise_error(Puppet::ResourceError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on advertising invalid networks' do
|
||||||
|
it do
|
||||||
|
expect {
|
||||||
|
Puppet::Type.type(:midonet_gateway_bgp).new(
|
||||||
|
:router => 'edge_router',
|
||||||
|
:bgp_advertised_networks => ["12.13.14.15", "11.33.44.55/12"],
|
||||||
|
:username => 'admin',
|
||||||
|
:password => 'admin')
|
||||||
|
}.to raise_error(Puppet::ResourceError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,202 +0,0 @@
|
|||||||
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 without vlan tag' 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 interface name with vlan tag' 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.9')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'assign to it' do
|
|
||||||
expect(resource[:interface]).to eq 'eth0.9'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'on invalid interface 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',
|
|
||||||
:interface => 'eth0.9a')
|
|
||||||
}.to raise_error(Puppet::ResourceError)
|
|
||||||
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
|
|
||||||
|
|
105
templates/gateway/create_fake_uplink_l2.sh.erb
Normal file
105
templates/gateway/create_fake_uplink_l2.sh.erb
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2016 Midokura SARL
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# This script creates the fake uplink assuming that midonet is running
|
||||||
|
# correctly. It takes the IP address CIDR as its argument which gets
|
||||||
|
# routed into the MidoNet provider router. CIDR is defaulted to
|
||||||
|
# 172.24.4.0/24 with the gateway 172.24.4.1, but you can override by:
|
||||||
|
#
|
||||||
|
# ./create_fake_uplink_l2.sh <network_id> 172.24.4.0/24 172.24.4.1
|
||||||
|
# or
|
||||||
|
# CIDR=172.24.4.0/24 GATEWAY_IP=172.24.4.1 NETWORK_ID=<network_id> \
|
||||||
|
# ./create_fake_uplink.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
echo "Usage: $0 <NETWORK_ID> <CIDR> <GATEWAY_IP>]" 1>&2;
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NETWORK_ID=<%= @network_id %>
|
||||||
|
CIDR=<%= @cidr %>
|
||||||
|
GATEWAY_IP=<%= @gateway_ip %>
|
||||||
|
|
||||||
|
if [ -z "${NETWORK_ID}" ] || [ -z "${CIDR}" ] || [ -z "${GATEWAY_IP}" ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "NETWORK_ID = $NETWORK_ID"
|
||||||
|
echo "CIDR = $CIDR"
|
||||||
|
echo "GATEWAY_IP = $GATEWAY_IP"
|
||||||
|
|
||||||
|
OLD_IFS=$IFS
|
||||||
|
IFS='/'; read -ra CIDR_SPLIT <<< "$CIDR"
|
||||||
|
NET_LEN=${CIDR_SPLIT[1]}
|
||||||
|
IFS=$OLD_IFS
|
||||||
|
|
||||||
|
# Save the top directory and source the functions and midorc
|
||||||
|
TOP_DIR=$(cd $(dirname "$0") && pwd)
|
||||||
|
source $TOP_DIR/midorc
|
||||||
|
source $TOP_DIR/functions
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Get the host id of the devstack machine
|
||||||
|
HOST_ID=$(midonet-cli -e host list | awk '{ print $2 }')
|
||||||
|
die_if_not_set $LINENO HOST_ID "FAILED to obtain host id"
|
||||||
|
echo "Host: ${HOST_ID}"
|
||||||
|
|
||||||
|
# Check if the default tunnel zone exists
|
||||||
|
TZ_NAME='DEFAULT'
|
||||||
|
TZ_ID=$(midonet-cli -e list tunnel-zone name $TZ_NAME | awk '{ print $2 }')
|
||||||
|
if [[ -z "$TZ_ID" ]]; then
|
||||||
|
TZ_ID=$(midonet-cli -e create tunnel-zone name $TZ_NAME type gre)
|
||||||
|
fi
|
||||||
|
echo "Tunnel Zone: ${TZ_ID}"
|
||||||
|
|
||||||
|
# Check if the host is a member of the tunnel zone
|
||||||
|
TZ_MEMBER=$(midonet-cli -e tunnel-zone $TZ_ID list member host $HOST_ID \
|
||||||
|
address $GATEWAY_IP)
|
||||||
|
if [[ -z "$TZ_MEMBER" ]]; then
|
||||||
|
TZ_MEMBER=$(midonet-cli -e tunnel-zone $TZ_ID add member host $HOST_ID \
|
||||||
|
address $GATEWAY_IP)
|
||||||
|
fi
|
||||||
|
echo "Tunnel Zone Member: ${TZ_MEMBER}"
|
||||||
|
|
||||||
|
# Check if we need to bind - we assume that if 'veth1' is bound, then it must
|
||||||
|
# exist, and so does veth0
|
||||||
|
BINDING=$(midonet-cli -e host $HOST_ID list binding interface veth1)
|
||||||
|
if [[ -z "$BINDING" ]]; then
|
||||||
|
|
||||||
|
# Create the veth interfaces
|
||||||
|
sudo ip link add type veth
|
||||||
|
sudo ip addr flush veth0
|
||||||
|
sudo ip addr flush veth1
|
||||||
|
sudo ip link set dev veth0 up
|
||||||
|
sudo ip link set dev veth1 up
|
||||||
|
|
||||||
|
PORT_ID=$(midonet-cli -e bridge $NETWORK_ID add port)
|
||||||
|
echo "Port: ${PORT_ID}"
|
||||||
|
|
||||||
|
BINDING=$(midonet-cli -e host $HOST_ID add binding \
|
||||||
|
port bridge $NETWORK_ID port $PORT_ID interface veth1)
|
||||||
|
fi
|
||||||
|
echo "Binding: ${BINDING}"
|
||||||
|
|
||||||
|
# Add the gateway address to the other veth
|
||||||
|
if ! ip addr | grep veth0 | grep $GATEWAY_IP; then
|
||||||
|
sudo ip addr add $GATEWAY_IP/$NET_LEN dev veth0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Successfully created fake uplink"
|
58
templates/gateway/midorc.erb
Normal file
58
templates/gateway/midorc.erb
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2016 Midokura SARL
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# If you want to override these variables, create a file called 'localrc'
|
||||||
|
# and place it in the same directory as this file.
|
||||||
|
|
||||||
|
# rc file(s) location
|
||||||
|
RC_DIR=$(cd $(dirname "${BASH_SOURCE:-$0}") && pwd)
|
||||||
|
|
||||||
|
# allow local overrides of env variables
|
||||||
|
if [[ -f $RC_DIR/localrc ]]; then
|
||||||
|
source $RC_DIR/localrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
# IP address/hostname to use for the services
|
||||||
|
SERVICE_HOST=<%= @service_host %>
|
||||||
|
|
||||||
|
# Directory where all the service files will live
|
||||||
|
SERVICE_DIR=<%= @service_dir %>
|
||||||
|
|
||||||
|
USE_SCREEN="True"
|
||||||
|
|
||||||
|
# ZK Hosts (comma delimited)
|
||||||
|
<%- zkarr = Array.new -%>
|
||||||
|
<%- @zookeeper_hosts.each do |s| -%>
|
||||||
|
<%- zkarr.push("#{s['ip']}:#{s['port'] ||= 2181 }") -%>
|
||||||
|
<%- end -%>
|
||||||
|
ZOOKEEPER_HOSTS=<%= zkarr.join(",") %>
|
||||||
|
|
||||||
|
# MidoNet API port and URI
|
||||||
|
API_PORT=<%= @api_port %>
|
||||||
|
API_URI=http://$SERVICE_HOST:$API_PORT/midonet-api
|
||||||
|
|
||||||
|
MIDO_DB_USER=<%= @mido_db_user %>
|
||||||
|
MIDO_DB_PASSWORD=<%= @mido_db_password %>
|
||||||
|
|
||||||
|
# MidoNet Client
|
||||||
|
# --------------
|
||||||
|
|
||||||
|
# Auth variables. They are exported so that you could source this file and
|
||||||
|
# run midonet-cli using these credentials
|
||||||
|
export MIDO_API_URL=$API_URI
|
||||||
|
export MIDO_USER=${MIDO_USER:-admin}
|
||||||
|
export MIDO_PROJECT_ID=${MIDO_PROJECT_ID:-admin}
|
||||||
|
export MIDO_PASSWORD=${MIDO_PASSWORD:-midonet}
|
Loading…
x
Reference in New Issue
Block a user