Set up dnsmasq for Public networks
authorAndy Bavier <acb@cs.princeton.edu>
Tue, 1 Oct 2013 14:51:29 +0000 (10:51 -0400)
committerAndy Bavier <acb@cs.princeton.edu>
Tue, 1 Oct 2013 14:51:29 +0000 (10:51 -0400)
plugins/planetstack-net.py

index e23744e..1a686d4 100644 (file)
@@ -32,6 +32,16 @@ import logger
 
 plugin = "planetstack-net"
 
+nat_net_name = "nat-net"
+nat_net_id = None
+site_net_id = None
+
+quantum_auth_url = "http://viccidev1:5000/v2.0/"
+quantum_username = None
+quantum_password = None
+quantum_tenant_name = None
+
+
 # Helper functions for converting to CIDR notation
 def get_net_size(netmask):
     binary_str = ''
@@ -144,34 +154,33 @@ def dnsmasq_sighup(dev):
     except:
         logger.log("%s: Sending SIGHUP to dnsmasq FAILED on dev %s" % (plugin, dev))
 
-# Enable dnsmasq for this interface
+# Enable dnsmasq for this interface.
+# It's possible that we could get by with a single instance of dnsmasq running on
+# all devices but I haven't tried it.
 def start_dnsmasq(dev, interface):
     if not dnsmasq_running(dev):
+        # The '--dhcp-range=<IP addr>,static' argument to dnsmasq ensures that it only
+        # hands out IP addresses to clients listed in the hostsfile
+        cmd = ['/usr/sbin/dnsmasq',
+               '--strict-order',
+               '--bind-interfaces',
+               '--local=//',
+               '--domain-needed',
+               '--pid-file=%s' % get_pidfile(dev),
+               '--conf-file=',
+               '--interface=%s' % dev,
+               '--except-interface=lo',
+               '--dhcp-leasefile=%s' % get_leasefile(dev),
+               '--dhcp-hostsfile=%s' % get_hostsfile(dev),
+               '--dhcp-no-override',
+               '--dhcp-range=%s,static' % interface['ip']]
+
         try:
             logger.log('%s: starting dnsmasq on device %s' % (plugin, dev))
-            iprange = ipaddr_range(interface['network'], interface['broadcast'])
-            logger.log('%s: IP range: %s' % (plugin, iprange))
-            subprocess.check_call(['/usr/sbin/dnsmasq', 
-                                   '--strict-order', 
-                                   '--bind-interfaces',
-                                   '--local=//',  
-                                   '--domain-needed',  
-                                   '--pid-file=%s' % get_pidfile(dev), 
-                                   '--conf-file=',
-                                   '--interface=%s' % dev, 
-                                   '--dhcp-range=%s,120' % iprange, 
-                                   '--dhcp-leasefile=%s' % get_leasefile(dev),
-                                   '--dhcp-hostsfile=%s' % get_hostsfile(dev),
-                                   '--dhcp-no-override'])
+            subprocess.check_call(cmd)
         except:
             logger.log('%s: FAILED to start dnsmasq for device %s' % (plugin, dev))
-
-nat_net_name = "nat-net"
-nat_net_id = None
-quantum_auth_url = "http://viccidev1:5000/v2.0/"
-quantum_username = None
-quantum_password = None
-quantum_tenant_name = None
+            logger.log(' '.join(cmd))
 
 def convert_ovs_output_to_dict(out):
     decoded = json.loads(out.strip())
@@ -210,30 +219,36 @@ def convert_ovs_output_to_dict(out):
 # 2) Sets up iptables rules in the 'planetstack-net' chain based on 
 # the nat:forward_ports field in the Port record.  
 
-def process_quantum_ports(dev):
+def get_local_quantum_ports():
+    ports = []
+
     # Get local information for VM interfaces from OvS
     ovs_out = subprocess.check_output(['/usr/bin/ovs-vsctl', '-f', 'json', 'find', 
                                        'Interface', 'external_ids:iface-id!="absent"'])
     records = convert_ovs_output_to_dict(ovs_out)
 
-    # Extract Quantum Port IDs from OvS records
-    port_ids = []
-    for rec in records:
-        port_ids.append(rec['external_ids']['iface-id'])
+    if records:
+        # Extract Quantum Port IDs from OvS records
+        port_ids = []
+        for rec in records:
+            port_ids.append(rec['external_ids']['iface-id'])
 
-    # Get the full info on these ports from Quantum
-    quantum = client.Client(username=quantum_username, 
-                            password=quantum_password,
-                            tenant_name=quantum_tenant_name,
-                            auth_url=quantum_auth_url)
-    ports = quantum.list_ports(id=port_ids)
-    # logger.log("%s: %s" % (plugin, ports))
+        # Get the full info on these ports from Quantum
+        quantum = client.Client(username=quantum_username,
+                                password=quantum_password,
+                                tenant_name=quantum_tenant_name,
+                                auth_url=quantum_auth_url)
+        ports = quantum.list_ports(id=port_ids)['ports']
 
