dd2aef1c652478c6f9efe8efbca5ccaef00e5237
[cascardo/ovs.git] / utilities / ovs-docker
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 # Check for programs we'll need.
17 search_path () {
18     save_IFS=$IFS
19     IFS=:
20     for dir in $PATH; do
21         IFS=$save_IFS
22         if test -x "$dir/$1"; then
23             return 0
24         fi
25     done
26     IFS=$save_IFS
27     echo >&2 "$0: $1 not found in \$PATH, please install and try again"
28     exit 1
29 }
30
31 ovs_vsctl () {
32     ovs-vsctl --timeout=60 "$@"
33 }
34
35 create_netns_link () {
36     mkdir -p /var/run/netns
37     if [ ! -e /var/run/netns/"$PID" ]; then
38         ln -s /proc/"$PID"/ns/net /var/run/netns/"$PID"
39         trap 'delete_netns_link' 0
40         for signal in 1 2 3 13 14 15; do
41             trap 'delete_netns_link; trap - $signal; kill -$signal $$' $signal
42         done
43     fi
44 }
45
46 delete_netns_link () {
47     rm -f /var/run/netns/"$PID"
48 }
49
50 get_port_for_container_interface () {
51     CONTAINER="$1"
52     INTERFACE="$2"
53
54     PORT=`ovs_vsctl --data=bare --no-heading --columns=name find interface \
55              external_ids:container_id="$CONTAINER"  \
56              external_ids:container_iface="$INTERFACE"`
57     if [ -z "$PORT" ]; then
58         echo >&2 "$UTIL: Failed to find any attached port" \
59                  "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE"
60     fi
61     echo "$PORT"
62 }
63
64 add_port () {
65     BRIDGE="$1"
66     INTERFACE="$2"
67     CONTAINER="$3"
68     ADDRESS="$4"
69     GATEWAY="$5"
70
71     if [ "$#" -lt 3 ]; then
72         usage
73         exit 1
74     fi
75
76     # Check if a port is already attached for the given container and interface
77     PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE" \
78             2>/dev/null`
79     if [ -n "$PORT" ]; then
80         echo >&2 "$UTIL: Port already attached" \
81                  "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE"
82         exit 1
83     fi
84
85     if ovs_vsctl br-exists "$BRIDGE" || \
86         ovs_vsctl add-br "$BRIDGE"; then :; else
87         echo >&2 "$UTIL: Failed to create bridge $BRIDGE"
88         exit 1
89     fi
90
91     if PID=`docker inspect -f '{{.State.Pid}}' "$CONTAINER"`; then :; else
92         echo >&2 "$UTIL: Failed to get the PID of the container"
93         exit 1
94     fi
95
96     create_netns_link
97
98     # Create a veth pair.
99     ID=`uuidgen | sed 's/-//g'`
100     PORTNAME="${ID:0:13}"
101     ip link add "${PORTNAME}_l" type veth peer name "${PORTNAME}_c"
102
103     # Add one end of veth to OVS bridge.
104     if ovs_vsctl --may-exist add-port "$BRIDGE" "${PORTNAME}_l" \
105        -- set interface "${PORTNAME}_l" \
106        external_ids:container_id="$CONTAINER" \
107        external_ids:container_iface="$INTERFACE"; then :; else
108         echo >&2 "$UTIL: Failed to add "${PORTNAME}_l" port to bridge $BRIDGE"
109         ip link delete "${PORTNAME}_l"
110         exit 1
111     fi
112
113     ip link set "${PORTNAME}_l" up
114
115     # Move "${PORTNAME}_c" inside the container and changes its name.
116     ip link set "${PORTNAME}_c" netns "$PID"
117     ip netns exec "$PID" ip link set dev "${PORTNAME}_c" name "$INTERFACE"
118     ip netns exec "$PID" ip link set "$INTERFACE" up
119
120     if [ -n "$ADDRESS" ]; then
121         ip netns exec "$PID" ip addr add "$ADDRESS" dev "$INTERFACE"
122     fi
123
124     if [ -n "$GATEWAY" ]; then
125         ip netns exec "$PID" ip route add default via "$GATEWAY"
126     fi
127 }
128
129 del_port () {
130     BRIDGE="$1"
131     INTERFACE="$2"
132     CONTAINER="$3"
133
134     if [ "$#" -lt 3 ]; then
135         usage
136         exit 1
137     fi
138
139     PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE"`
140     if [ -z "$PORT" ]; then
141         exit 1
142     fi
143
144     ovs_vsctl --if-exists del-port "$PORT"
145
146     ip link delete "$PORT"
147 }
148
149 del_ports () {
150     BRIDGE="$1"
151     CONTAINER="$2"
152     if [ "$#" -lt 2 ]; then
153         usage
154         exit 1
155     fi
156
157     PORTS=`ovs_vsctl --data=bare --no-heading --columns=name find interface \
158              external_ids:container_id="$CONTAINER"`
159     if [ -z "$PORTS" ]; then
160         exit 0
161     fi
162
163     for PORT in $PORTS; do
164         ovs_vsctl --if-exists del-port "$PORT"
165         ip link delete "$PORT"
166     done
167 }
168
169 set_vlan () {
170     BRIDGE="$1"
171     INTERFACE="$2"
172     CONTAINER_ID="$3"
173     VLAN="$4"
174
175     if [ "$#" -lt 4 ]; then
176         usage
177         exit 1
178     fi
179
180     PORT=`get_port_for_container_interface "$CONTAINER_ID" "$INTERFACE"`
181     if [ -z "$PORT" ]; then
182         exit 1
183     fi
184     ovs_vsctl set port "$PORT" tag="$VLAN"
185 }
186
187 usage() {
188     cat << EOF
189 ${UTIL}: Performs integration of Open vSwitch with Docker.
190 usage: ${UTIL} COMMAND
191
192 Commands:
193   add-port BRIDGE INTERFACE CONTAINER [ADDRESS [GATEWAY]]
194                     Adds INTERFACE inside CONTAINER and connects it as a port
195                     in Open vSwitch BRIDGE. Optionally, sets ADDRESS on
196                     INTERFACE. ADDRESS can include a '/' to represent network
197                     prefix length. Along with ADDRESS, optionally set the
198                     default gateway for the container. e.g.:
199                     ${UTIL} add-port br-int eth1 c474a0e2830e 192.168.1.2/24 \
200                         192.168.1.1
201   del-port BRIDGE INTERFACE CONTAINER
202                     Deletes INTERFACE inside CONTAINER and removes its
203                     connection to Open vSwitch BRIDGE. e.g.:
204                     ${UTIL} del-port br-int eth1 c474a0e2830e
205   del-ports BRIDGE CONTAINER
206                     Removes all Open vSwitch interfaces from CONTAINER. e.g.:
207                     ${UTIL} del-ports br-int c474a0e2830e
208   set-vlan BRIDGE INTERFACE CONTAINER VLAN
209                     Configures the INTERFACE of CONTAINER attached to BRIDGE
210                     to become an access port of VLAN. e.g.:
211                     ${UTIL} set-vlan br-int eth1 c474a0e2830e 5
212 Options:
213   -h, --help        display this help message.
214 EOF
215 }
216
217 UTIL=$(basename $0)
218 search_path ovs-vsctl
219 search_path docker
220 search_path uuidgen
221
222 if (ip netns) > /dev/null 2>&1; then :; else
223     echo >&2 "$UTIL: ip utility not found (or it does not support netns),"\
224              "cannot proceed"
225     exit 1
226 fi
227
228 if [ $# -eq 0 ]; then
229     usage
230     exit 0
231 fi
232
233 case $1 in
234     "add-port")
235         shift
236         add_port "$@"
237         exit 0
238         ;;
239     "del-port")
240         shift
241         del_port "$@"
242         exit 0
243         ;;
244     "del-ports")
245         shift
246         del_ports "$@"
247         exit 0
248         ;;
249     "set-vlan")
250         shift
251         set_vlan "$@"
252         exit 0
253         ;;
254     -h | --help)
255         usage
256         exit 0
257         ;;
258     *)
259         echo >&2 "$UTIL: unknown command \"$1\" (use --help for help)"
260         exit 1
261         ;;
262 esac