175 lines
6.1 KiB
Python
175 lines
6.1 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2010 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import logging
|
|
import signal
|
|
import os
|
|
import subprocess
|
|
|
|
# todo(ja): does the definition of network_path belong here?
|
|
|
|
from nova import utils
|
|
|
|
from nova import flags
|
|
FLAGS=flags.FLAGS
|
|
|
|
def execute(cmd):
|
|
if FLAGS.fake_network:
|
|
logging.debug("FAKE NET: %s" % cmd)
|
|
return "fake", 0
|
|
else:
|
|
return nova.utils.execute(cmd)
|
|
|
|
def runthis(desc, cmd):
|
|
if FLAGS.fake_network:
|
|
return execute(cmd)
|
|
else:
|
|
return nova.utils.runthis(desc,cmd)
|
|
|
|
def Popen(cmd):
|
|
if FLAGS.fake_network:
|
|
execute(' '.join(cmd))
|
|
else:
|
|
subprocess.Popen(cmd)
|
|
|
|
|
|
def device_exists(device):
|
|
(out, err) = execute("ifconfig %s" % device)
|
|
return not err
|
|
|
|
def confirm_rule(cmd):
|
|
execute("sudo iptables --delete %s" % (cmd))
|
|
execute("sudo iptables -I %s" % (cmd))
|
|
|
|
def remove_rule(cmd):
|
|
execute("sudo iptables --delete %s" % (cmd))
|
|
|
|
def bind_public_ip(ip, interface):
|
|
runthis("Binding IP to interface: %s", "sudo ip addr add %s dev %s" % (ip, interface))
|
|
|
|
def unbind_public_ip(ip, interface):
|
|
runthis("Binding IP to interface: %s", "sudo ip addr del %s dev %s" % (ip, interface))
|
|
|
|
def vlan_create(net):
|
|
""" create a vlan on on a bridge device unless vlan already exists """
|
|
if not device_exists("vlan%s" % net['vlan']):
|
|
logging.debug("Starting VLAN inteface for %s network", (net['vlan']))
|
|
execute("sudo vconfig set_name_type VLAN_PLUS_VID_NO_PAD")
|
|
execute("sudo vconfig add %s %s" % (FLAGS.bridge_dev, net['vlan']))
|
|
execute("sudo ifconfig vlan%s up" % (net['vlan']))
|
|
|
|
def bridge_create(net):
|
|
""" create a bridge on a vlan unless it already exists """
|
|
if not device_exists(net['bridge_name']):
|
|
logging.debug("Starting Bridge inteface for %s network", (net['vlan']))
|
|
execute("sudo brctl addbr %s" % (net['bridge_name']))
|
|
execute("sudo brctl setfd %s 0" % (net.bridge_name))
|
|
# execute("sudo brctl setageing %s 10" % (net.bridge_name))
|
|
execute("sudo brctl stp %s off" % (net['bridge_name']))
|
|
execute("sudo brctl addif %s vlan%s" % (net['bridge_name'], net['vlan']))
|
|
if net.bridge_gets_ip:
|
|
execute("sudo ifconfig %s %s broadcast %s netmask %s up" % \
|
|
(net['bridge_name'], net.gateway, net.broadcast, net.netmask))
|
|
confirm_rule("FORWARD --in-interface %s -j ACCEPT" % (net['bridge_name']))
|
|
else:
|
|
execute("sudo ifconfig %s up" % net['bridge_name'])
|
|
|
|
def dnsmasq_cmd(net):
|
|
cmd = ['sudo dnsmasq',
|
|
' --strict-order',
|
|
' --bind-interfaces',
|
|
' --conf-file=',
|
|
' --pid-file=%s' % dhcp_file(net['vlan'], 'pid'),
|
|
' --listen-address=%s' % net.dhcp_listen_address,
|
|
' --except-interface=lo',
|
|
' --dhcp-range=%s,static,600s' % (net.dhcp_range_start),
|
|
' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'),
|
|
' --dhcp-script=%s' % bin_file('dhcpleasor.py'),
|
|
' --leasefile-ro']
|
|
return ''.join(cmd)
|
|
|
|
def hostDHCP(network, host, mac):
|
|
idx = host.split(".")[-1] # Logically, the idx of instances they've launched in this net
|
|
return "%s,%s-%s-%s.novalocal,%s" % \
|
|
(mac, network['user_id'], network['vlan'], idx, host)
|
|
|
|
# todo(ja): if the system has restarted or pid numbers have wrapped
|
|
# then you cannot be certain that the pid refers to the
|
|
# dnsmasq. As well, sending a HUP only reloads the hostfile,
|
|
# so any configuration options (like dchp-range, vlan, ...)
|
|
# aren't reloaded
|
|
def start_dnsmasq(network):
|
|
""" (re)starts a dnsmasq server for a given network
|
|
|
|
if a dnsmasq instance is already running then send a HUP
|
|
signal causing it to reload, otherwise spawn a new instance
|
|
"""
|
|
with open(dhcp_file(network['vlan'], 'conf'), 'w') as f:
|
|
for host_name in network.hosts:
|
|
f.write("%s\n" % hostDHCP(network, host_name, network.hosts[host_name]))
|
|
|
|
pid = dnsmasq_pid_for(network)
|
|
|
|
# if dnsmasq is already running, then tell it to reload
|
|
if pid:
|
|
# todo(ja): use "/proc/%d/cmdline" % (pid) to determine if pid refers
|
|
# correct dnsmasq process
|
|
try:
|
|
os.kill(pid, signal.SIGHUP)
|
|
except Exception, e:
|
|
logging.debug("Hupping dnsmasq threw %s", e)
|
|
|
|
# otherwise delete the existing leases file and start dnsmasq
|
|
lease_file = dhcp_file(network['vlan'], 'leases')
|
|
if os.path.exists(lease_file):
|
|
os.unlink(lease_file)
|
|
|
|
Popen(dnsmasq_cmd(network).split(" "))
|
|
|
|
def stop_dnsmasq(network):
|
|
""" stops the dnsmasq instance for a given network """
|
|
pid = dnsmasq_pid_for(network)
|
|
|
|
if pid:
|
|
try:
|
|
os.kill(pid, signal.SIGTERM)
|
|
except Exception, e:
|
|
logging.debug("Killing dnsmasq threw %s", e)
|
|
|
|
def dhcp_file(vlan, kind):
|
|
""" return path to a pid, leases or conf file for a vlan """
|
|
|
|
return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path, vlan, kind))
|
|
|
|
def bin_file(script):
|
|
return os.path.abspath(os.path.join(__file__, "../../../bin", script))
|
|
|
|
def dnsmasq_pid_for(network):
|
|
""" the pid for prior dnsmasq instance for a vlan,
|
|
returns None if no pid file exists
|
|
|
|
if machine has rebooted pid might be incorrect (caller should check)
|
|
"""
|
|
|
|
pid_file = dhcp_file(network['vlan'], 'pid')
|
|
|
|
if os.path.exists(pid_file):
|
|
with open(pid_file, 'r') as f:
|
|
return int(f.read())
|
|
|