Harden check for running ovs in sfc full deploy
[integration/test.git] / csit / suites / sfc / Full_Deploy / docker-ovs.sh
1 #!/bin/bash
2 # Copyright (C) 2014 Nicira, Inc.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 # BASED ON https://github.com/openvswitch/ovs/blob/master/utilities/ovs-docker
17 # MODIFIED
18
19 set -o xtrace
20 #set -e #Exit script if a command fails
21
22 # Check for programs we'll need.
23 search_path () {
24     save_IFS=$IFS
25     IFS=:
26     for dir in $PATH; do
27         IFS=$save_IFS
28         if test -x "$dir/$1"; then
29             return 0
30         fi
31     done
32     IFS=$save_IFS
33     echo >&2 "$0: $1 not found in \$PATH, please install and try again"
34     exit 1
35 }
36
37 ovs_vsctl () {
38     sudo ovs-vsctl --timeout=60 "$@"
39 }
40
41 d_ovs_vsctl () {
42     CONTAINER="$1"
43     shift
44     sudo docker exec "$CONTAINER" ovs-vsctl --timeout=60 "$@"
45 }
46
47 create_netns_link () {
48     sudo mkdir -p /var/run/netns
49     if [ ! -e /var/run/netns/"$PID" ]; then
50         sudo ln -s /proc/"$PID"/ns/net /var/run/netns/"$PID"
51         trap 'delete_netns_link' 0
52         for signal in 1 2 3 13 14 15; do
53             trap 'delete_netns_link; trap - $signal; kill -$signal $$' $signal
54         done
55     fi
56 }
57
58 delete_netns_link () {
59     sudo rm -f /var/run/netns/"$PID"
60 }
61
62 setup_ip_forwarding () {
63     sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
64     sudo iptables -F
65     sudo iptables -t nat -F
66     sudo iptables -P FORWARD ACCEPT
67 }
68
69 connect_namespace_to_container () {
70
71     NAMESPACE="$1"
72     CONTAINER="$2"
73
74     if [ -z "$NAMESPACE" ] || [ -z "$CONTAINER" ]; then
75         echo >&2 "$UTIL add-port: not enough arguments (use --help for help)"
76         exit 1
77     fi
78
79     shift 2
80     while [ $# -ne 0 ]; do
81         case $1 in
82             --ipaddress=*)
83                 ADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'`
84                 shift
85                 ;;
86             --macaddress=*)
87                 MACADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'`
88                 shift
89                 ;;
90             *)
91                 echo >&2 "$UTIL add-port: unknown option \"$1\""
92                 exit 1
93                 ;;
94         esac
95     done
96
97     if PID=`sudo docker inspect -f '{{.State.Pid}}' "$CONTAINER"`; then :; else
98         echo >&2 "$UTIL: Failed to get the PID of the container"
99         exit 1
100     fi
101
102     create_netns_link
103
104     CONTAINER_IF="v-$NAMESPACE"
105     NAMESPACE_IF="v-${CONTAINER:0:12}"
106
107     # Create namespace
108     if [ -z `sudo ip netns list | grep "$NAMESPACE"` ]; then
109          sudo ip netns add "$NAMESPACE"
110     fi
111
112     # Create a veth pair in namespace.
113     sudo ip netns exec "$NAMESPACE" ip link add "$NAMESPACE_IF" type veth peer \
114        name "$CONTAINER_IF"
115     sudo ip netns exec "$NAMESPACE" ip link set dev "$NAMESPACE_IF" up
116
117     # Move one side to container namespace.
118     sudo ip netns exec "$NAMESPACE" ip link set dev "$CONTAINER_IF" netns "$PID"
119     sudo ip netns exec "$PID" ip link set dev "$CONTAINER_IF" up
120
121     # And put it in integration bridge
122     d_ovs_vsctl "$CONTAINER" add-port br-int "$CONTAINER_IF"
123
124     if [ -n "$ADDRESS" ]; then
125         sudo ip netns exec "$NAMESPACE" ip addr add "$ADDRESS" dev "$NAMESPACE_IF"
126     fi
127
128     if [ -n "$MACADDRESS" ]; then
129         sudo ip netns exec "$NAMESPACE" ip link set dev "$NAMESPACE_IF" \
130            address "$MACADDRESS"
131     fi
132
133     delete_netns_link
134 }
135
136 check_status () {
137     CHECKED_CONTAINER="$1"
138     CHECKED_PROGRAM="$2"
139     CHECKED_STATUS="$3"
140
141     STATUS=""
142     retry=0
143     while [ "$STATUS" != "$CHECKED_STATUS" -a $retry -le 60 ]; do
144        sleep 1
145        STATUS=`sudo docker exec "$CHECKED_CONTAINER" supervisorctl status "$CHECKED_PROGRAM" |\
146           awk '{print $2}'`
147        retry=$[$retry+1]
148     done
149     [ "$STATUS" == "$CHECKED_STATUS" ] || exit 1
150 }
151
152 spawn_node () {
153     NODE="$1"
154     TUN="$2"
155
156     if [ -z `sudo docker images | awk '/^ovs-docker/ {print $1}'` ]; then
157         echo "$UTIL: Docker image ovs-docker does not exist, creating..."
158         sudo docker build -t ovs-docker .
159     fi
160
161     CONTAINER=`sudo docker run -itd --privileged --cap-add ALL --name=ovs-node-"$NODE" ovs-docker`
162
163     if [ $? -ne 0 ]; then
164        echo >&2 "$UTIL: Failed to start container $NODE"
165        exit 1
166     fi
167
168     check_status "$CONTAINER" ovsdb-server RUNNING
169     check_status "$CONTAINER" ovs-vswitchd RUNNING
170     check_status "$CONTAINER" configure-ovs EXITED
171
172     CONTAINER_GW=`sudo docker inspect -f '{{ .NetworkSettings.Gateway }}' "$CONTAINER"`
173     CONTAINER_IP=`sudo docker inspect -f '{{ .NetworkSettings.IPAddress }}' "$CONTAINER"`
174
175     # Create a container bridge as integration for all guests
176     if d_ovs_vsctl "$CONTAINER" br-exists br-int; then :; else
177         d_ovs_vsctl "$CONTAINER" add-br br-int
178         d_ovs_vsctl "$CONTAINER" add-port br-int patch-tun -- \
179            set interface patch-tun type=patch option:peer=patch-int
180     fi
181
182
183     # Create a container bridge as endpoint for all tunnels
184     if d_ovs_vsctl "$CONTAINER" br-exists br-tun; then :; else
185         d_ovs_vsctl "$CONTAINER" add-br br-tun
186         d_ovs_vsctl "$CONTAINER" add-port br-tun patch-int -- \
187            set interface patch-int type=patch option:peer=patch-tun
188     fi
189
190     # Setup the tunnel
191     if [ "$TUN" == "vxlan" ]; then
192         TUN_OPT="type=vxlan"
193     elif [ "$TUN" == "vxlan-gpe" ]; then
194         TUN_OPT="type=vxlan option:exts=gpe"
195     else
196         TUN_OPT=""
197     fi
198
199     if [ -z "$TUN" ]; then :; else
200         ovs_vsctl add-port br-tun vtep-node-"$NODE" -- \
201             set interface vtep-node-"$NODE" $TUN_OPT \
202             option:remote_ip="$CONTAINER_IP" ofport_request="$NODE"
203         d_ovs_vsctl "$CONTAINER" add-port br-tun vtep -- \
204             set interface vtep $TUN_OPT \
205             option:remote_ip="$CONTAINER_GW" ofport_request=10
206     fi
207
208     if [ -z "$ODL" ]; then :; else
209         d_ovs_vsctl "$CONTAINER" set-manager "tcp:${ODL}:6640"
210     fi
211
212     DO_GUEST="$GUESTS"
213     until [ $DO_GUEST -eq 0 ]; do
214         ADDRESS="10.0.${NODE}.${DO_GUEST}/16"
215         connect_namespace_to_container "ovsnsn${NODE}g$DO_GUEST" "$CONTAINER" \
216            --ipaddress="$ADDRESS"
217         let DO_GUEST-=1
218     done
219 }
220
221 spawn_nodes_and_guests () {
222
223     while [ $# -ne 0 ]; do
224         case $1 in
225             --nodes=*)
226                 NODES=`expr X"$1" : 'X[^=]*=\(.*\)'`
227                 shift
228                 ;;
229             --guests=*)
230                 GUESTS=`expr X"$1" : 'X[^=]*=\(.*\)'`
231                 shift
232                 ;;
233             --tun=*)
234                 TUN=`expr X"$1" : 'X[^=]*=\(.*\)'`
235                 shift
236                 ;;
237             --odl=*)
238                 ODL=`expr X"$1" : 'X[^=]*=\(.*\)'`
239                 shift
240                 ;;
241             *)
242                 echo >&2 "$UTIL spawn: unknown option \"$1\""
243                 exit 1
244                 ;;
245         esac
246     done
247
248     NUM_REGEX="[0-9]+"
249     TUN_REGEX="vxlan|vxlan-gpe|^$"
250     IP_REGEX="^$|^(([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])$"
251
252     if [[ $NODES =~ $NUM_REGEX ]]; then :; else
253          echo >&2 "$UTIL: NODES has to be a number"
254          exit 1
255     fi
256
257     if [ $NODES -gt 256 ]; then
258          echo >&2 "$UTIL: NODES has to be less than 256"
259          exit 1
260     fi
261
262     if [[ $GUESTS =~ $NUM_REGEX ]]; then :; else
263          echo >&2 "$UTIL: GUESTS has to be a number"
264          exit 1
265     fi
266
267     if [ $GUESTS -gt 256 ]; then
268          echo >&2 "$UTIL: GUESTS has to be less than 256"
269          exit 1
270     fi
271
272     if [[ $TUN =~ $TUN_REGEX ]]; then :; else
273          echo >&2 "$UTIL: TYPE has to be vxlan or vxlan-gpe"
274          exit 1
275     fi
276
277     if [[ $ODL =~ $IP_REGEX ]]; then :; else
278          echo >&2 "$UTIL: IP has to be a valid ip address"
279          exit 1
280     fi
281
282     # Make sure ip forwarding is enabled
283     setup_ip_forwarding
284
285     # Create a host bridge as end point for all tunnels
286     if ovs_vsctl br-exists br-tun; then :; else
287         ovs_vsctl add-br br-tun
288         if [ -z "$ODL" ]; then :; else
289             ovs_vsctl set-manager "tcp:${ODL}:6640"
290             ovs_vsctl set-controller br-tun "tcp:${ODL}:6633"
291         fi
292     fi
293
294     DO_NODE="$NODES"
295     until [ $DO_NODE -eq 0 ]; do
296        spawn_node "$DO_NODE" "$TUN"
297        let DO_NODE-=1
298     done
299 }
300
301 clean() {
302
303      for ID in `sudo docker ps -a | awk '/ovs-node-[0-9]+$/ {print $1}'`; do
304          sudo docker stop "$ID"
305          sudo docker rm "$ID"
306      done
307
308      for NS in `sudo ip netns list | grep ovsns`; do
309          sudo ip netns del "$NS"
310      done
311
312      ovs_vsctl del-br br-tun
313      ovs_vsctl del-manager
314 }
315
316
317 usage() {
318     cat << EOF
319 ${UTIL}: Perform various tasks related with docker-ovs container.
320 usage: ${UTIL} COMMAND
321
322 Commands:
323   spawn --nodes=NODES --guests=GUESTS --tun=TYPE --odl=IP
324                     Runs NODES number of docker-ovs instances and attaches
325                     GUESTS number of namespaces to each instance. If tun
326                     option is specified, tunnel of such type will be configured
327                     between the nodes and a host bridge. Types supported are
328                     vxlan or vxlan-gpe
329   clean
330                     Stops containers and deletes namespaces
331 Options:
332   -h, --help        display this help message.
333 EOF
334 }
335
336 UTIL=$(basename $0)
337 search_path ovs-vsctl
338 search_path docker
339
340 #if [[ $EUID -ne 0 ]]; then
341 #   echo "This script must be run as root" 1>&2
342 #   exit 1
343 #fi
344
345 if (sudo ip netns) > /dev/null 2>&1; then :; else
346     echo >&2 "$UTIL: ip utility not found (or it does not support netns),"\
347              "cannot proceed"
348     exit 1
349 fi
350
351 if [ $# -eq 0 ]; then
352     usage
353     exit 0
354 fi
355
356 case $1 in
357     "spawn")
358         shift
359         spawn_nodes_and_guests "$@"
360         exit 0
361         ;;
362     "clean")
363         shift
364         clean
365         exit 0
366         ;;
367     -h | --help)
368         usage
369         exit 0
370         ;;
371     *)
372         echo >&2 "$UTIL: unknown command \"$1\" (use --help for help)"
373         exit 1
374         ;;
375 esac
376