diff --git a/files/gateway/functions b/files/gateway/functions new file mode 100644 index 0000000..3b8b189 --- /dev/null +++ b/files/gateway/functions @@ -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 diff --git a/lib/puppet/provider/midonet_gateway/midonet_api_caller.rb b/lib/puppet/provider/midonet_gateway/midonet_api_caller.rb deleted file mode 100644 index 1a0abee..0000000 --- a/lib/puppet/provider/midonet_gateway/midonet_api_caller.rb +++ /dev/null @@ -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 diff --git a/lib/puppet/provider/midonet_gateway_bgp/midonet_api_caller.rb b/lib/puppet/provider/midonet_gateway_bgp/midonet_api_caller.rb new file mode 100644 index 0000000..62aca34 --- /dev/null +++ b/lib/puppet/provider/midonet_gateway_bgp/midonet_api_caller.rb @@ -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 diff --git a/lib/puppet/type/midonet_gateway.rb b/lib/puppet/type/midonet_gateway.rb deleted file mode 100644 index 0cd8055..0000000 --- a/lib/puppet/type/midonet_gateway.rb +++ /dev/null @@ -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 - diff --git a/lib/puppet/type/midonet_gateway_bgp.rb b/lib/puppet/type/midonet_gateway_bgp.rb new file mode 100644 index 0000000..bff0a7f --- /dev/null +++ b/lib/puppet/type/midonet_gateway_bgp.rb @@ -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 diff --git a/manifests/gateway/static.pp b/manifests/gateway/static.pp new file mode 100644 index 0000000..42d36b5 --- /dev/null +++ b/manifests/gateway/static.pp @@ -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'], + ] + } +} diff --git a/spec/classes/midonet_cluster_spec.rb b/spec/classes/midonet_cluster_spec.rb deleted file mode 100644 index adb95f2..0000000 --- a/spec/classes/midonet_cluster_spec.rb +++ /dev/null @@ -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 diff --git a/spec/classes/midonet_gateway_static_spec.rb b/spec/classes/midonet_gateway_static_spec.rb new file mode 100644 index 0000000..ac64d4f --- /dev/null +++ b/spec/classes/midonet_gateway_static_spec.rb @@ -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 diff --git a/spec/unit/puppet/provider/midonet_gateway/midonet_api_caller_spec.rb b/spec/unit/puppet/provider/midonet_gateway/midonet_api_caller_spec.rb index d70eec7..3d57309 100644 --- a/spec/unit/puppet/provider/midonet_gateway/midonet_api_caller_spec.rb +++ b/spec/unit/puppet/provider/midonet_gateway/midonet_api_caller_spec.rb @@ -1,158 +1,108 @@ 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(: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' } ] - } - )} + 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 - # - 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) { [ { "id" => "e6a53892-03bf-4f16-8212-e4d76ad204e3", - "name" => "MidoNet Provider Router" + "name" => "edge_router" } ] } - - let(:ports) { + # 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) { [ { - "hostId" => "b3f2f63e-02a6-459a-af0f-44eeac441a09", - "interfaceName" => "eth0", - "id" => "20b169b1-ec0a-4639-8479-44b63e016935" + "id" => "4a5e4356-3417-4c60-9cf8-7516aedb7067", } ] } - - let(:bgps) { + # 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-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" - } - ] - } - - let(:tenants) { - [ - { - "id" => "4486908d-8e15-4f01-b3b4-86f9def0fa04", - "name" => "4486908d-8e15-4f01-b3b4-86f9def0fa04" + "id" => "4a5e4356-3417-4c60-9cf8-7516abcd1234", } ] } 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_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 - 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 + it 'follows happy path (BGP)' do 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_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'}) + 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 - 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 + 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 diff --git a/spec/unit/puppet/provider/midonet_gateway_bgp/midonet_api_caller_spec.rb b/spec/unit/puppet/provider/midonet_gateway_bgp/midonet_api_caller_spec.rb new file mode 100644 index 0000000..3d57309 --- /dev/null +++ b/spec/unit/puppet/provider/midonet_gateway_bgp/midonet_api_caller_spec.rb @@ -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 diff --git a/spec/unit/puppet/type/midonet_gateway_bgp_spec.rb b/spec/unit/puppet/type/midonet_gateway_bgp_spec.rb new file mode 100644 index 0000000..7291e64 --- /dev/null +++ b/spec/unit/puppet/type/midonet_gateway_bgp_spec.rb @@ -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 diff --git a/spec/unit/puppet/type/midonet_gateway_spec.rb b/spec/unit/puppet/type/midonet_gateway_spec.rb deleted file mode 100644 index 37907a8..0000000 --- a/spec/unit/puppet/type/midonet_gateway_spec.rb +++ /dev/null @@ -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 - diff --git a/templates/gateway/create_fake_uplink_l2.sh.erb b/templates/gateway/create_fake_uplink_l2.sh.erb new file mode 100644 index 0000000..95e1edd --- /dev/null +++ b/templates/gateway/create_fake_uplink_l2.sh.erb @@ -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 172.24.4.0/24 172.24.4.1 +# or +# CIDR=172.24.4.0/24 GATEWAY_IP=172.24.4.1 NETWORK_ID= \ +# ./create_fake_uplink.sh +# + +function usage() { + echo "Usage: $0 ]" 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" diff --git a/templates/gateway/midorc.erb b/templates/gateway/midorc.erb new file mode 100644 index 0000000..04792b1 --- /dev/null +++ b/templates/gateway/midorc.erb @@ -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}