+    return ports
+
+
+def write_dnsmasq_hostsfile(dev, ports, net_id):
     # Write relevant entries to dnsmasq hostsfile
     logger.log("%s: Writing hostsfile for %s" % (plugin, dev))
     f = open(get_hostsfile(dev), 'w')
-    for port in ports['ports']:
-        if port['network_id'] == nat_net_id:
+    for port in ports:
+        if port['network_id'] == net_id:
             entry = "%s,%s\n" % (port['mac_address'], port['fixed_ips'][0]['ip_address'])
             f.write(entry)
             logger.log("%s: %s" % (plugin, entry))
@@ -242,8 +257,9 @@ def process_quantum_ports(dev):
     # Send SIGHUP to dnsmasq to make it re-read hostsfile
     dnsmasq_sighup(dev)
 
+def set_up_port_forwarding(dev, ports):
     # Set up iptables rules for port forwarding
-    for port in ports['ports']:
+    for port in ports:
         if port['network_id'] == nat_net_id:
             for fw in port['nat:forward_ports']:
                 ipaddr = port['fixed_ips'][0]['ip_address']
@@ -256,12 +272,20 @@ def process_quantum_ports(dev):
                 add_iptables_rule('nat', plugin, ['-i', 'br-eth0', 
                                                   '-p', protocol, '--dport', fwport,
                                                   '-j', 'DNAT', '--to-destination', ipaddr])
-            
+
+def get_net_id_by_name(name):
+    quantum = client.Client(username=quantum_username,
+                            password=quantum_password,
+                            tenant_name=quantum_tenant_name,
+                            auth_url=quantum_auth_url)
+
+    net = quantum.list_networks(name=name)
+    return net['networks'][0]['id']
+
 def start():
     global quantum_username
     global quantum_password
     global quantum_tenant_name
-    global nat_net_id
 
     logger.log("%s: plugin starting up..." % plugin)
 
@@ -271,18 +295,32 @@ def start():
     quantum_password = parser.get("DEFAULT", "quantum_admin_password")
     quantum_tenant_name = parser.get("DEFAULT", "quantum_admin_tenant_name")
 
-    quantum = client.Client(username=quantum_username, 
-                            password=quantum_password,
-                            tenant_name=quantum_tenant_name,
-                            auth_url=quantum_auth_url)
 
-    net = quantum.list_networks(name=nat_net_name)
-    nat_net_id = net['networks'][0]['id']
+def GetSlivers(data, config=None, plc=None):
+    global nat_net_id
+    global site_net_id
+
+    if not nat_net_id:
+        try:
+            nat_net_id = get_net_id_by_name(nat_net_name)
+            logger.log("%s: %s id is %s..." % (plugin, nat_net_name, nat_net_id))
+        except:
+            logger.log("%s: no network called %s..." % (plugin, nat_net_name))
 
-    logger.log("%s: %s id is %s..." % (plugin, nat_net_name, nat_net_id))
 
-def GetSlivers(data, config=None, plc=None):
+    # Fix me
+    # site_net_name = "devel"
+    site_net_name = "sharednet1"
+    if not site_net_id:
+        try:
+            site_net_id = get_net_id_by_name(site_net_name)
+            logger.log("%s: %s id is %s..." % (plugin, site_net_name, site_net_id))
+        except:
+            logger.log("%s: no network called %s..." % (plugin, site_net_name))
+
     reset_iptables_chain()
+    ports = get_local_quantum_ports()
+
     for interface in data['interfaces']:
         try:
             settings = plc.GetInterfaceTags({'interface_tag_id': interface['interface_tag_ids']})
@@ -301,8 +339,18 @@ def GetSlivers(data, config=None, plc=None):
             continue
 
         logger.log('%s: Processing device %s' % (plugin, dev))
-        
-        if 'NAT' in tags:
+
+        # Process Private-Nat networks
+        if 'NAT' in tags and nat_net_id:
             add_iptables_masq(dev, interface)
-            process_quantum_ports(dev)
+            write_dnsmasq_hostsfile(dev, ports, nat_net_id)
+            set_up_port_forwarding(dev, ports)
             start_dnsmasq(dev, interface)
+
+        # Process Public networks
+        if interface['is_primary'] and site_net_id:
+            if 'OVS_BRIDGE' in tags:
+                dev = tags['OVS_BRIDGE']
+            write_dnsmasq_hostsfile(dev, ports, site_net_id)
+            start_dnsmasq(dev, interface)
+