Statistics
| Branch: | Tag: | Revision:

root / tools / kvm-ifup-os.in @ 65b526e7

History | View | Annotate | Download (8.8 kB)

1
#!/bin/bash
2
#
3

    
4
# Copyright (C) 2014 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
# This script is a hook called to configure new TAP network interfaces
22
# used for instance communication, and it should be called whenever a
23
# new instance is started.
24
#
25
# This script configures the new interface but it also performs
26
# maintenance on the network interfaces that have been configured
27
# before, by checking whether those TAP interfaces still exist, etc.
28
#
29
# This script also controls the DHCP server that leases IP address for
30
# instances, i.e., the NICs inside the instances, not the TAP
31
# interfaces.  The DHCP server is started and restarted (or reHUPed)
32
# as necessary and always with up-to-date configuration files.
33
#
34
# This script expects the following environment variables
35
#
36
#   INTERFACE: network interface name to be configured
37
#   MODE: networking mode for 'INTERFACE' (must be 'routed')
38
#   MAC: MAC address for 'INTERFACE'
39
#   IP: IP address for 'INTERFACE'
40

    
41
source @PKGLIBDIR@/net-common
42

    
43
readonly NETMASK=255.255.255.255
44
readonly DNSMASQ_CONF=/var/run/ganeti/dnsmasq.conf
45
readonly DNSMASQ_HOSTS=/var/run/ganeti/dnsmasq.hosts
46
readonly DNSMASQ_PID=/var/run/ganeti/dnsmasq.pid
47

    
48
# join intercalates a sequence of arguments using the given separator
49
function join {
50
  local IFS="$1"
51
  shift
52
  echo "$*"
53
}
54

    
55
# restart_dnsmasq restarts the DHCP server dnsmasq with the (possibly
56
# up-to-date) configuration file.
57
#
58
# If all instances have been terminated, which means there are no more
59
# TAP network interfaces to monitor or IP addresses to lease, the DHCP
60
# server is terminated through 'SIGTERM'.
61
#
62
# If there are still instances running, then:
63
#  - if the DHCP server is running, a 'SIGHUP' will be sent to the
64
#    dnsmasq process which will cause the configuration file to be
65
#    re-read, while keeping the process running
66
#  - if the DHCP server is not running, it will be started, and the
67
#    configuration file will be passed it
68
function restart_dnsmasq {
69
  SIGNAL=
70

    
71
  if [ -z "$ALIVE_INTERFACES" -o -z "$ALIVE_LEASES" ]
72
  then
73
      SIGNAL=TERM
74
  else
75
      SIGNAL=HUP
76
  fi
77

    
78
  RUNNING=
79

    
80
  if [ -f "$DNSMASQ_PID" ]
81
  then
82
      PID=$(cat $DNSMASQ_PID)
83
      if [ -n "$PID" ] && ps -p "$PID"
84
      then
85
	  RUNNING=yes
86
      fi
87
  fi
88

    
89
  KILLED=
90

    
91
  if [ "$RUNNING" = yes ]
92
  then
93
      kill -$SIGNAL $PID
94

    
95
      if [ "$SIGNAL" = TERM ]
96
      then
97
	  KILLED=yes
98
      fi
99
  fi
100

    
101
  if [ "$KILLED" = yes ]
102
  then
103
      rm -f $DNSMASQ_PID
104
  fi
105

    
106
  if [ "$RUNNING" != yes -o "$KILLED" == yes ]
107
  then
108
      if [ -n "$ALIVE_INTERFACES" -a -n "$ALIVE_LEASES" ]
109
      then
110
	  dnsmasq -C $DNSMASQ_CONF
111
      fi
112
  fi
113

    
114
  return 0
115
}
116

    
117
# Check that environment variable 'INTERFACE' exists.
118
#
119
# This environment variable holds the TAP network interface that
120
# should be configured by this script.  Ganeti always passes it,
121
# but... :)
122
if [ -z "$INTERFACE" ]
123
then
124
  echo kvm-vif-bridge: Failed to configure communication mechanism \
125
      interface because the \'INTERFACE\' environment variable was \
126
      not specified to the \'kvm-vif-bridge\' script
127
  exit 1
128
fi
129

    
130
# Check that environment variable 'MODE' exists.
131
#
132
# See comment about environment variable 'INTERFACE'.
133
if [ -z "$MODE" ]
134
then
135
  echo kvm-vif-bridge: Failed to configure communication mechanism \
136
      interface because the \'MODE\' environment variable was \
137
      not specified to the \'kvm-vif-bridge\' script
138
  exit 1
139
fi
140

    
141
# Check whether the interface being configured has instance
142
# communication enabled, otherwise exit this script.
143
if ! is_instance_communication_tap; then exit 0; fi
144

    
145
# Check that environment variable 'MAC' exists.
146
#
147
# See comment about environment variable 'INTERFACE'.
148
if [ -z "$MAC" ]
149
then
150
  echo kvm-vif-bridge: Failed to configure communication mechanism \
151
      interface because the \'MAC\' environment variable was \
152
      not specified to the \'kvm-vif-bridge\' script
153
  exit 1
154
fi
155

    
156
# Check that environment variable 'IP' exists.
157
#
158
# See comment about environment variable 'INTERFACE'.
159
if [ -z "$IP" ]
160
then
161
  echo kvm-vif-bridge: Failed to configure communication mechanism \
162
      interface because the \'IP\' environment variable was \
163
      not specified to the \'kvm-vif-bridge\' script
164
  exit 1
