22
22
# Quantum OpenVSwitch Plugin.
23
23
# @author: Sumit Naiksatam, Cisco Systems, Inc.
35
from sqlalchemy.ext.sqlsoup import SqlSoup
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
51
46
LOG = logging.getLogger(__name__)
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"
270
def ensure_physical_in_bridge(self, network_id,
273
physical_interface = self.interface_mappings.get(physical_network)
274
if not physical_interface:
275
LOG.error("No mapping for physical network %s" %
279
if int(vlan_id) == lconst.FLAT_VLAN_ID:
280
self.ensure_flat_bridge(network_id, physical_interface)
282
self.ensure_vlan_bridge(network_id, physical_interface,
284
286
def add_tap_interface(self, network_id, physical_network, vlan_id,
285
287
tap_device_name):
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
290
if not tap_device_name:
293
292
if not self.device_exists(tap_device_name):
294
LOG.debug("Tap device: %s does not exist on this host, skipped" %
293
LOG.debug(_("Tap device: %s does not exist on "
294
"this host, skipped" % tap_device_name))
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:
302
LOG.debug("Adding device %s to bridge %s" % (tap_device_name,
304
if current_bridge_name:
305
if utils.execute(['brctl', 'delif', current_bridge_name,
306
tap_device_name], root_helper=self.root_helper):
309
298
if int(vlan_id) == lconst.LOCAL_VLAN_ID:
310
299
self.ensure_local_bridge(network_id)
312
physical_interface = self.interface_mappings.get(physical_network)
313
if not physical_interface:
314
LOG.error("No mapping for physical network %s" %
318
if int(vlan_id) == lconst.FLAT_VLAN_ID:
319
self.ensure_flat_bridge(network_id, physical_interface)
321
self.ensure_vlan_bridge(network_id, physical_interface,
324
if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
325
root_helper=self.root_helper):
328
LOG.debug("Done adding device %s to bridge %s" % (tap_device_name,
301
result = self.ensure_physical_in_bridge(network_id,
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()
313
if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
314
root_helper=self.root_helper):
317
msg = _("%(tap_device_name)s already exists on bridge "
318
"%(bridge_name)s") % locals()
332
322
def add_interface(self, network_id, physical_network, vlan_id,
336
Since the VIF id is null, no VIF is plugged into this port
337
no more processing is required
341
if interface_id.startswith(GATEWAY_INTERFACE_PREFIX):
342
return self.add_tap_interface(network_id,
343
physical_network, vlan_id,
346
tap_device_name = self.get_tap_device_name(interface_id)
347
return self.add_tap_interface(network_id,
348
physical_network, vlan_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,
351
329
def delete_vlan_bridge(self, bridge_name):
352
330
if self.device_exists(bridge_name):
445
425
return dispatcher.RpcDispatcher([self])
448
class LinuxBridgeQuantumAgentDB:
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
459
def setup_linux_bridge(self, interface_mappings):
460
self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
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,
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)
473
def process_unplugged_interfaces(self, plugged_interfaces):
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
480
plugged_tap_device_names = []
481
plugged_gateway_device_names = []
482
for interface in plugged_interfaces:
483
if interface.startswith(GATEWAY_INTERFACE_PREFIX):
485
The name for the gateway devices is set by the linux net
486
driver, hence we use the name as is
488
plugged_gateway_device_names.append(interface)
490
tap_device_name = self.linux_br.get_tap_device_name(interface)
491
plugged_tap_device_names.append(tap_device_name)
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,
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,
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)
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)
522
def manage_networks_on_host(self, db,
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: {},
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)
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: {},
552
for bind in port_binds:
554
all_bindings[bind.id] = bind
555
entry = {'network_id': bind.network_id,
557
'status': bind.status,
558
'interface_id': bind.id}
559
append_entry = bind.admin_state_up
561
port_bindings.append(entry)
563
plugged_interfaces = []
565
for pb in port_bindings:
566
ports_string = "%s %s" % (ports_string, pb)
568
interface_id = pb['interface_id']
569
network_id = pb['network_id']
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,
577
all_bindings[port_id].status = constants.PORT_STATUS_ACTIVE
579
plugged_interfaces.append(interface_id)
581
if old_port_bindings != port_bindings:
582
LOG.debug("Port-bindings: %s" % ports_string)
584
self.process_unplugged_interfaces(plugged_interfaces)
586
if old_vlan_bindings != vlan_bindings:
587
LOG.debug("VLAN-bindings: %s" % vlans_string)
589
self.process_deleted_networks(vlan_bindings)
593
except Exception as e:
594
LOG.info("Unable to update database! Exception: %s" % e)
599
return {VLAN_BINDINGS: vlan_bindings,
600
PORT_BINDINGS: port_bindings}
602
def daemon_loop(self):
603
old_vlan_bindings = {}
604
old_port_bindings = []
605
self.db_connected = False
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,
617
old_vlan_bindings = bindings[VLAN_BINDINGS]
618
old_port_bindings = bindings[PORT_BINDINGS]
619
time.sleep(self.polling_interval)
622
428
class LinuxBridgeQuantumAgentRPC:
624
430
def __init__(self, interface_mappings, polling_interval,
792
592
# (TODO) gary - swap with common logging
793
593
logging_config.setup_logging(cfg.CONF)
795
interface_mappings = {}
796
for mapping in cfg.CONF.LINUX_BRIDGE.physical_interface_mappings:
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" %
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)
602
LOG.info(_("Interface mappings: %s") % interface_mappings)
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
812
plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
816
db_connection_url = cfg.CONF.DATABASE.sql_connection
817
plugin = LinuxBridgeQuantumAgentDB(interface_mappings,
822
LOG.info("Agent initialized successfully, now running... ")
606
plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
609
LOG.info(_("Agent initialized successfully, now running... "))
823
610
plugin.daemon_loop()
826
614
if __name__ == "__main__":