~zulcss/ubuntu/precise/quantum/trunk

« back to all changes in this revision

Viewing changes to quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py

  • Committer: Chuck Short
  • Date: 2012-11-26 19:51:11 UTC
  • mfrom: (26.1.1 raring-proposed)
  • Revision ID: zulcss@ubuntu.com-20121126195111-jnz2cr4xi6whemw2
* New upstream release for the Ubuntu Cloud Archive.
* debian/patches/*: Refreshed for opening of Grizzly.
* New upstream release.
* debian/rules: FTFBS if there is missing binaries.
* debian/quantum-server.install: Add quantum-debug.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
# Quantum OpenVSwitch Plugin.
23
23
# @author: Sumit Naiksatam, Cisco Systems, Inc.
24
24
 
25
 
import logging
26
25
import os
27
 
import shlex
28
 
import signal
29
 
import subprocess
30
26
import sys
31
27
import time
32
28
 
33
29
import eventlet
34
30
import pyudev
35
 
from sqlalchemy.ext.sqlsoup import SqlSoup
36
31
 
37
32
from quantum.agent.linux import ip_lib
38
33
from quantum.agent.linux import utils
39
34
from quantum.agent import rpc as agent_rpc
40
35
from quantum.common import config as logging_config
41
 
from quantum.common import constants
42
36
from quantum.common import topics
 
37
from quantum.common import utils as q_utils
 
38
from quantum import context
43
39
from quantum.openstack.common import cfg
44
 
from quantum.openstack.common import context
45
 
from quantum.openstack.common import rpc
 
40
from quantum.openstack.common import log as logging
46
41
from quantum.openstack.common.rpc import dispatcher
47
42
from quantum.plugins.linuxbridge.common import config
48
43
from quantum.plugins.linuxbridge.common import constants as lconst
49
44
 
50
 
logging.basicConfig()
 
45
 
51
46
LOG = logging.getLogger(__name__)
52
47
 
53
48
BRIDGE_NAME_PREFIX = "brq"
54
 
GATEWAY_INTERFACE_PREFIX = "gw-"
55
49
TAP_INTERFACE_PREFIX = "tap"
56
50
BRIDGE_FS = "/sys/devices/virtual/net/"
57
51
BRIDGE_NAME_PLACEHOLDER = "bridge_name"
58
52
BRIDGE_INTERFACES_FS = BRIDGE_FS + BRIDGE_NAME_PLACEHOLDER + "/brif/"
59
53
DEVICE_NAME_PLACEHOLDER = "device_name"
60
54
BRIDGE_PORT_FS_FOR_DEVICE = BRIDGE_FS + DEVICE_NAME_PLACEHOLDER + "/brport"
61
 
VLAN_BINDINGS = "vlan_bindings"
62
 
PORT_BINDINGS = "port_bindings"
63
55
 
64
56
 
65
57
class LinuxBridge:
147
139
        except RuntimeError:
148
140
            return self._get_prefixed_ip_link_devices(TAP_INTERFACE_PREFIX)
149
141
 
150
 
    def get_all_gateway_devices(self):
151
 
        try:
152
 
            return self._get_prefixed_tap_devices(GATEWAY_INTERFACE_PREFIX)
153
 
        except RuntimeError:
154
 
            return self._get_prefixed_ip_link_devices(GATEWAY_INTERFACE_PREFIX)
155
 
 
156
142
    def get_bridge_for_tap_device(self, tap_device_name):
157
143
        bridges = self.get_all_quantum_bridges()
158
144
        for bridge in bridges:
281
267
                          bridge_name, e)
282
268
                return
283
269
 
 
270
    def ensure_physical_in_bridge(self, network_id,
 
271
                                  physical_network,
 
272
                                  vlan_id):
 
273
        physical_interface = self.interface_mappings.get(physical_network)
 
274
        if not physical_interface:
 
275
            LOG.error("No mapping for physical network %s" %
 
276
                      physical_network)
 
277
            return False
 
278
 
 
279
        if int(vlan_id) == lconst.FLAT_VLAN_ID:
 
280
            self.ensure_flat_bridge(network_id, physical_interface)
 
281
        else:
 
282
            self.ensure_vlan_bridge(network_id, physical_interface,
 
283
                                    vlan_id)
 
284
        return True
 
285
 
284
286
    def add_tap_interface(self, network_id, physical_network, vlan_id,
285
287
                          tap_device_name):
286
288
        """
287
289
        If a VIF has been plugged into a network, this function will
288
290
        add the corresponding tap device to the relevant bridge
289
291
        """
290
 
        if not tap_device_name:
291
 
            return False
292
 
 
293
292
        if not self.device_exists(tap_device_name):
294
 
            LOG.debug("Tap device: %s does not exist on this host, skipped" %
295
 
                      tap_device_name)
 
293
            LOG.debug(_("Tap device: %s does not exist on "
 
294
                        "this host, skipped" % tap_device_name))
296
295
            return False
297
296
 
298
 
        current_bridge_name = self.get_bridge_for_tap_device(tap_device_name)
299
297
        bridge_name = self.get_bridge_name(network_id)
300
 
        if bridge_name == current_bridge_name:
301
 
            return False
302
 
        LOG.debug("Adding device %s to bridge %s" % (tap_device_name,
303
 
                                                     bridge_name))
304
 
        if current_bridge_name:
305
 
            if utils.execute(['brctl', 'delif', current_bridge_name,
306
 
                              tap_device_name], root_helper=self.root_helper):
307
 
                return False
308
 
 
309
298
        if int(vlan_id) == lconst.LOCAL_VLAN_ID:
310
299
            self.ensure_local_bridge(network_id)
311
300
        else:
312
 
            physical_interface = self.interface_mappings.get(physical_network)
313
 
            if not physical_interface:
314
 
                LOG.error("No mapping for physical network %s" %
315
 
                          physical_network)
316
 
                return False
317
 
 
318
 
            if int(vlan_id) == lconst.FLAT_VLAN_ID:
319
 
                self.ensure_flat_bridge(network_id, physical_interface)
320
 
            else:
321
 
                self.ensure_vlan_bridge(network_id, physical_interface,
322
 
                                        vlan_id)
323
 
 
324
 
        if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
325
 
                         root_helper=self.root_helper):
326
 
            return False
327
 
 
328
 
        LOG.debug("Done adding device %s to bridge %s" % (tap_device_name,
329
 
                                                          bridge_name))
 
301
            result = self.ensure_physical_in_bridge(network_id,
 
302
                                                    physical_network,
 
303
                                                    vlan_id)
 
304
            if not result:
 
305
                return False
 
306
 
 
307
        # Check if device needs to be added to bridge
 
308
        tap_device_in_bridge = self.get_bridge_for_tap_device(tap_device_name)
 
309
        if not tap_device_in_bridge:
 
310
            msg = _("Adding device %(tap_device_name)s to bridge "
 
311
                    "%(bridge_name)s") % locals()
 
312
            LOG.debug(msg)
 
313
            if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
 
314
                             root_helper=self.root_helper):
 
315
                return False
 
316
        else:
 
317
            msg = _("%(tap_device_name)s already exists on bridge "
 
318
                    "%(bridge_name)s") % locals()
 
319
            LOG.debug(msg)
330
320
        return True
331
321
 
332
322
    def add_interface(self, network_id, physical_network, vlan_id,
333
 
                      interface_id):
334
 
        if not interface_id:
335
 
            """
336
 
            Since the VIF id is null, no VIF is plugged into this port
337
 
            no more processing is required
338
 
            """
339
 
            return False
340
 
 
341
 
        if interface_id.startswith(GATEWAY_INTERFACE_PREFIX):
342
 
            return self.add_tap_interface(network_id,
343
 
                                          physical_network, vlan_id,
344
 
                                          interface_id)
345
 
        else:
346
 
            tap_device_name = self.get_tap_device_name(interface_id)
347
 
            return self.add_tap_interface(network_id,
348
 
                                          physical_network, vlan_id,
349
 
                                          tap_device_name)
 
323
                      port_id):
 
324
        tap_device_name = self.get_tap_device_name(port_id)
 
325
        return self.add_tap_interface(network_id,
 
326
                                      physical_network, vlan_id,
 
327
                                      tap_device_name)
350
328
 
351
329
    def delete_vlan_bridge(self, bridge_name):
352
330
        if self.device_exists(bridge_name):
427
405
        port = kwargs.get('port')
428
406
        if port['admin_state_up']:
429
407
            vlan_id = kwargs.get('vlan_id')
 
408
            physical_network = kwargs.get('physical_network')
430
409
            # create the networking for the port
431
410
            self.linux_br.add_interface(port['network_id'],
 
411
                                        physical_network,
432
412
                                        vlan_id,
433
413
                                        port['id'])
434
414
        else:
445
425
        return dispatcher.RpcDispatcher([self])
446
426
 
447
427
 
448
 
class LinuxBridgeQuantumAgentDB:
449
 
 
450
 
    def __init__(self, interface_mappings, polling_interval,
451
 
                 reconnect_interval, root_helper, db_connection_url):
452
 
        self.polling_interval = polling_interval
453
 
        self.root_helper = root_helper
454
 
        self.setup_linux_bridge(interface_mappings)
455
 
        self.reconnect_interval = reconnect_interval
456
 
        self.db_connected = False
457
 
        self.db_connection_url = db_connection_url
458
 
 
459
 
    def setup_linux_bridge(self, interface_mappings):
460
 
        self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
461
 
 
462
 
    def process_port_binding(self, network_id, interface_id,
463
 
                             physical_network, vlan_id):
464
 
        return self.linux_br.add_interface(network_id,
465
 
                                           physical_network, vlan_id,
466
 
                                           interface_id)
467
 
 
468
 
    def remove_port_binding(self, network_id, interface_id):
469
 
        bridge_name = self.linux_br.get_bridge_name(network_id)
470
 
        tap_device_name = self.linux_br.get_tap_device_name(interface_id)
471
 
        return self.linux_br.remove_interface(bridge_name, tap_device_name)
472
 
 
473
 
    def process_unplugged_interfaces(self, plugged_interfaces):
474
 
        """
475
 
        If there are any tap devices that are not corresponding to the
476
 
        list of attached VIFs, then those are corresponding to recently
477
 
        unplugged VIFs, so we need to remove those tap devices from their
478
 
        current bridge association
479
 
        """
480
 
        plugged_tap_device_names = []
481
 
        plugged_gateway_device_names = []
482
 
        for interface in plugged_interfaces:
483
 
            if interface.startswith(GATEWAY_INTERFACE_PREFIX):
484
 
                """
485
 
                The name for the gateway devices is set by the linux net
486
 
                driver, hence we use the name as is
487
 
                """
488
 
                plugged_gateway_device_names.append(interface)
489
 
            else:
490
 
                tap_device_name = self.linux_br.get_tap_device_name(interface)
491
 
                plugged_tap_device_names.append(tap_device_name)
492
 
 
493
 
        LOG.debug("plugged tap device names %s" % plugged_tap_device_names)
494
 
        for tap_device in self.linux_br.get_all_tap_devices():
495
 
            if tap_device not in plugged_tap_device_names:
496
 
                current_bridge_name = (
497
 
                    self.linux_br.get_bridge_for_tap_device(tap_device))
498
 
                if current_bridge_name:
499
 
                    self.linux_br.remove_interface(current_bridge_name,
500
 
                                                   tap_device)
501
 
 
502
 
        for gw_device in self.linux_br.get_all_gateway_devices():
503
 
            if gw_device not in plugged_gateway_device_names:
504
 
                current_bridge_name = (
505
 
                    self.linux_br.get_bridge_for_tap_device(gw_device))
506
 
                if current_bridge_name:
507
 
                    self.linux_br.remove_interface(current_bridge_name,
508
 
                                                   gw_device)
509
 
 
510
 
    def process_deleted_networks(self, vlan_bindings):
511
 
        current_quantum_networks = vlan_bindings.keys()
512
 
        current_quantum_bridge_names = []
513
 
        for network in current_quantum_networks:
514
 
            bridge_name = self.linux_br.get_bridge_name(network)
515
 
            current_quantum_bridge_names.append(bridge_name)
516
 
 
517
 
        quantum_bridges_on_this_host = self.linux_br.get_all_quantum_bridges()
518
 
        for bridge in quantum_bridges_on_this_host:
519
 
            if bridge not in current_quantum_bridge_names:
520
 
                self.linux_br.delete_vlan_bridge(bridge)
521
 
 
522
 
    def manage_networks_on_host(self, db,
523
 
                                old_vlan_bindings,
524
 
                                old_port_bindings):
525
 
        vlan_bindings = {}
526
 
        try:
527
 
            network_binds = db.network_bindings.all()
528
 
        except Exception as e:
529
 
            LOG.info("Unable to get network bindings! Exception: %s" % e)
530
 
            self.db_connected = False
531
 
            return {VLAN_BINDINGS: {},
532
 
                    PORT_BINDINGS: []}
533
 
 
534
 
        vlans_string = ""
535
 
        for bind in network_binds:
536
 
            entry = {'network_id': bind.network_id,
537
 
                     'physical_network': bind.physical_network,
538
 
                     'vlan_id': bind.vlan_id}
539
 
            vlan_bindings[bind.network_id] = entry
540
 
            vlans_string = "%s %s" % (vlans_string, entry)
541
 
 
542
 
        port_bindings = []
543
 
        try:
544
 
            port_binds = db.ports.all()
545
 
        except Exception as e:
546
 
            LOG.info("Unable to get port bindings! Exception: %s" % e)
547
 
            self.db_connected = False
548
 
            return {VLAN_BINDINGS: {},
549
 
                    PORT_BINDINGS: []}
550
 
 
551
 
        all_bindings = {}
552
 
        for bind in port_binds:
553
 
            append_entry = False
554
 
            all_bindings[bind.id] = bind
555
 
            entry = {'network_id': bind.network_id,
556
 
                     'uuid': bind.id,
557
 
                     'status': bind.status,
558
 
                     'interface_id': bind.id}
559
 
            append_entry = bind.admin_state_up
560
 
            if append_entry:
561
 
                port_bindings.append(entry)
562
 
 
563
 
        plugged_interfaces = []
564
 
        ports_string = ""
565
 
        for pb in port_bindings:
566
 
            ports_string = "%s %s" % (ports_string, pb)
567
 
            port_id = pb['uuid']
568
 
            interface_id = pb['interface_id']
569
 
            network_id = pb['network_id']
570
 
 
571
 
            physical_network = vlan_bindings[network_id]['physical_network']
572
 
            vlan_id = str(vlan_bindings[network_id]['vlan_id'])
573
 
            if self.process_port_binding(network_id,
574
 
                                         interface_id,
575
 
                                         physical_network,
576
 
                                         vlan_id):
577
 
                all_bindings[port_id].status = constants.PORT_STATUS_ACTIVE
578
 
 
579
 
            plugged_interfaces.append(interface_id)
580
 
 
581
 
        if old_port_bindings != port_bindings:
582
 
            LOG.debug("Port-bindings: %s" % ports_string)
583
 
 
584
 
        self.process_unplugged_interfaces(plugged_interfaces)
585
 
 
586
 
        if old_vlan_bindings != vlan_bindings:
587
 
            LOG.debug("VLAN-bindings: %s" % vlans_string)
588
 
 
589
 
        self.process_deleted_networks(vlan_bindings)
590
 
 
591
 
        try:
592
 
            db.commit()
593
 
        except Exception as e:
594
 
            LOG.info("Unable to update database! Exception: %s" % e)
595
 
            db.rollback()
596
 
            vlan_bindings = {}
597
 
            port_bindings = []
598
 
 
599
 
        return {VLAN_BINDINGS: vlan_bindings,
600
 
                PORT_BINDINGS: port_bindings}
601
 
 
602
 
    def daemon_loop(self):
603
 
        old_vlan_bindings = {}
604
 
        old_port_bindings = []
605
 
        self.db_connected = False
606
 
 
607
 
        while True:
608
 
            if not self.db_connected:
609
 
                time.sleep(self.reconnect_interval)
610
 
                db = SqlSoup(self.db_connection_url)
611
 
                self.db_connected = True
612
 
                LOG.info("Connecting to database \"%s\" on %s" %
613
 
                         (db.engine.url.database, db.engine.url.host))
614
 
            bindings = self.manage_networks_on_host(db,
615
 
                                                    old_vlan_bindings,
616
 
                                                    old_port_bindings)
617
 
            old_vlan_bindings = bindings[VLAN_BINDINGS]
618
 
            old_port_bindings = bindings[PORT_BINDINGS]
619
 
            time.sleep(self.polling_interval)
620
 
 
621
 
 
622
428
class LinuxBridgeQuantumAgentRPC:
623
429
 
624
430
    def __init__(self, interface_mappings, polling_interval,
636
442
            if devices:
637
443
                mac = utils.get_interface_mac(devices[0].name)
638
444
            else:
639
 
                LOG.error("Unable to obtain MAC of any device for agent_id")
 
445
                LOG.error("Unable to obtain MAC address for unique ID. "
 
446
                          "Agent terminated!")
640
447
                exit(1)
641
448
        self.agent_id = '%s%s' % ('lb', (mac.replace(":", "")))
642
449
        LOG.info("RPC agent_id: %s" % self.agent_id)
645
452
        self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
646
453
 
647
454
        # RPC network init
648
 
        self.context = context.RequestContext('quantum', 'quantum',
649
 
                                              is_admin=False)
 
455
        self.context = context.get_admin_context_without_session()
650
456
        # Handle updates from service
651
457
        self.callbacks = LinuxBridgeRpcCallbacks(self.context,
652
458
                                                 self.linux_br)
664
470
    def setup_linux_bridge(self, interface_mappings):
665
471
        self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
666
472
 
667
 
    def process_port_binding(self, network_id, interface_id,
668
 
                             physical_network, vlan_id):
669
 
        return self.linux_br.add_interface(network_id,
670
 
                                           physical_network, vlan_id,
671
 
                                           interface_id)
672
 
 
673
473
    def remove_port_binding(self, network_id, interface_id):
674
474
        bridge_name = self.linux_br.get_bridge_name(network_id)
675
475
        tap_device_name = self.linux_br.get_tap_device_name(interface_id)
712
512
    def treat_devices_added(self, devices):
713
513
        resync = False
714
514
        for device in devices:
715
 
            LOG.info("Port %s added", device)
 
515
            LOG.debug("Port %s added", device)
716
516
            try:
717
517
                details = self.plugin_rpc.get_device_details(self.context,
718
518
                                                             device,
725
525
                LOG.info("Port %s updated. Details: %s", device, details)
726
526
                if details['admin_state_up']:
727
527
                    # create the networking for the port
728
 
                    self.process_port_binding(details['network_id'],
729
 
                                              details['port_id'],
730
 
                                              details['physical_network'],
731
 
                                              details['vlan_id'])
 
528
                    self.linux_br.add_interface(details['network_id'],
 
529
                                                details['physical_network'],
 
530
                                                details['vlan_id'],
 
531
                                                details['port_id'])
732
532
                else:
733
533
                    self.remove_port_binding(details['network_id'],
734
534
                                             details['port_id'])
735
535
            else:
736
 
                LOG.debug("Device %s not defined on plugin", device)
 
536
                LOG.info("Device %s not defined on plugin", device)
737
537
        return resync
738
538
 
739
539
    def treat_devices_removed(self, devices):
792
592
    # (TODO) gary - swap with common logging
793
593
    logging_config.setup_logging(cfg.CONF)
794
594
 
795
 
    interface_mappings = {}
796
 
    for mapping in cfg.CONF.LINUX_BRIDGE.physical_interface_mappings:
797
 
        try:
798
 
            physical_network, physical_interface = mapping.split(':')
799
 
            interface_mappings[physical_network] = physical_interface
800
 
            LOG.debug("physical network %s mapped to physical interface %s" %
801
 
                      (physical_network, physical_interface))
802
 
        except ValueError as ex:
803
 
            LOG.error("Invalid physical interface mapping: \'%s\' - %s" %
804
 
                      (mapping, ex))
805
 
            sys.exit(1)
 
595
    try:
 
596
        interface_mappings = q_utils.parse_mappings(
 
597
            cfg.CONF.LINUX_BRIDGE.physical_interface_mappings)
 
598
    except ValueError as e:
 
599
        LOG.error(_("Parsing physical_interface_mappings failed: %s."
 
600
                    " Agent terminated!"), e)
 
601
        sys.exit(1)
 
602
    LOG.info(_("Interface mappings: %s") % interface_mappings)
806
603
 
807
604
    polling_interval = cfg.CONF.AGENT.polling_interval
808
 
    reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
809
605
    root_helper = cfg.CONF.AGENT.root_helper
810
 
    rpc = cfg.CONF.AGENT.rpc
811
 
    if rpc:
812
 
        plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
813
 
                                            polling_interval,
814
 
                                            root_helper)
815
 
    else:
816
 
        db_connection_url = cfg.CONF.DATABASE.sql_connection
817
 
        plugin = LinuxBridgeQuantumAgentDB(interface_mappings,
818
 
                                           polling_interval,
819
 
                                           reconnect_interval,
820
 
                                           root_helper,
821
 
                                           db_connection_url)
822
 
    LOG.info("Agent initialized successfully, now running... ")
 
606
    plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
 
607
                                        polling_interval,
 
608
                                        root_helper)
 
609
    LOG.info(_("Agent initialized successfully, now running... "))
823
610
    plugin.daemon_loop()
824
611
    sys.exit(0)
825
612
 
 
613
 
826
614
if __name__ == "__main__":
827
615
    main()