~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/network/linux_net.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-01-21 11:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20110121114806-v8fvnnl6az4m4ohv
Tags: upstream-2011.1~bzr597
ImportĀ upstreamĀ versionĀ 2011.1~bzr597

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
Implements vlans, bridges, and iptables rules using linux utilities.
18
18
"""
19
19
 
20
 
import logging
21
20
import os
22
 
import signal
23
 
 
24
 
# TODO(ja): does the definition of network_path belong here?
25
21
 
26
22
from nova import db
27
23
from nova import flags
 
24
from nova import log as logging
28
25
from nova import utils
29
26
 
30
27
 
 
28
LOG = logging.getLogger("nova.linux_net")
 
29
 
 
30
 
31
31
def _bin_file(script):
32
32
    """Return the absolute path to scipt in the bin directory"""
33
33
    return os.path.abspath(os.path.join(__file__, "../../../bin", script))
46
46
                    'network device for vlans')
47
47
flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'),
48
48
                        'location of nova-dhcpbridge')
49
 
flags.DEFINE_string('cc_host', utils.get_my_ip(), 'ip of api server')
50
 
flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
51
 
flags.DEFINE_string('routing_source_ip', '127.0.0.1',
 
49
flags.DEFINE_string('routing_source_ip', '$my_ip',
52
50
                    'Public IP of network host')
53
51
flags.DEFINE_bool('use_nova_chains', False,
54
52
                  'use the nova_ routing chains instead of default')
55
53
 
56
 
DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)]
 
54
flags.DEFINE_string('dns_server', None,
 
55
                    'if set, uses specific dns server for dnsmasq')
 
56
flags.DEFINE_string('dmz_cidr', '10.128.0.0/24',
 
57
                    'dmz range that should be accepted')
57
58
 
58
59
 
59
60
def metadata_forward():
60
61
    """Create forwarding rule for metadata"""
61
62
    _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 "
62
63
             "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT "
63
 
             "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port))
 
64
             "--to-destination %s:%s" % (FLAGS.ec2_dmz_host, FLAGS.ec2_port))
64
65
 
65
66
 
66
67
def init_host():
67
68
    """Basic networking setup goes here"""
 
69
 
 
70
    if FLAGS.use_nova_chains:
 
71
        _execute("sudo iptables -N nova_input", check_exit_code=False)
 
72
        _execute("sudo iptables -D %s -j nova_input" % FLAGS.input_chain,
 
73
                 check_exit_code=False)
 
74
        _execute("sudo iptables -A %s -j nova_input" % FLAGS.input_chain)
 
75
 
 
76
        _execute("sudo iptables -N nova_forward", check_exit_code=False)
 
77
        _execute("sudo iptables -D FORWARD -j nova_forward",
 
78
                 check_exit_code=False)
 
79
        _execute("sudo iptables -A FORWARD -j nova_forward")
 
80
 
 
81
        _execute("sudo iptables -N nova_output", check_exit_code=False)
 
82
        _execute("sudo iptables -D OUTPUT -j nova_output",
 
83
                 check_exit_code=False)
 
84
        _execute("sudo iptables -A OUTPUT -j nova_output")
 
85
 
 
86
        _execute("sudo iptables -t nat -N nova_prerouting",
 
87
                 check_exit_code=False)
 
88
        _execute("sudo iptables -t nat -D PREROUTING -j nova_prerouting",
 
89
                 check_exit_code=False)
 
90
        _execute("sudo iptables -t nat -A PREROUTING -j nova_prerouting")
 
91
 
 
92
        _execute("sudo iptables -t nat -N nova_postrouting",
 
93
                 check_exit_code=False)
 
94
        _execute("sudo iptables -t nat -D POSTROUTING -j nova_postrouting",
 
95
                 check_exit_code=False)
 
96
        _execute("sudo iptables -t nat -A POSTROUTING -j nova_postrouting")
 
97
 
 
98
        _execute("sudo iptables -t nat -N nova_snatting",
 
99
                 check_exit_code=False)
 
100
        _execute("sudo iptables -t nat -D POSTROUTING -j nova_snatting",
 
101
                 check_exit_code=False)
 
102
        _execute("sudo iptables -t nat -A POSTROUTING -j nova_snatting")
 
103
 
 
104
        _execute("sudo iptables -t nat -N nova_output", check_exit_code=False)
 
105
        _execute("sudo iptables -t nat -D OUTPUT -j nova_output",
 
106
                 check_exit_code=False)
 
107
        _execute("sudo iptables -t nat -A OUTPUT -j nova_output")
 
108
    else:
 
109
        # NOTE(vish): This makes it easy to ensure snatting rules always
 
110
        #             come after the accept rules in the postrouting chain
 
111
        _execute("sudo iptables -t nat -N SNATTING",
 
112
                 check_exit_code=False)
 
113
        _execute("sudo iptables -t nat -D POSTROUTING -j SNATTING",
 
114
                 check_exit_code=False)
 
115
        _execute("sudo iptables -t nat -A POSTROUTING -j SNATTING")
 
116
 
68
117
    # NOTE(devcamcar): Cloud public SNAT entries and the default
69
118
    # SNAT rule for outbound traffic.
70
 
    _confirm_rule("POSTROUTING", "-t nat -s %s "
 
119
    _confirm_rule("SNATTING", "-t nat -s %s "
71
120
             "-j SNAT --to-source %s"
72
 
             % (FLAGS.fixed_range, FLAGS.routing_source_ip))
 
121
             % (FLAGS.fixed_range, FLAGS.routing_source_ip), append=True)
73
122
 
74
 
    _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" %
75
 
                  FLAGS.fixed_range)
 
123
    _confirm_rule("POSTROUTING", "-t nat -s %s -d %s -j ACCEPT" %
 
124
                  (FLAGS.fixed_range, FLAGS.dmz_cidr))
76
125
    _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" %
77
126
                  {'range': FLAGS.fixed_range})
78
127
 
79
128
 
80
 
def bind_floating_ip(floating_ip):
 
129
def bind_floating_ip(floating_ip, check_exit_code=True):
81
130
    """Bind ip to public interface"""
82
131
    _execute("sudo ip addr add %s dev %s" % (floating_ip,
83
 
                                             FLAGS.public_interface))
 
132
                                             FLAGS.public_interface),
 
133
             check_exit_code=check_exit_code)
84
134
 
85
135
 
86
136
def unbind_floating_ip(floating_ip):
102
152
    """Ensure floating ip forwarding rule"""
103
153
    _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s"
104
154
                           % (floating_ip, fixed_ip))
105
 
    _confirm_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s"
 
155
    _confirm_rule("SNATTING", "-t nat -s %s -j SNAT --to %s"
106
156
                           % (fixed_ip, floating_ip))
107
 
    # TODO(joshua): Get these from the secgroup datastore entries
108
 
    _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT"
109
 
                           % (fixed_ip))
110
 
    for (protocol, port) in DEFAULT_PORTS:
111
 
        _confirm_rule("FORWARD", "-d %s -p %s --dport %s -j ACCEPT"
112
 
            % (fixed_ip, protocol, port))
113
157
 
114
158
 
115
159
def remove_floating_forward(floating_ip, fixed_ip):
116
160
    """Remove forwarding for floating ip"""
117
161
    _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s"
118
162
                          % (floating_ip, fixed_ip))
119
 
    _remove_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s"
 
163
    _remove_rule("SNATTING", "-t nat -s %s -j SNAT --to %s"
120
164
                          % (fixed_ip, floating_ip))
121
 
    _remove_rule("FORWARD", "-d %s -p icmp -j ACCEPT"
122
 
                          % (fixed_ip))
123
 
    for (protocol, port) in DEFAULT_PORTS:
124
 
        _remove_rule("FORWARD", "-d %s -p %s --dport %s -j ACCEPT"
125
 
                              % (fixed_ip, protocol, port))
126
165
 
127
166
 
128
167
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
135
174
    """Create a vlan unless it already exists"""
136
175
    interface = "vlan%s" % vlan_num
137
176
    if not _device_exists(interface):
138
 
        logging.debug("Starting VLAN inteface %s", interface)
 
177
        LOG.debug(_("Starting VLAN inteface %s"), interface)
139
178
        _execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD")
140
179
        _execute("sudo vconfig add %s %s" % (FLAGS.vlan_interface, vlan_num))
141
180
        _execute("sudo ifconfig %s up" % interface)
145
184
def ensure_bridge(bridge, interface, net_attrs=None):
146
185
    """Create a bridge unless it already exists"""
147
186
    if not _device_exists(bridge):
148
 
        logging.debug("Starting Bridge interface for %s", interface)
 
187
        LOG.debug(_("Starting Bridge interface for %s"), interface)
149
188
        _execute("sudo brctl addbr %s" % bridge)
150
189
        _execute("sudo brctl setfd %s 0" % bridge)
151
190
        # _execute("sudo brctl setageing %s 10" % bridge)
158
197
                 net_attrs['gateway'],
159
198
                 net_attrs['broadcast'],
160
199
                 net_attrs['netmask']))
 
200
        if(FLAGS.use_ipv6):
 
201
            _execute("sudo ifconfig %s add %s up" % \
 
202
                     (bridge,
 
203
                      net_attrs['cidr_v6']))
161
204
    else:
162
205
        _execute("sudo ifconfig %s up" % bridge)
 
206
    if FLAGS.use_nova_chains:
 
207
        (out, err) = _execute("sudo iptables -N nova_forward",
 
208
                              check_exit_code=False)
 
209
        if err != 'iptables: Chain already exists.\n':
 
210
            # NOTE(vish): chain didn't exist link chain
 
211
            _execute("sudo iptables -D FORWARD -j nova_forward",
 
212
                     check_exit_code=False)
 
213
            _execute("sudo iptables -A FORWARD -j nova_forward")
 
214
 
163
215
    _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge)
164
216
    _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge)
 
217
    _execute("sudo iptables -N nova-local", check_exit_code=False)
 
218
    _confirm_rule("FORWARD", "-j nova-local")
165
219
 
166
220
 
167
221
def get_dhcp_hosts(context, network_id):
202
256
                _execute('sudo kill -HUP %d' % pid)
203
257
                return
204
258
            except Exception as exc:  # pylint: disable-msg=W0703
205
 
                logging.debug("Hupping dnsmasq threw %s", exc)
 
259
                LOG.debug(_("Hupping dnsmasq threw %s"), exc)
206
260
        else:
207
 
            logging.debug("Pid %d is stale, relaunching dnsmasq", pid)
 
261
            LOG.debug(_("Pid %d is stale, relaunching dnsmasq"), pid)
208
262
 
209
263
    # FLAGFILE and DNSMASQ_INTERFACE in env
210
264
    env = {'FLAGFILE': FLAGS.dhcpbridge_flagfile,
213
267
    _execute(command, addl_env=env)
214
268
 
215
269
 
 
270
def update_ra(context, network_id):
 
271
    network_ref = db.network_get(context, network_id)
 
272
 
 
273
    conffile = _ra_file(network_ref['bridge'], 'conf')
 
274
    with open(conffile, 'w') as f:
 
275
        conf_str = """
 