165
fi
166

    
167
# Configure the TAP interface
168
#
169
# Ganeti defers the configuration of instance network interfaces to
170
# hooks, therefore, we must configure the interface's network address,
171
# netmask, and IP address.
172
#
173
# The TAP network interface, which is used by the instance
174
# communication, is part of the network 169.254.0.0/16 and has the IP
175
# 169.254.169.254.  Because all network interfaces used in the
176
# instance communication have the same IP, the routing table must also
177
# be configured, and that is done at a later step.
178
#
179
# Note the interface must be marked as up before configuring the
180
# routing table and before starting/restarting the DHCP server.
181
#
182
# Note also that we don't have to check whether the interface is
183
# already configured because reconfiguring the interface with the same
184
# parameters does not produce an error.
185
ifconfig $INTERFACE 169.254.169.254 netmask $NETMASK up
186

    
187
# Configure the routing table
188
#
189
# Given that all TAP network interfaces in the instance communication
190
# have the same IP address, the routing table must be configured in
191
# order to properly route traffic from the host to the guests.
192
#
193
# Note that we must first check if a duplicate routing rule has
194
# already been added to the routing table, as this operation will fail
195
# if we try to add a routing rule that already exists.
196
ACTIVE_IP=$(ip route | grep "dev $INTERFACE" | awk '{ print $1 }')
197

    
198
if [ -z "$ACTIVE_IP" -o "$ACTIVE_IP" != "$IP" ]
199
then
200
  route add -host $IP dev $INTERFACE
201
fi
202

    
203
# Ensure the DHCP server configuration files exist
204
touch $DNSMASQ_CONF
205
chmod 0644 $DNSMASQ_CONF
206

    
207
touch $DNSMASQ_HOSTS
208
chmod 0644 $DNSMASQ_HOSTS
209

    
210
# Determine dnsmasq operational mode.
211
#
212
# The DHCP server dnsmasq can run in different modes.  In this version
213
# of the script, only the mode 'bind-dynamic' is supported.  Please
214
# refer to the dnsmasq FAQ for a detailed of each mode.
215
#
216
# Note that dnsmasq might already be running, therefore, we don't need
217
# to determine which modes are supported by this DHCP server.
218
# Instead, we just read the current mode from the configuration file.
219
DNSMASQ_MODE=$(head -n 1 $DNSMASQ_CONF)
220

    
221
if [ -z "$DNSMASQ_MODE" ]
222
then
223
  BIND_DYNAMIC=$(dnsmasq --help | grep -e --bind-dynamic)
224

    
225
  if [ -z "$BIND_DYNAMIC" ]
226
  then
227
    echo kvm-vif-bridge: dnsmasq mode \"bind-dynamic\" is not supported
228
    exit 1
229
  fi
230

    
231
  DNSMASQ_MODE=bind-dynamic
232
fi
233

    
234
# Determine the interfaces that should go in the configuration file.
235
#
236
# The TAP network interfaces used by the instance communication are
237
# named after the following pattern
238
#
239
#  gnt.com.%d
240
#
241
# where '%d' is a unique number within the host.  Fortunately, dnsmasq
242
# supports binding to specific network interfaces via a pattern.
243
ALIVE_INTERFACES=${GANETI_TAP}.*
244

    
245
# Determine which of the leases are not duplicated and should go in
246
# the new configuration file for the DHCP server.
247
#
248
# Given that instances come and go, it is possible that we offer more
249
# leases that necessary and, worse, that we have duplicate leases,
250
# that is, the same IP address for the same/different MAC addresses.
251
# Duplicate leases must be eliminated before being written to the
252
# configuration file.
253
CONF_LEASES=$(cat $DNSMASQ_HOSTS)
254
CONF_LEASES=$(join $'\n' $CONF_LEASES | sort -u)
255

    
256
ALIVE_LEASES=( $MAC,$IP )
257

    
258
for i in $CONF_LEASES
259
do
260
  LEASE_MAC=$(echo $i | cut -d "," -f 1)
261
  LEASE_IP=$(echo $i | cut -d "," -f 2)
262
  if [ "$LEASE_MAC" != "$MAC" -a "$LEASE_IP" != "$IP" ]
263
  then
264
      ALIVE_LEASES=( ${ALIVE_LEASES[@]} $i )
265
  fi
266
done
267

    
268
ALIVE_LEASES=$(echo ${ALIVE_LEASES[@]} | sort -u)
269

    
270
# Update dnsmasq configuration.
271
#
272
# Write the parameters we have collected before into the new dnsmasq
273
# configuration file.  Also, write the new leases into the new dnsmasq
274
# hosts file.  Finally, restart dnsmasq with the new configuration
275
# files.
276
cat > $DNSMASQ_CONF <<EOF
277
$DNSMASQ_MODE
278
dhcp-authoritative
279
dhcp-hostsfile=$DNSMASQ_HOSTS
280
dhcp-range=169.254.0.0,static,255.255.0.0
281
except-interface=eth*
282
except-interface=lo
283
leasefile-ro
284
no-hosts
285
no-ping
286
no-resolv
287
pid-file=$DNSMASQ_PID
288
port=0
289
strict-order
290
EOF
291
for i in $ALIVE_INTERFACES; do echo interface=$i >> $DNSMASQ_CONF; done
292

    
293
echo -n > $DNSMASQ_HOSTS
294
for i in $ALIVE_LEASES; do echo $i >> $DNSMASQ_HOSTS; done
295

    
296
restart_dnsmasq