mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Added guest network configuration
Added iptables config (adds but no deletes yet) Apache config (VPC GN) DNSMasq config (VPX GN)
This commit is contained in:
parent
95e5f867f9
commit
05702ecd1a
@ -22,6 +22,8 @@ import subprocess
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import shutil
|
||||
import os.path
|
||||
|
||||
class CsHelper:
|
||||
""" General helper functions
|
||||
@ -59,6 +61,60 @@ class CsHelper:
|
||||
result = p.communicate()[0]
|
||||
return result.splitlines()
|
||||
|
||||
def service(self, name, op):
|
||||
self.execute("service %s %s" % (name, op))
|
||||
logging.info("Service %s %s" % (name, op))
|
||||
|
||||
def copy_if_needed(self, src, dest):
|
||||
""" Copy a file if the destination does not already exist
|
||||
"""
|
||||
if os.path.isfile(dest):
|
||||
return
|
||||
try:
|
||||
shutil.copy2(src, dest)
|
||||
except IOError:
|
||||
logging.Error("Could not copy %s to %s" % (src, dest))
|
||||
else:
|
||||
logging.info("Copied %s to %s" % (src, dest))
|
||||
|
||||
class CsFile:
|
||||
""" File editors """
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.changed = False
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
self.new_config = []
|
||||
for line in open(self.filename):
|
||||
self.new_config.append(line)
|
||||
logging.debug("Reading file %s" % self.filename)
|
||||
|
||||
def isChanged(self):
|
||||
return self.changed
|
||||
|
||||
def commit(self):
|
||||
if not self.changed:
|
||||
return
|
||||
handle = open(self.filename, "w+")
|
||||
for line in self.new_config:
|
||||
handle.write(line)
|
||||
handle.close()
|
||||
logging.info("Wrote edited file %s" % self.filename)
|
||||
|
||||
def search(self, search, replace):
|
||||
found = False
|
||||
logging.debug("Searching for %s and replacing with %s" % (search, replace))
|
||||
for index, line in enumerate(self.new_config):
|
||||
if re.search(search, line):
|
||||
found = True
|
||||
if not replace in line:
|
||||
self.changed = True
|
||||
self.new_config[index] = replace + "\n"
|
||||
if not found:
|
||||
self.new_config.append(replace + "\n")
|
||||
self.changed = True
|
||||
|
||||
class CsRule:
|
||||
""" Manage iprules
|
||||
@ -85,7 +141,7 @@ class CsRule:
|
||||
return False
|
||||
|
||||
|
||||
class csRoute:
|
||||
class CsRoute:
|
||||
""" Manage routes """
|
||||
|
||||
def __init__(self, dev):
|
||||
@ -102,24 +158,29 @@ class csRoute:
|
||||
CsHelper().execute("ip route flush table %s" % (self.table))
|
||||
CsHelper().execute("ip route flush cache")
|
||||
|
||||
def add(self, address):
|
||||
def add(self, address, method = "add"):
|
||||
# ip route show dev eth1 table Table_eth1 10.0.2.0/24
|
||||
# sudo ip route add default via $defaultGwIP table $tableName proto static
|
||||
cmd = "dev %s table %s %s" % (self.dev, self.table, address['network'])
|
||||
self.addifmissing(cmd)
|
||||
if(method == "add"):
|
||||
cmd = "dev %s table %s %s" % (self.dev, self.table, address['network'])
|
||||
self.set_route(cmd, method)
|
||||
|
||||
def addifmissing(self, cmd):
|
||||
def set_route(self, cmd, method = "add"):
|
||||
""" Add a route is it is not already defined """
|
||||
found = False
|
||||
for i in CsHelper().execute("ip route show " + cmd):
|
||||
found = True
|
||||
if not found:
|
||||
if not found and method == "add":
|
||||
logging.info("Add " + cmd)
|
||||
cmd = "ip route add " + cmd
|
||||
CsHelper().execute(cmd)
|
||||
elif found and method == "delete":
|
||||
logging.info("Delete " + cmd)
|
||||
cmd = "ip route delete " + cmd
|
||||
else:
|
||||
return
|
||||
CsHelper().execute(cmd)
|
||||
|
||||
|
||||
class csRpsrfs:
|
||||
class CsRpsrfs:
|
||||
""" Configure rpsrfs if there is more than one cpu """
|
||||
|
||||
def __init__(self, dev):
|
||||
@ -156,6 +217,86 @@ class csRpsrfs:
|
||||
return count
|
||||
|
||||
|
||||
class CsApp:
|
||||
def __init__(self, ip):
|
||||
self.dev = ip.getDevice()
|
||||
self.ip = ip.getAddress()['public_ip']
|
||||
self.domain = "domain.local"
|
||||
self.type = ip.get_type()
|
||||
if self.type == "guest":
|
||||
gn = CsGuestNetwork(self.dev)
|
||||
self.domain = gn.getDomain()
|
||||
|
||||
class CsApache(CsApp):
|
||||
""" Set up Apache """
|
||||
|
||||
def setup(self):
|
||||
CsHelper().copy_if_needed("/etc/apache2/vhostexample.conf", "/etc/apache2/conf.d/vhost%s.conf" % self.dev)
|
||||
|
||||
file = CsFile("/etc/apache2/conf.d/vhost%s.conf" % (self.dev))
|
||||
file.search("<VirtualHost.*:80>", "\t<VirtualHost %s:80>" % (self.ip))
|
||||
file.search("<VirtualHost.*:80>", "\t<VirtualHost %s:80>" % (self.ip))
|
||||
file.search("<VirtualHost.*:443>", "\t<VirtualHost %s:443>" % (self.ip))
|
||||
file.search("Listen .*:80", "Listen %s:80" % (self.ip))
|
||||
file.search("Listen .*:443", "Listen %s:443" % (self.ip))
|
||||
file.search("ServerName.*", "\tServerName vhost%s.cloudinternal.com" % (self.dev))
|
||||
file.commit()
|
||||
|
||||
class CsDnsmasq(CsApp):
|
||||
""" Set up dnsmasq """
|
||||
|
||||
def add_firewall_rules(self, method):
|
||||
""" Add the necessary firewall rules
|
||||
This is problamatic because the current logic cannot delete them
|
||||
(In a convergence model)
|
||||
|
||||
We will need to store some state about what "used" to be there
|
||||
"""
|
||||
firewall = CsIpTables(self.dev)
|
||||
|
||||
cmds = "-A INPUT -i %s -p udp -m udp --dport 67 -j %s"
|
||||
slist = [ self.dev, "ACCEPT" ]
|
||||
firewall.change_rule("", slist, cmds)
|
||||
|
||||
cmds = "-A INPUT -i %s -d %s -p %s -m %s --dport %s -j %s"
|
||||
slist = [ self.dev, self.ip, "udp", "udp", "53", "ACCEPT" ]
|
||||
firewall.change_rule("", slist, cmds)
|
||||
|
||||
cmds = "-A INPUT -i %s -d %s -p %s -m %s --dport %s -j %s"
|
||||
slist = [ self.dev, self.ip, "tcp", "tcp", "53", "ACCEPT" ]
|
||||
firewall.change_rule("", slist, cmds)
|
||||
|
||||
def configure_server(self, method = "add"):
|
||||
file = CsFile("/etc/dnsmasq.d/cloud.conf")
|
||||
file.search("dhcp-range=interface:%s" % self.dev, \
|
||||
"dhcp-range=interface:%s,set:interface-%s,%s,static" % (self.dev, self.dev, self.ip))
|
||||
file.search("dhcp-option=tag:interface-%s," % self.dev, \
|
||||
"dhcp-option=tag:interface-%s,15,%s" % (self.dev, self.domain))
|
||||
file.commit()
|
||||
|
||||
if file.isChanged():
|
||||
CsHelper().service("dnsmasq", "restart")
|
||||
|
||||
|
||||
class CsGuestNetwork:
|
||||
def __init__(self, device):
|
||||
self.data = {}
|
||||
db = dataBag()
|
||||
db.setKey("guestnetwork")
|
||||
db.load()
|
||||
dbag = db.getDataBag()
|
||||
for dev in dbag:
|
||||
if dev == "id":
|
||||
continue
|
||||
if dev == device:
|
||||
self.data = dbag[dev][0]
|
||||
|
||||
def getDomain(self):
|
||||
if 'domain_name' in self.data:
|
||||
return self.data['domain_name']
|
||||
else:
|
||||
return "cloudnine.internal"
|
||||
|
||||
class CsDevice:
|
||||
""" Configure Network Devices """
|
||||
def __init__(self, dev):
|
||||
@ -168,6 +309,13 @@ class CsDevice:
|
||||
self.tableNo = dev[3]
|
||||
self.table = "Table_%s" % (dev)
|
||||
|
||||
def configure_rp(self):
|
||||
"""
|
||||
Configure Reverse Path Filtering
|
||||
"""
|
||||
filename = "/proc/sys/net/ipv4/conf/%s/rp_filter" % (self.dev)
|
||||
CsHelper().updatefile(filename, "1\n", "w")
|
||||
|
||||
def buildlist(self):
|
||||
"""
|
||||
List all available network devices on the system
|
||||
@ -182,30 +330,6 @@ class CsDevice:
|
||||
continue
|
||||
self.devlist.append(vals[0])
|
||||
|
||||
def set_connmark(self):
|
||||
""" Set connmark for device """
|
||||
if not self.has_connmark():
|
||||
cmd="-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-mark %s" \
|
||||
% (self.dev, self.tableNo)
|
||||
CsHelper().execute("iptables -t mangle %s" % (cmd))
|
||||
logging.error("Set connmark for device %s (Table %s)", self.dev, self.tableNo)
|
||||
|
||||
def has_connmark(self):
|
||||
cmd = "iptables-save -t mangle"
|
||||
for line in CsHelper().execute(cmd):
|
||||
if not "PREROUTING" in line:
|
||||
continue
|
||||
if not "state" in line:
|
||||
continue
|
||||
if not "CONNMARK" in line:
|
||||
continue
|
||||
if not "set-xmark" in line:
|
||||
continue
|
||||
if not self.dev in line:
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def waitForDevice(self):
|
||||
""" Wait up to 15 seconds for a device to become available """
|
||||
@ -229,9 +353,108 @@ class CsDevice:
|
||||
if " DOWN " in i:
|
||||
cmd2 = "ip link set %s up" % (self.dev)
|
||||
CsHelper().execute(cmd2)
|
||||
self.set_connmark()
|
||||
CsIpTables(self.dev).set_connmark()
|
||||
|
||||
|
||||
class CsIpTables:
|
||||
|
||||
""" Utility class
|
||||
All the bits and pieces needed for iptables operations
|
||||
"""
|
||||
def __init__(self, dev):
|
||||
self.dev = dev
|
||||
self.tableNo = dev[3]
|
||||
self.table = "Table_%s" % (dev)
|
||||
self.devChain = "ACL_INBOUND_%s" % (dev)
|
||||
|
||||
def set_connmark(self, method = "add"):
|
||||
""" Set connmark for device """
|
||||
slist = ["PREROUTING", self.dev, "state", "NEW", "CONNMARK", self.tableNo]
|
||||
if method == "add":
|
||||
cmds="-A %s -i %s -m %s --state %s -j %s --set-mark 0x%s"
|
||||
else:
|
||||
cmds="-D %s -i %s -m %s --state %s -j %s --set-mark 0x%s"
|
||||
self.change_rule("mangle", slist, cmds)
|
||||
|
||||
def set_accept(self, table, method = "add"):
|
||||
""" Add an accept rule
|
||||
First version - very simple - once I find out what the patterns
|
||||
are this will be refactored
|
||||
"""
|
||||
slist = [ self.devChain, "ACCEPT" ]
|
||||
if method == "add":
|
||||
cmds = "-A %s -j %s"
|
||||
else:
|
||||
cmds = "-D %s -j %s"
|
||||
self.change_rule( table, slist, cmds)
|
||||
|
||||
def set_forward(self, ip, method = "add"):
|
||||
""" set a forward to the device chain
|
||||
takes a CsIP object """
|
||||
slist = [ "FORWARD", self.dev, ip['network'], self.devChain ]
|
||||
if method == "add":
|
||||
cmds = "-A %s -o %s -d %s -j %s"
|
||||
else:
|
||||
cmds = "-A %s -o %s -d %s -j %s"
|
||||
self.change_rule('mangle', slist, cmds)
|
||||
|
||||
def set_drop(self, method = "add"):
|
||||
""" Ensure the last rule is drop """
|
||||
slist = [ self.devChain, "DROP" ]
|
||||
if method == "add":
|
||||
cmds="-A %s -j %s"
|
||||
else:
|
||||
cmds="-D %s -j %s"
|
||||
self.change_rule('', slist, cmds)
|
||||
|
||||
def set_static_nat(self, ip, method = "add"):
|
||||
""" Add static nat to a device/ip combination
|
||||
Takes a CsIp object as its parameter
|
||||
"""
|
||||
slist = ["POSTROUTING", ip['network'], self.dev, "SNAT", ip['public_ip']]
|
||||
if method == "add":
|
||||
cmds ="-A %s -s %s -o %s -j %s --to-source %s"
|
||||
else:
|
||||
cmds ="-D %s -s %s -o %s -j %s --to-source %s"
|
||||
self.change_rule('nat', slist, cmds)
|
||||
|
||||
def set_preroute(self, ip, method):
|
||||
slist = [ "PREROUTING", "NEW", self.dev, ip['network'], ip['public_ip'], self.devChain ]
|
||||
cmds = "-A %s -m state --state %s -i %s -s %s ! -d %s -j %s"
|
||||
if method == "add":
|
||||
self.change_rule('mangle', slist, cmds)
|
||||
|
||||
def change_rule(self, table, slist, cmds):
|
||||
cmd = ''
|
||||
if not self.has_rule(table, slist):
|
||||
if table != '':
|
||||
cmd = "-t %s " % table
|
||||
cmd += cmds % tuple(slist)
|
||||
CsHelper().execute("iptables %s" % (cmd))
|
||||
logging.info("iptables %s", cmd)
|
||||
|
||||
def set_chain(self, table, method):
|
||||
""" Create a chain if it does not already exist """
|
||||
slist = [ self.devChain ]
|
||||
cmd = ''
|
||||
if not self.has_rule(table, slist):
|
||||
if table != '':
|
||||
cmd = "-t %s " % table
|
||||
cmd += "-N %s" % tuple(slist)
|
||||
CsHelper().execute("iptables %s" % (cmd))
|
||||
logging.info("iptables %s", cmd)
|
||||
|
||||
def has_rule(self, table, list):
|
||||
""" Check if a particular rule exists """
|
||||
cmd = "iptables-save "
|
||||
if table != "":
|
||||
cmd += "-t %s" % (table)
|
||||
for line in CsHelper().execute(cmd):
|
||||
matches = len([i for i in list if i in line])
|
||||
if(matches == len(list)):
|
||||
return True
|
||||
return False
|
||||
|
||||
class CsIP:
|
||||
|
||||
def __init__(self, dev):
|
||||
@ -243,6 +466,8 @@ class CsIP:
|
||||
def setAddress(self, address):
|
||||
self.address = address
|
||||
|
||||
def getAddress(self):
|
||||
return self.address
|
||||
|
||||
def configure(self):
|
||||
logging.info("Configuring address %s on device %s", self.ip(), self.dev)
|
||||
@ -252,13 +477,44 @@ class CsIP:
|
||||
|
||||
def post_configure(self):
|
||||
""" The steps that must be done after a device is configured """
|
||||
route = csRoute(self.dev)
|
||||
route = CsRoute(self.dev)
|
||||
route.routeTable()
|
||||
CsRule(self.dev).addMark()
|
||||
CsDevice(self.dev).setUp()
|
||||
self.arpPing()
|
||||
route.add(self.address)
|
||||
csRpsrfs(self.dev).enable()
|
||||
CsRpsrfs(self.dev).enable()
|
||||
self.post_config_change("add")
|
||||
|
||||
def get_type(self):
|
||||
""" Return the type of the IP
|
||||
guest
|
||||
control
|
||||
public
|
||||
"""
|
||||
if "nw_type" in self.address:
|
||||
return self.address['nw_type']
|
||||
return "unknown"
|
||||
|
||||
def post_config_change(self, method):
|
||||
route = CsRoute(self.dev)
|
||||
route.routeTable()
|
||||
route.add(self.address, method)
|
||||
# On deletion nw_type will no longer be known
|
||||
if self.get_type() in [ "guest" ]:
|
||||
CsDevice(self.dev).configure_rp()
|
||||
CsIpTables(self.dev).set_static_nat(self.address, method)
|
||||
CsIpTables(self.dev).set_chain('', method)
|
||||
CsIpTables(self.dev).set_chain('mangle', method)
|
||||
CsIpTables(self.dev).set_accept('mangle', method)
|
||||
CsIpTables(self.dev).set_forward(self.address, method)
|
||||
CsIpTables(self.dev).set_drop(method)
|
||||
CsIpTables(self.dev).set_preroute(self.address, method)
|
||||
dns = CsDnsmasq(self)
|
||||
dns.add_firewall_rules("add")
|
||||
dns.configure_server()
|
||||
app = CsApache(self)
|
||||
app.setup()
|
||||
|
||||
route.flush()
|
||||
|
||||
def list(self):
|
||||
@ -278,6 +534,9 @@ class CsIP:
|
||||
def ip(self):
|
||||
return str(self.address['cidr'])
|
||||
|
||||
def getDevice(self):
|
||||
return self.dev
|
||||
|
||||
def hasIP(self, ip):
|
||||
return ip in self.address.values()
|
||||
|
||||
@ -312,9 +571,10 @@ class CsIP:
|
||||
cmd = "ip addr del dev %s %s" % (self.dev, ip)
|
||||
subprocess.call(cmd, shell=True)
|
||||
logging.info("Removed address %s from device %s", ip, self.dev)
|
||||
|
||||
self.post_config_change("delete")
|
||||
|
||||
def main(argv):
|
||||
|
||||
logging.basicConfig(filename='/var/log/cloud.log',
|
||||
level=logging.DEBUG, format='%(asctime)s %(message)s')
|
||||
|
||||
@ -333,7 +593,7 @@ def main(argv):
|
||||
continue
|
||||
ip = CsIP(dev)
|
||||
for address in dbag[dev]:
|
||||
csRoute(dev).add(address)
|
||||
CsRoute(dev).add(address)
|
||||
ip.setAddress(address)
|
||||
if ip.configured():
|
||||
logging.info("Address %s on device %s already configured", ip.ip(), dev)
|
||||
|
||||
@ -84,6 +84,8 @@ class updateDataBag:
|
||||
dp['nw_type'] = 'guest'
|
||||
qf = loadQueueFile()
|
||||
qf.load({ 'ip_address' : [ dp ], 'type' : 'ips'})
|
||||
if 'domain_name' not in d.keys() or d['domain_name'] == '':
|
||||
d['domain_name'] = "cloudnine.internal"
|
||||
return cs_guestnetwork.merge(dbag, self.qFile.data)
|
||||
|
||||
def processIP(self, dbag):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user