276
interface %s
 
277
{
 
278
   AdvSendAdvert on;
 
279
   MinRtrAdvInterval 3;
 
280
   MaxRtrAdvInterval 10;
 
281
   prefix %s
 
282
   {
 
283
        AdvOnLink on;
 
284
        AdvAutonomous on;
 
285
   };
 
286
};
 
287
""" % (network_ref['bridge'], network_ref['cidr_v6'])
 
288
        f.write(conf_str)
 
289
 
 
290
    # Make sure radvd can actually read it (it setuid()s to "nobody")
 
291
    os.chmod(conffile, 0644)
 
292
 
 
293
    pid = _ra_pid_for(network_ref['bridge'])
 
294
 
 
295
    # if radvd is already running, then tell it to reload
 
296
    if pid:
 
297
        out, _err = _execute('cat /proc/%d/cmdline'
 
298
                             % pid, check_exit_code=False)
 
299
        if conffile in out:
 
300
            try:
 
301
                _execute('sudo kill -HUP %d' % pid)
 
302
                return
 
303
            except Exception as exc:  # pylint: disable-msg=W0703
 
304
                LOG.debug(_("Hupping radvd threw %s"), exc)
 
305
        else:
 
306
            LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
 
307
    command = _ra_cmd(network_ref)
 
308
    _execute(command)
 
309
    db.network_update(context, network_id,
 
310
                      {"ra_server":
 
311
                       utils.get_my_linklocal(network_ref['bridge'])})
 
312
 
 
313
 
216
314
def _host_dhcp(fixed_ip_ref):
217
315
    """Return a host string for an address"""
218
316
    instance_ref = fixed_ip_ref['instance']
224
322
def _execute(cmd, *args, **kwargs):
225
323
    """Wrapper around utils._execute for fake_network"""
226
324
    if FLAGS.fake_network:
227
 
        logging.debug("FAKE NET: %s", cmd)
 
325
        LOG.debug("FAKE NET: %s", cmd)
228
326
        return "fake", 0
229
327
    else:
230
328
        return utils.execute(cmd, *args, **kwargs)
236
334
    return not err
237
335
 
238
336
 
239
 
def _confirm_rule(chain, cmd):
 
337
def _confirm_rule(chain, cmd, append=False):
240
338
    """Delete and re-add iptables rule"""
241
339
    if FLAGS.use_nova_chains:
242
340
        chain = "nova_%s" % chain.lower()
 
341
    if append:
 
342
        loc = "-A"
 
343
    else:
 
344
        loc = "-I"
243
345
    _execute("sudo iptables --delete %s %s" % (chain, cmd),
244
346
             check_exit_code=False)
245
 
    _execute("sudo iptables -I %s %s" % (chain, cmd))
 
347
    _execute("sudo iptables %s %s %s" % (loc, chain, cmd))
246
348
 
247
349
 
248
350
def _remove_rule(chain, cmd):
265
367
           ' --dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'),
266
368
           ' --dhcp-script=%s' % FLAGS.dhcpbridge,
267
369
           ' --leasefile-ro']
 
370
    if FLAGS.dns_server:
 
371
        cmd.append(' -h -R --server=%s' % FLAGS.dns_server)
 
372
    return ''.join(cmd)
 
373
 
 
374
 
 
375
def _ra_cmd(net):
 
376
    """Builds radvd command"""
 
377
    cmd = ['sudo -E radvd',
 
378
#           ' -u nobody',
 
379
           ' -C %s' % _ra_file(net['bridge'], 'conf'),
 
380
           ' -p %s' % _ra_file(net['bridge'], 'pid')]
268
381
    return ''.join(cmd)
269
382
 
270
383
 
276
389
        try:
277
390
            _execute('sudo kill -TERM %d' % pid)
278
391
        except Exception as exc:  # pylint: disable-msg=W0703
279
 
            logging.debug("Killing dnsmasq threw %s", exc)
 
392
            LOG.debug(_("Killing dnsmasq threw %s"), exc)
280
393
 
281
394
 
282
395
def _dhcp_file(bridge, kind):
289
402
                                              kind))
290
403
 
291
404
 
 
405
def _ra_file(bridge, kind):
 
406
    """Return path to a pid or conf file for a bridge"""
 
407
 
 
408
    if not os.path.exists(FLAGS.networks_path):
 
409
        os.makedirs(FLAGS.networks_path)
 
410
    return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
 
411
                                              bridge,
 
412
                                              kind))
 
413
 
 
414
 
292
415
def _dnsmasq_pid_for(bridge):
293
416
    """Returns the pid for prior dnsmasq instance for a bridge
294
417
 
302
425
    if os.path.exists(pid_file):
303
426
        with open(pid_file, 'r') as f:
304
427
            return int(f.read())
 
428
 
 
429
 
 
430
def _ra_pid_for(bridge):
 
431
    """Returns the pid for prior radvd instance for a bridge
 
432
 
 
433
    Returns None if no pid file exists
 
434
 
 
435
    If machine has rebooted pid might be incorrect (caller should check)
 
436
    """
 
437
 
 
438
    pid_file = _ra_file(bridge, 'pid')
 
439
 
 
440
    if os.path.exists(pid_file):
 
441
        with open(pid_file, 'r') as f:
 
442
            return int(f.read())