diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsAddress.py b/systemvm/patches/debian/config/opt/cloud/bin/CsAddress.py new file mode 100644 index 00000000000..b767e3ebb24 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsAddress.py @@ -0,0 +1,397 @@ +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +from CsDatabag import CsDataBag, CsCmdLine +from CsApp import CsApache, CsDnsmasq, CsPasswdSvc +import CsHelper +import logging +import CsHelper +import subprocess +from CsRoute import CsRoute +from CsRule import CsRule + +class CsAddress(CsDataBag): + + def compare(self): + for dev in CsDevice('', self.fw).list(): + ip = CsIP(dev, self.fw) + ip.compare(self.dbag) + + def get_ips(self): + ret = [] + for dev in self.dbag: + if dev == "id": + continue + for ip in self.dbag[dev]: + ret.append(CsInterface(ip)) + return ret + + def needs_vrrp(self,o): + """ + Returns if the ip needs to be managed by keepalived or not + """ + if "nw_type" in o and o['nw_type'] in [ 'guest' ]: + return True + return False + + def get_control_if(self): + """ + Return the address object that has the control interface + """ + for ip in self.get_ips(): + if ip.is_control(): + return ip + return None + + def process(self): + for dev in self.dbag: + if dev == "id": + continue + ip = CsIP(dev, self.fw) + addcnt = 0 + for address in self.dbag[dev]: + if not address["nw_type"] == "control": + CsRoute(dev).add(address) + ip.setAddress(address) + if ip.configured(): + logging.info("Address %s on device %s already configured", ip.ip(), dev) + ip.post_configure() + else: + logging.info("Address %s on device %s not configured", ip.ip(), dev) + if CsDevice(dev, self.fw).waitfordevice(): + ip.configure() + # This could go one level up but the ip type is stored in the + # ip address object and not in the device object + # Call only once + if addcnt == 0: + self.add_netstats(address) + addcnt += 1 + + def add_netstats(self, address): + # add in the network stats iptables rules + dev = "eth%s" % address['nic_dev_id'] + if address["nw_type"] == "public_ip": + self.fw.append(["", "front", "-A FORWARD -j NETWORK_STATS"]) + self.fw.append(["", "front", "-A INPUT -j NETWORK_STATS"]) + self.fw.append(["", "front", "-A OUTPUT -j NETWORK_STATS"]) + # it is not possible to calculate these devices + # When the vrouter and the vpc router are combined this silliness can go + self.fw.append(["", "", "-A NETWORK_STATS -i %s -o eth0 -p tcp" % dev]) + self.fw.append(["", "", "-A NETWORK_STATS -o %s -i eth0 -p tcp" % dev]) + self.fw.append(["", "", "-A NETWORK_STATS -o %s ! -i eth0 -p tcp" % dev]) + self.fw.append(["", "", "-A NETWORK_STATS -i %s ! -o eth0 -p tcp" % dev]) + + if address["nw_type"] == "guest": + self.fw.append(["", "front", "-A FORWARD -j NETWORK_STATS_%s" % dev]) + self.fw.append(["", "front", "-A NETWORK_STATS_%s -o %s -s %s" % (dev, dev, address['network'])]) + self.fw.append(["", "front", "-A NETWORK_STATS_%s -o %s -d %s" % (dev, dev, address['network'])]) + # Only relevant if there is a VPN configured so will have to move + # at some stage + self.fw.append(["mangle", "", "-A FORWARD -j VPN_STATS_%s" % dev]) + self.fw.append(["mangle", "", "-A VPN_STATS_%s -o %s -m mark --set-xmark 0x525/0xffffffff" % (dev, dev)]) + self.fw.append(["mangle", "", "-A VPN_STATS_%s -i %s -m mark --set-xmark 0x524/0xffffffff" % (dev, dev)]) + +class CsInterface: + """ Hold one single ip """ + def __init__(self, o): + self.address = o + + def get_ip(self): + return self.get_attr("public_ip") + + def get_device(self): + return self.get_attr("device") + + def get_cidr(self): + return self.get_attr("cidr") + + def get_broadcast(self): + return self.get_attr("broadcast") + + def get_attr(self, attr): + if attr in self.address: + return self.address[attr] + else: + return "ERROR" + + def needs_vrrp(self): + """ + Returns if the ip needs to be managed by keepalived or not + """ + if "nw_type" in self.address and self.address['nw_type'] in [ 'guest' ]: + return True + return False + + def is_control(self): + if "nw_type" in self.address and self.address['nw_type'] in [ 'control' ]: + return True + return False + + def to_str(self): + pprint(self.address) + +class CsDevice: + """ Configure Network Devices """ + def __init__(self, dev, fw): + self.devlist = [] + self.dev = dev + self.buildlist() + self.table = '' + self.tableNo = '' + if dev != '': + self.tableNo = dev[3] + self.table = "Table_%s" % dev + self.fw = fw + + 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 + """ + self.devlist = [] + for line in open('/proc/net/dev'): + vals = line.lstrip().split(':') + if (not vals[0].startswith("eth")): + continue + self.devlist.append(vals[0]) + + + def waitfordevice(self): + """ Wait up to 15 seconds for a device to become available """ + count = 0 + while count < 15: + if self.dev in self.devlist: + return True + time.sleep(1) + count += 1 + self.buildlist(); + logging.error("Device %s cannot be configured - device was not found", self.dev) + return False + + def list(self): + return self.devlist + + def setUp(self): + """ Ensure device is up """ + cmd = "ip link show %s | grep 'state DOWN'" % self.dev + for i in CsHelper.execute(cmd): + if " DOWN " in i: + cmd2 = "ip link set %s up" % self.dev + CsHelper.execute(cmd2) + cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark 0x%s/0xffffffff" % \ + (self.dev, self.dev[3]) + self.fw.append(["mangle", "", cmd]) + + +class CsIP: + + def __init__(self, dev, fw): + self.dev = dev + self.iplist = {} + self.address = {} + self.list() + self.fw = fw + + 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) + cmd = "ip addr add dev %s %s brd +" % (self.dev, self.ip()) + subprocess.call(cmd, shell=True) + self.post_configure() + + def post_configure(self): + """ The steps that must be done after a device is configured """ + if not self.get_type() in [ "control" ]: + route = CsRoute(self.dev) + route.routeTable() + CsRule(self.dev).addMark() + CsDevice(self.dev, self.fw).setUp() + self.arpPing() + 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 get_ip_address(self): + """ + Return ip address if known + """ + if "public_ip" in self.address: + return self.address['public_ip'] + 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" ]: + devChain = "ACL_INBOUND_%s" % (self.dev) + CsDevice(self.dev, self.fw).configure_rp() + + self.fw.append(["nat", "front", + "-A POSTROUTING -s %s -o %s -j SNAT --to-source %s" % \ + (self.address['network'], self.dev, self.address['public_ip']) + ]) + self.fw.append(["mangle", "front", "-A %s -j ACCEPT" % devChain]) + + self.fw.append(["", "front", + "-A FORWARD -o %s -d %s -j %s" % (self.dev, self.address['network'], devChain) + ]) + self.fw.append(["", "", "-A %s -j DROP" % devChain]) + self.fw.append(["mangle", "", + "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j %s" % \ + (self.dev, self.address['network'], self.address['public_ip'], devChain) + ]) + dns = CsDnsmasq(self) + dns.add_firewall_rules() + app = CsApache(self) + app.setup() + pwdsvc = CsPasswdSvc(self).setup() + elif self.get_type() == "public": + if self.address["source_nat"] == True: + cmdline = CsDataBag("cmdline", self.fw) + dbag = cmdline.get_bag() + type = dbag["config"]["type"] + if type == "vpcrouter": + vpccidr = dbag["config"]["vpccidr"] + self.fw.append(["filter", "", "-A FORWARD -s %s ! -d %s -j ACCEPT" % (vpccidr, vpccidr)]) + self.fw.append(["nat","","-A POSTROUTING -j SNAT -o %s --to-source %s" % (self.dev, self.address['public_ip'])]) + elif type == "router": + logging.error("Not able to setup sourcenat for a regular router yet") + else: + logging.error("Unable to process source nat configuration for router of type %s" % type) + route.flush() + + def list(self): + self.iplist = {} + cmd = ("ip addr show dev " + self.dev) + for i in CsHelper.execute(cmd): + vals = i.lstrip().split() + if (vals[0] == 'inet'): + self.iplist[vals[1]] = self.dev + + def configured(self): + if self.address['cidr'] in self.iplist.keys(): + return True + return False + + def ip(self): + return str(self.address['cidr']) + + def getDevice(self): + return self.dev + + def hasIP(self, ip): + return ip in self.address.values() + + def arpPing(self): + cmd = "arping -c 1 -I %s -A -U -s %s %s" % (self.dev, self.address['public_ip'], self.address['public_ip']) + CsHelper.execute(cmd) + + # Delete any ips that are configured but not in the bag + def compare(self, bag): + if len(self.iplist) > 0 and (not self.dev in bag.keys() or len(bag[self.dev]) == 0): + # Remove all IPs on this device + logging.info("Will remove all configured addresses on device %s", self.dev) + self.delete("all") + app = CsApache(self) + app.remove() + + # This condition should not really happen but did :) + # It means an apache file got orphaned after a guest network address was deleted + if len(self.iplist) == 0 and (not self.dev in bag.keys() or len(bag[self.dev]) == 0): + app = CsApache(self) + app.remove() + + for ip in self.iplist: + found = False + if self.dev in bag.keys(): + for address in bag[self.dev]: + self.setAddress(address) + if self.hasIP(ip): + found = True + if not found: + self.delete(ip) + + def delete(self, ip): + remove = [] + if ip == "all": + logging.info("Removing addresses from device %s", self.dev) + remove = self.iplist.keys() + else: + remove.append(ip) + for ip in remove: + 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") + +class CsRpsrfs: + """ Configure rpsrfs if there is more than one cpu """ + + def __init__(self, dev): + self.dev = dev + + def enable(self): + if not self.inKernel(): return + cpus = self.cpus() + if cpus < 2: return + val = format((1 << cpus) - 1, "x") + filename = "/sys/class/net/%s/queues/rx-0/rps_cpus" % (self.dev) + CsHelper.updatefile(filename, val, "w+") + CsHelper.updatefile("/proc/sys/net/core/rps_sock_flow_entries", "256", "w+") + filename = "/sys/class/net/%s/queues/rx-0/rps_flow_cnt" % (self.dev) + CsHelper.updatefile(filename, "256", "w+") + logging.debug("rpsfr is configured for %s cpus" % (cpus)) + + def inKernel(self): + try: + open('/etc/rpsrfsenable') + except IOError: + logging.debug("rpsfr is not present in the kernel") + return False + else: + logging.debug("rpsfr is present in the kernel") + return True + + def cpus(self): + count = 0 + for line in open('/proc/cpuinfo'): + if "processor" not in line: continue + count += 1 + if count < 2: logging.debug("Single CPU machine") + return count diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsApp.py b/systemvm/patches/debian/config/opt/cloud/bin/CsApp.py new file mode 100644 index 00000000000..e6c7e178b7f --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsApp.py @@ -0,0 +1,93 @@ +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 os +import CsHelper +from CsFile import CsFile +from CsProcess import CsProcess +import CsHelper + +class CsApp: + def __init__(self, ip): + self.dev = ip.getDevice() + self.ip = ip.get_ip_address() + self.type = ip.get_type() + self.fw = ip.fw + +class CsApache(CsApp): + """ Set up Apache """ + + def remove(self): + file = "/etc/apache2/conf.d/vhost%s.conf" % self.dev + if os.path.isfile(file): + os.remove(file) + CsHelper.service("apache2", "restart") + + + 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("", "\t" % (self.ip)) + file.search("", "\t" % (self.ip)) + file.search("", "\t" % (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() + if file.is_changed(): + CsHelper.service("apache2", "restart") + + self.fw.append(["", "front", + "-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 80 -j ACCEPT" % (self.dev, self.ip) + ]) + +class CsPasswdSvc(CsApp): + """ + nohup bash /opt/cloud/bin/vpc_passwd_server $ip >/dev/null 2>&1 & + """ + + def setup(self): + self.fw.append(["", "front", + "-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 8080 -j ACCEPT" % (self.dev, self.ip) + ]) + + proc = CsProcess(['/opt/cloud/bin/vpc_passwd_server', self.ip]) + if not proc.find(): + proc.start("/usr/bin/nohup", "2>&1 &") + + +class CsDnsmasq(CsApp): + """ Set up dnsmasq """ + + def add_firewall_rules(self): + """ Add the necessary firewall rules + """ + self.fw.append(["", "front", + "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev + ]) + + self.fw.append(["", "front", + "-A INPUT -i %s -d %s/32 -p udp -m udp --dport 53 -j ACCEPT" % (self.dev, self.ip) + ]) + + self.fw.append(["", "front", + "-A INPUT -i %s -d %s/32 -p tcp -m tcp --dport 53 -j ACCEPT" % ( self.dev, self.ip ) + ]) + + diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_databag.py b/systemvm/patches/debian/config/opt/cloud/bin/CsDatabag.py similarity index 74% rename from systemvm/patches/debian/config/opt/cloud/bin/cs_databag.py rename to systemvm/patches/debian/config/opt/cloud/bin/CsDatabag.py index daf570ffc5f..224d2875e22 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs_databag.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsDatabag.py @@ -19,13 +19,16 @@ from merge import dataBag class CsDataBag(object): - def __init__(self, key): + def __init__(self, key, fw = None): self.data = {} - db = dataBag() - db.setKey(key) - db.load() - self.dbag = db.getDataBag() - global fw + self.db = dataBag() + self.db.setKey(key) + self.db.load() + self.dbag = self.db.getDataBag() + self.fw = fw + + def dump(self): + print self.dbag def get_bag(self): return self.dbag @@ -33,11 +36,18 @@ class CsDataBag(object): def process(self): pass + def save(self): + """ + Call to the databag save routine + Use sparingly! + """ + self.db.save(self.dbag) + class CsCmdLine(CsDataBag): """ Get cmdline config parameters """ def is_redundant(self): - if "redundant" in self.dbag['config']: - return self.dbag['config']['redundant'] == "true" + if "redundant_router" in self.dbag['config']: + return self.dbag['config']['redundant_router'] == "true" return False def get_name(self): diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsNetfilter.py b/systemvm/patches/debian/config/opt/cloud/bin/CsNetfilter.py index 493738ed596..e9a1da8d48d 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/CsNetfilter.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsNetfilter.py @@ -17,7 +17,7 @@ # under the License. import CsHelper from pprint import pprint -from cs_databag import CsDataBag, CsCmdLine +from CsDatabag import CsDataBag, CsCmdLine import logging class CsChain(object): diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsProcess.py b/systemvm/patches/debian/config/opt/cloud/bin/CsProcess.py new file mode 100644 index 00000000000..2ed94c8bf39 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsProcess.py @@ -0,0 +1,27 @@ +import os +import re +import CsHelper +import logging + +class CsProcess(object): + """ Manipulate processes """ + + def __init__(self, search): + self.search = search + + def start(self, thru, background = ''): + #if(background): + #cmd = cmd + " &" + logging.info("Started %s", " ".join(self.search)) + os.system("%s %s %s" % (thru, " ".join(self.search), background)) + + def find(self): + self.pid = [] + for i in CsHelper.execute("ps aux"): + items = len(self.search) + proc = re.split("\s+", i)[items*-1:] + matches = len([m for m in proc if m in self.search]) + if matches == items: + self.pid.append(re.split("\s+", i)[1]) + return len(self.pid) > 0 + diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsRedundant.py b/systemvm/patches/debian/config/opt/cloud/bin/CsRedundant.py index 61a727e7b6f..980fa807cce 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/CsRedundant.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsRedundant.py @@ -33,7 +33,7 @@ import sys import os from pprint import pprint -from cs_databag import CsDataBag, CsCmdLine +from CsDatabag import CsDataBag, CsCmdLine import logging import CsHelper from CsFile import CsFile @@ -49,24 +49,30 @@ class CsRedundant(object): "arping_gateways.sh.templ", "check_bumpup.sh", "disable_pubip.sh", "services.sh", ] - CS_TEMPLATES_DIR = "/opt/cloud/templates" + CS_TEMPLATES_DIR = "/opt/cloud/templates" + CONNTRACKD_BIN = "/usr/sbin/conntrackd" + CONNTRACKD_LOCK = "/var/lock/conntrack.lock" + CONNTRACKD_CONFIG = "/etc/conntrackd/conntrackd.conf" - def __init__(self): - pass - def set(self, cl, address): - logging.debug("Router redundancy status is %s", cl.is_redundant()) + def __init__(self, cl, address): self.cl = cl self.address = address - if cl.is_redundant(): + + def set(self): + logging.debug("Router redundancy status is %s", self.cl.is_redundant()) + if self.cl.is_redundant(): self._redundant_on() else: self._redundant_off() def _redundant_off(self): + CsHelper.service("conntrackd", "stop") + CsHelper.service("keepalived", "stop") CsHelper.umount_tmpfs(self.CS_RAMDISK_DIR) CsHelper.rmdir(self.CS_RAMDISK_DIR) - CsHelper.rm("/etc/cron.d/heartbeat") + CsHelper.rm("/etc/conntrackd/conntrackd.conf") + CsHelper.rm("/etc/keepalived/keepalived.conf") def _redundant_on(self): CsHelper.mkdir(self.CS_RAMDISK_DIR, 0755, False) @@ -97,6 +103,11 @@ class CsRedundant(object): connt.search("[\s\t]Interface ", "\t\tInterface %s" % control.get_device()) connt.section("Address Ignore {", "}", self._collect_ignore_ips()) connt.commit() + if connt.is_changed(): + CsHelper.service("conntrackd", "restart") + + if file.is_changed(): + CsHelper.service("keepalived", "restart") # FIXME # enable/disable_pubip/master/slave etc. will need rewriting to use the new python config @@ -108,6 +119,28 @@ class CsRedundant(object): cron.add("*/1 * * * * root $SHELL %s/check_heartbeat.sh 2>&1 > /dev/null" % self.CS_ROUTER_DIR, -1) cron.commit() + def set_master(self): + """ + This will enable all the public ips on the router + It is part of the process that sets the current router to master + """ + ads = [o for o in self.address.get_ips() if o.needs_vrrp()] + for o in ads: + CsHelper.execute("ifconfig %s down" % o.get_device()) + CsHelper.execute("ifconfig %s up" % o.get_device()) + CsHelper.execute("arping -I %s -A %s -c 1" % (o.get_device(), o.get_ip())) + # FIXME Need to add in the default routes but I am unsure what the gateway is + # ip route add default via $gw table Table_$dev proto static + cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG) + CsHelper.execute("%s -c" % cmd) + CsHelper.execute("%s -f" % cmd) + CsHelper.execute("%s -R" % cmd) + CsHelper.execute("%s -B" % cmd) + CsHelper.service("ipsec", "restart") + CsHelper.service("xl2tpd", "restart") + CsHelper.service("cloud-passwd-srvr", "restart") + CsHelper.service("dnsmasq", "restart") + def _collect_ignore_ips(self): """ This returns a list of ip objects that should be ignored diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsRoute.py b/systemvm/patches/debian/config/opt/cloud/bin/CsRoute.py new file mode 100644 index 00000000000..b6825d76906 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsRoute.py @@ -0,0 +1,57 @@ +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 CsHelper +import logging + +class CsRoute: + """ Manage routes """ + + def __init__(self, dev): + self.dev = dev + self.tableNo = dev[3] + self.table = "Table_%s" % (dev) + + def routeTable(self): + str = "%s %s" % (self.tableNo, self.table) + filename = "/etc/iproute2/rt_tables" + CsHelper.addifmissing(filename, str) + + def flush(self): + CsHelper.execute("ip route flush table %s" % (self.table)) + CsHelper.execute("ip route flush cache") + + def add(self, address, method = "add"): + # ip route show dev eth1 table Table_eth1 10.0.2.0/24 + if(method == "add"): + cmd = "dev %s table %s %s" % (self.dev, self.table, address['network']) + self.set_route(cmd, method) + + 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 and method == "add": + logging.info("Add " + cmd) + cmd = "ip route add " + cmd + elif found and method == "delete": + logging.info("Delete " + cmd) + cmd = "ip route delete " + cmd + else: + return + CsHelper.execute(cmd) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsRule.py b/systemvm/patches/debian/config/opt/cloud/bin/CsRule.py new file mode 100644 index 00000000000..e1fe17162ef --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsRule.py @@ -0,0 +1,26 @@ +import CsHelper +import logging + +class CsRule: + """ Manage iprules + Supported Types: + fwmark + """ + + def __init__(self, dev): + self.dev = dev + self.tableNo = dev[3] + self.table = "Table_%s" % (dev) + + def addMark(self): + if not self.findMark(): + cmd = "ip rule add fwmark %s table %s" % (self.tableNo, self.table) + CsHelper.execute(cmd) + logging.info("Added fwmark rule for %s" % (self.table)) + + def findMark(self): + srch = "from all fwmark 0x%s lookup %s" % (self.tableNo, self.table) + for i in CsHelper.execute("ip rule show"): + if srch in i.strip(): + return True + return False diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index adbfa3ba4bf..aee91bd7fc6 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -18,8 +18,9 @@ # under the License. import sys import os + from merge import dataBag -from cs_databag import CsDataBag, CsCmdLine +from CsDatabag import CsDataBag, CsCmdLine from pprint import pprint import subprocess import logging @@ -28,421 +29,15 @@ import time import shutil import os.path import CsHelper + from CsNetfilter import CsNetfilters from fcntl import flock, LOCK_EX, LOCK_UN from CsDhcp import CsDhcp from CsRedundant import * from CsFile import CsFile - -fw = [] - -class CsRule: - """ Manage iprules - Supported Types: - fwmark - """ - - def __init__(self, dev): - self.dev = dev - self.tableNo = dev[3] - self.table = "Table_%s" % (dev) - - def addMark(self): - if not self.findMark(): - cmd = "ip rule add fwmark %s table %s" % (self.tableNo, self.table) - CsHelper.execute(cmd) - logging.info("Added fwmark rule for %s" % (self.table)) - - def findMark(self): - srch = "from all fwmark 0x%s lookup %s" % (self.tableNo, self.table) - for i in CsHelper.execute("ip rule show"): - if srch in i.strip(): - return True - return False - - -class CsRoute: - """ Manage routes """ - - def __init__(self, dev): - self.dev = dev - self.tableNo = dev[3] - self.table = "Table_%s" % (dev) - - def routeTable(self): - str = "%s %s" % (self.tableNo, self.table) - filename = "/etc/iproute2/rt_tables" - CsHelper.addifmissing(filename, str) - - def flush(self): - CsHelper.execute("ip route flush table %s" % (self.table)) - CsHelper.execute("ip route flush cache") - - def add(self, address, method = "add"): - # ip route show dev eth1 table Table_eth1 10.0.2.0/24 - if(method == "add"): - cmd = "dev %s table %s %s" % (self.dev, self.table, address['network']) - self.set_route(cmd, method) - - 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 and method == "add": - logging.info("Add " + cmd) - cmd = "ip route add " + cmd - elif found and method == "delete": - logging.info("Delete " + cmd) - cmd = "ip route delete " + cmd - else: - return - CsHelper.execute(cmd) - - -class CsRpsrfs: - """ Configure rpsrfs if there is more than one cpu """ - - def __init__(self, dev): - self.dev = dev - - def enable(self): - if not self.inKernel(): return - cpus = self.cpus() - if cpus < 2: return - val = format((1 << cpus) - 1, "x") - filename = "/sys/class/net/%s/queues/rx-0/rps_cpus" % (self.dev) - CsHelper.updatefile(filename, val, "w+") - CsHelper.updatefile("/proc/sys/net/core/rps_sock_flow_entries", "256", "w+") - filename = "/sys/class/net/%s/queues/rx-0/rps_flow_cnt" % (self.dev) - CsHelper.updatefile(filename, "256", "w+") - logging.debug("rpsfr is configured for %s cpus" % (cpus)) - - def inKernel(self): - try: - open('/etc/rpsrfsenable') - except IOError: - logging.debug("rpsfr is not present in the kernel") - return False - else: - logging.debug("rpsfr is present in the kernel") - return True - - def cpus(self): - count = 0 - for line in open('/proc/cpuinfo'): - if "processor" not in line: continue - count += 1 - if count < 2: logging.debug("Single CPU machine") - return count - - -class CsProcess(object): - """ Manipulate processes """ - - def __init__(self, search): - self.search = search - - def start(self, thru, background = ''): - #if(background): - #cmd = cmd + " &" - logging.info("Started %s", " ".join(self.search)) - os.system("%s %s %s" % (thru, " ".join(self.search), background)) - - def find(self): - self.pid = [] - for i in CsHelper.execute("ps aux"): - items = len(self.search) - proc = re.split("\s+", i)[items*-1:] - matches = len([m for m in proc if m in self.search]) - if matches == items: - self.pid.append(re.split("\s+", i)[1]) - return len(self.pid) > 0 - - -class CsApp: - def __init__(self, ip): - self.dev = ip.getDevice() - self.ip = ip.get_ip_address() - self.type = ip.get_type() - global fw - -class CsPasswdSvc(CsApp): - """ - nohup bash /opt/cloud/bin/vpc_passwd_server $ip >/dev/null 2>&1 & - """ - - def setup(self): - fw.append(["", "front", - "-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 8080 -j ACCEPT" % (self.dev, self.ip) - ]) - - proc = CsProcess(['/opt/cloud/bin/vpc_passwd_server', self.ip]) - if not proc.find(): - proc.start("/usr/bin/nohup", "2>&1 &") - -class CsApache(CsApp): - """ Set up Apache """ - - def remove(self): - file = "/etc/apache2/conf.d/vhost%s.conf" % self.dev - if os.path.isfile(file): - os.remove(file) - CsHelper.service("apache2", "restart") - - - 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("", "\t" % (self.ip)) - file.search("", "\t" % (self.ip)) - file.search("", "\t" % (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() - if file.is_changed(): - CsHelper.service("apache2", "restart") - - fw.append(["", "front", - "-A INPUT -i %s -d %s/32 -p tcp -m tcp -m state --state NEW --dport 80 -j ACCEPT" % (self.dev, self.ip) - ]) - -class CsDnsmasq(CsApp): - """ Set up dnsmasq """ - - def add_firewall_rules(self): - """ Add the necessary firewall rules - """ - fw.append(["", "front", - "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev - ]) - - fw.append(["", "front", - "-A INPUT -i %s -d %s/32 -p udp -m udp --dport 53 -j ACCEPT" % (self.dev, self.ip) - ]) - - fw.append(["", "front", - "-A INPUT -i %s -d %s/32 -p tcp -m tcp --dport 53 -j ACCEPT" % ( self.dev, self.ip ) - ]) - -class CsDevice: - """ Configure Network Devices """ - def __init__(self, dev): - self.devlist = [] - self.dev = dev - self.buildlist() - self.table = '' - self.tableNo = '' - if dev != '': - self.tableNo = dev[3] - self.table = "Table_%s" % dev - global fw - - 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 - """ - self.devlist = [] - for line in open('/proc/net/dev'): - vals = line.lstrip().split(':') - if (not vals[0].startswith("eth")): - continue - self.devlist.append(vals[0]) - - - def waitfordevice(self): - """ Wait up to 15 seconds for a device to become available """ - count = 0 - while count < 15: - if self.dev in self.devlist: - return True - time.sleep(1) - count += 1 - self.buildlist(); - logging.error("Device %s cannot be configured - device was not found", self.dev) - return False - - def list(self): - return self.devlist - - def setUp(self): - """ Ensure device is up """ - cmd = "ip link show %s | grep 'state DOWN'" % self.dev - for i in CsHelper.execute(cmd): - if " DOWN " in i: - cmd2 = "ip link set %s up" % self.dev - CsHelper.execute(cmd2) - cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark 0x%s/0xffffffff" % \ - (self.dev, self.dev[3]) - fw.append(["mangle", "", cmd]) - - -class CsIP: - - def __init__(self, dev): - self.dev = dev - self.iplist = {} - self.address = {} - self.list() - - 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) - cmd = "ip addr add dev %s %s brd +" % (self.dev, self.ip()) - subprocess.call(cmd, shell=True) - self.post_configure() - - def post_configure(self): - """ The steps that must be done after a device is configured """ - if not self.get_type() in [ "control" ]: - route = CsRoute(self.dev) - route.routeTable() - CsRule(self.dev).addMark() - CsDevice(self.dev).setUp() - self.arpPing() - 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 get_ip_address(self): - """ - Return ip address if known - """ - if "public_ip" in self.address: - return self.address['public_ip'] - 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" ]: - devChain = "ACL_INBOUND_%s" % (self.dev) - CsDevice(self.dev).configure_rp() - - fw.append(["nat", "front", - "-A POSTROUTING -s %s -o %s -j SNAT --to-source %s" % \ - (self.address['network'], self.dev, self.address['public_ip']) - ]) - fw.append(["mangle", "front", "-A %s -j ACCEPT" % devChain]) - - fw.append(["", "front", - "-A FORWARD -o %s -d %s -j %s" % (self.dev, self.address['network'], devChain) - ]) - fw.append(["", "", "-A %s -j DROP" % devChain]) - fw.append(["mangle", "", - "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j %s" % \ - (self.dev, self.address['network'], self.address['public_ip'], devChain) - ]) - dns = CsDnsmasq(self) - dns.add_firewall_rules() - app = CsApache(self) - app.setup() - pwdsvc = CsPasswdSvc(self).setup() - elif self.get_type() == "public": - if self.address["source_nat"] == True: - cmdline = CsDataBag("cmdline") - dbag = cmdline.get_bag() - type = dbag["config"]["type"] - if type == "vpcrouter": - vpccidr = dbag["config"]["vpccidr"] - fw.append(["filter", "", "-A FORWARD -s %s ! -d %s -j ACCEPT" % (vpccidr, vpccidr)]) - fw.append(["nat","","-A POSTROUTING -j SNAT -o %s --to-source %s" % (self.dev, self.address['public_ip'])]) - elif type == "router": - logging.error("Not able to setup sourcenat for a regular router yet") - else: - logging.error("Unable to process source nat configuration for router of type %s" % type) - route.flush() - - def list(self): - self.iplist = {} - cmd = ("ip addr show dev " + self.dev) - for i in CsHelper.execute(cmd): - vals = i.lstrip().split() - if (vals[0] == 'inet'): - self.iplist[vals[1]] = self.dev - - def configured(self): - if self.address['cidr'] in self.iplist.keys(): - return True - return False - - def ip(self): - return str(self.address['cidr']) - - def getDevice(self): - return self.dev - - def hasIP(self, ip): - return ip in self.address.values() - - def arpPing(self): - cmd = "arping -c 1 -I %s -A -U -s %s %s" % (self.dev, self.address['public_ip'], self.address['public_ip']) - CsHelper.execute(cmd) - - # Delete any ips that are configured but not in the bag - def compare(self, bag): - if len(self.iplist) > 0 and (not self.dev in bag.keys() or len(bag[self.dev]) == 0): - # Remove all IPs on this device - logging.info("Will remove all configured addresses on device %s", self.dev) - self.delete("all") - app = CsApache(self) - app.remove() - - # This condition should not really happen but did :) - # It means an apache file got orphaned after a guest network address was deleted - if len(self.iplist) == 0 and (not self.dev in bag.keys() or len(bag[self.dev]) == 0): - app = CsApache(self) - app.remove() - - for ip in self.iplist: - found = False - if self.dev in bag.keys(): - for address in bag[self.dev]: - self.setAddress(address) - if self.hasIP(ip): - found = True - if not found: - self.delete(ip) - - def delete(self, ip): - remove = [] - if ip == "all": - logging.info("Removing addresses from device %s", self.dev) - remove = self.iplist.keys() - else: - remove.append(ip) - for ip in remove: - 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") +from CsAddress import CsAddress, CsInterface, CsDevice, CsIP +from CsApp import CsApache, CsPasswdSvc, CsDnsmasq +from CsRoute import CsRoute class CsPassword(CsDataBag): @@ -473,7 +68,7 @@ class CsAcl(CsDataBag): class AclDevice(): """ A little class for each list of acls per device """ - def __init__(self, obj): + def __init__(self, obj, fw): self.ingess = [] self.egress = [] self.device = obj['device'] @@ -484,6 +79,7 @@ class CsAcl(CsDataBag): self.ingress = obj['ingress_rules'] if "egress_rules" in obj.keys(): self.egress = obj['egress_rules'] + self.fw = fw def create(self): self.process("ingress", self.ingress) @@ -491,14 +87,15 @@ class CsAcl(CsDataBag): def process(self, direction, rule_list): for i in rule_list: - r = self.AclRule(direction, self.device, i) + r = self.AclRule(direction, self, i) r.create() class AclRule(): - def __init__(self, direction, device, rule): + def __init__(self, direction, acl, rule): self.table = "" - self.device = device + self.device = acl.device + self.fw = acl.fw self.chain = "ACL_INBOUND_%s" % self.device self.dest = "-s %s" % rule['cidr'] if direction == "egress": @@ -521,7 +118,6 @@ class CsAcl(CsDataBag): self.dport = "" if 'allowed' in rule.keys() and rule['allowed'] and rule['allowed']: self.action = "ACCEPT" - global fw if 'first_port' in rule.keys(): self.dport = "-m %s --dport %s" % (self.protocol, rule['first_port']) if 'last_port' in rule.keys() and self.dport and \ @@ -536,14 +132,14 @@ class CsAcl(CsDataBag): rstr = "%s -m icmp --icmp-type %s" % (rstr, self.icmp_type) rstr = "%s %s -j %s" % (rstr, self.dport, self.action) rstr = rstr.replace(" ", " ").lstrip() - fw.append([self.table, "front", rstr]) + self.fw.append([self.table, "front", rstr]) def process(self): for item in self.dbag: if item == "id": continue - dev_obj = self.AclDevice(self.dbag[item]).create() + dev_obj = self.AclDevice(self.dbag[item], self.fw).create() class CsVmMetadata(CsDataBag): @@ -697,125 +293,6 @@ class CsVmMetadata(CsDataBag): sys.exit(1) #FIXME return True -class CsAddress(CsDataBag): - - def compare(self): - for dev in CsDevice('').list(): - ip = CsIP(dev) - ip.compare(self.dbag) - - def get_ips(self): - ret = [] - for dev in self.dbag: - if dev == "id": - continue - for ip in self.dbag[dev]: - ret.append(CsInterface(ip)) - return ret - - def needs_vrrp(self,o): - """ - Returns if the ip needs to be managed by keepalived or not - """ - if "nw_type" in o and o['nw_type'] in [ 'guest' ]: - return True - return False - - def get_control_if(self): - """ - Return the address object that has the control interface - """ - for ip in self.get_ips(): - if ip.is_control(): - return ip - return None - - def process(self): - for dev in self.dbag: - if dev == "id": - continue - ip = CsIP(dev) - addcnt = 0 - for address in self.dbag[dev]: - if not address["nw_type"] == "control": - CsRoute(dev).add(address) - ip.setAddress(address) - if ip.configured(): - logging.info("Address %s on device %s already configured", ip.ip(), dev) - ip.post_configure() - else: - logging.info("Address %s on device %s not configured", ip.ip(), dev) - if CsDevice(dev).waitfordevice(): - ip.configure() - # This could go one level up but the ip type is stored in the - # ip address object and not in the device object - # Call only once - if addcnt == 0: - self.add_netstats(address) - addcnt += 1 - - def add_netstats(self, address): - # add in the network stats iptables rules - dev = "eth%s" % address['nic_dev_id'] - if address["nw_type"] == "public_ip": - fw.append(["", "front", "-A FORWARD -j NETWORK_STATS"]) - fw.append(["", "front", "-A INPUT -j NETWORK_STATS"]) - fw.append(["", "front", "-A OUTPUT -j NETWORK_STATS"]) - # it is not possible to calculate these devices - # When the vrouter and the vpc router are combined this silliness can go - fw.append(["", "", "-A NETWORK_STATS -i %s -o eth0 -p tcp" % dev]) - fw.append(["", "", "-A NETWORK_STATS -o %s -i eth0 -p tcp" % dev]) - fw.append(["", "", "-A NETWORK_STATS -o %s ! -i eth0 -p tcp" % dev]) - fw.append(["", "", "-A NETWORK_STATS -i %s ! -o eth0 -p tcp" % dev]) - - if address["nw_type"] == "guest": - fw.append(["", "front", "-A FORWARD -j NETWORK_STATS_%s" % dev]) - fw.append(["", "front", "-A NETWORK_STATS_%s -o %s -s %s" % (dev, dev, address['network'])]) - fw.append(["", "front", "-A NETWORK_STATS_%s -o %s -d %s" % (dev, dev, address['network'])]) - # Only relevant if there is a VPN configured so will have to move - # at some stage - fw.append(["mangle", "", "-A FORWARD -j VPN_STATS_%s" % dev]) - fw.append(["mangle", "", "-A VPN_STATS_%s -o %s -m mark --set-xmark 0x525/0xffffffff" % (dev, dev)]) - fw.append(["mangle", "", "-A VPN_STATS_%s -i %s -m mark --set-xmark 0x524/0xffffffff" % (dev, dev)]) - -class CsInterface: - """ Hold one single ip """ - def __init__(self, o): - self.address = o - - def get_ip(self): - return self.get_attr("public_ip") - - def get_device(self): - return self.get_attr("device") - - def get_cidr(self): - return self.get_attr("cidr") - - def get_broadcast(self): - return self.get_attr("broadcast") - - def get_attr(self, attr): - if attr in self.address: - return self.address[attr] - else: - return "ERROR" - - def needs_vrrp(self): - """ - Returns if the ip needs to be managed by keepalived or not - """ - if "nw_type" in self.address and self.address['nw_type'] in [ 'guest' ]: - return True - return False - - def is_control(self): - if "nw_type" in self.address and self.address['nw_type'] in [ 'control' ]: - return True - return False - - def to_str(self): - pprint(self.address) class CsSite2SiteVpn(CsDataBag): """ @@ -860,15 +337,15 @@ class CsSite2SiteVpn(CsDataBag): CsHelper.execute("ipsec auto --rereadall") def configure_iptables(self, dev, obj): - fw.append([ "", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -j ACCEPT" % dev ]) - fw.append([ "", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -j ACCEPT" % dev ]) - fw.append([ "", "front", "-A INPUT -i %s -p esp -j ACCEPT" % dev ]) - fw.append([ "nat", "front", "-A POSTROUTING -t nat -o %s-m mark --set-xmark 0x525/0xffffffff -j ACCEPT" % dev ]) + self.fw.append([ "", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -j ACCEPT" % dev ]) + self.fw.append([ "", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -j ACCEPT" % dev ]) + self.fw.append([ "", "front", "-A INPUT -i %s -p esp -j ACCEPT" % dev ]) + self.fw.append([ "nat", "front", "-A POSTROUTING -t nat -o %s-m mark --set-xmark 0x525/0xffffffff -j ACCEPT" % dev ]) for net in obj['peer_guest_cidr_list'].lstrip().rstrip().split(','): - fw.append([ "mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) - fw.append([ "mangle", "", "-A OUTPUT -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) - fw.append([ "mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr'])]) - fw.append([ "mangle", "", "-A INPUT -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr']) ]) + self.fw.append([ "mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) + self.fw.append([ "mangle", "", "-A OUTPUT -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) + self.fw.append([ "mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr'])]) + self.fw.append([ "mangle", "", "-A INPUT -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr']) ]) def configure_ipsec(self, obj): leftpeer = obj['local_public_ip'] @@ -917,10 +394,7 @@ class CsSite2SiteVpn(CsDataBag): class CsForwardingRules(CsDataBag): - def __init__(self, key): - super(CsForwardingRules, self).__init__(key) - global fw - + def process(self): for public_ip in self.dbag: if public_ip == "id": @@ -960,7 +434,7 @@ class CsForwardingRules(CsDataBag): fwrule += " -j DNAT --to-destination %s" % rule["internal_ip"] if not rule["internal_ports"] == "any": fwrule += ":" + self.portsToString(rule["internal_ports"], "-") - fw.append(["nat","",fwrule]) + self.fw.append(["nat","",fwrule]) def processStaticNatRule(self, rule): @@ -968,47 +442,43 @@ class CsForwardingRules(CsDataBag): device = self.getDeviceByIp(rule["public_ip"]) if device == None: raise Exception("Ip address %s has no device in the ips databag" % rule["public_ip"]) - fw.append(["nat","front","-A PREROUTING -d %s/32 -j DNAT --to-destination %s" % ( rule["public_ip"], rule["internal_ip"]) ]) - fw.append(["nat","front","-A POSTROUTING -o %s -s %s/32 -j SNAT --to-source %s" % ( device, rule["internal_ip"], rule["public_ip"]) ]) + self.fw.append(["nat","front","-A PREROUTING -d %s/32 -j DNAT --to-destination %s" % ( rule["public_ip"], rule["internal_ip"]) ]) + self.fw.append(["nat","front","-A POSTROUTING -o %s -s %s/32 -j SNAT --to-source %s" % ( device, rule["internal_ip"], rule["public_ip"]) ]) def main(argv): - + fw = [] logging.basicConfig(filename='/var/log/cloud.log', level=logging.DEBUG, format='%(asctime)s %(message)s') - cl = CsCmdLine("cmdline") - - address = CsAddress("ips") + cl = CsCmdLine("cmdline", fw) + address = CsAddress("ips", fw) address.compare() address.process() - password = CsPassword("vmpassword") + password = CsPassword("vmpassword", fw) password.process() - metadata = CsVmMetadata('vmdata') + metadata = CsVmMetadata('vmdata', fw) metadata.process() - acls = CsAcl('networkacl') + acls = CsAcl('networkacl', fw) acls.process() - fwd = CsForwardingRules("forwardingrules") + fwd = CsForwardingRules("forwardingrules", fw) fwd.process() - vpns = CsSite2SiteVpn("site2sitevpn") + vpns = CsSite2SiteVpn("site2sitevpn", fw) vpns.process() - red = CsRedundant() -# Move to init and make a single call? - red.set(cl, address) + red = CsRedundant(cl, address) + red.set() nf = CsNetfilters() nf.compare(fw) - dh = CsDataBag("dhcpentry") + dh = CsDataBag("dhcpentry", fw) dhcp = CsDhcp(dh.get_bag(), cl) - - if __name__ == "__main__": main(sys.argv) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_cmdline.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_cmdline.py index 86c007f0168..e4445af134b 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs_cmdline.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_cmdline.py @@ -1,9 +1,9 @@ from pprint import pprint def merge(dbag, cmdline): - if 'redundant' in cmdline['cmd_line']: - cmdline['cmd_line']['redundant'] = "true" + if 'redundant_router' in cmdline['cmd_line']: + cmdline['cmd_line']['redundant_router'] = "true" else: - cmdline['cmd_line']['redundant'] = "false" + cmdline['cmd_line']['redundant_router'] = "false" dbag['config'] = cmdline['cmd_line'] return dbag diff --git a/systemvm/patches/debian/config/opt/cloud/bin/master.py b/systemvm/patches/debian/config/opt/cloud/bin/master.py new file mode 100755 index 00000000000..e89a5ef1c44 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/master.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +from CsRedundant import CsRedundant +from CsDatabag import CsCmdLine +from CsAddress import CsAddress + +cl = CsCmdLine("cmdline") +address = CsAddress("ips") +red = CsRedundant(cl, address) +red.set_master() diff --git a/systemvm/patches/debian/config/opt/cloud/bin/set_redundant.py b/systemvm/patches/debian/config/opt/cloud/bin/set_redundant.py new file mode 100755 index 00000000000..1b97e0ad4a6 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/set_redundant.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# This file is used by the tests to switch the redundancy status + +from CsDatabag import CsCmdLine +from optparse import OptionParser + +parser = OptionParser() +parser.add_option("-e", "--enable", + action="store_true", default=False, dest="enable", + help="Set router redundant") +parser.add_option("-d", "--disable", + action="store_true", default=False, dest="disable", + help="Set router non redundant") + +(options, args) = parser.parse_args() + +cl = CsCmdLine("cmdline") +if options.enable: + cl.dbag['config']['redundant_router'] = "true" +if options.disable: + cl.dbag['config']['redundant_router'] = "false" + +cl.save() diff --git a/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ b/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ index a4969a5b698..ac9f1780365 100644 --- a/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ +++ b/systemvm/patches/debian/config/opt/cloud/templates/keepalived.conf.templ @@ -51,7 +51,7 @@ vrrp_instance inside_network { heartbeat } - notify_master "[RROUTER_BIN_PATH]/master.sh" + notify_master "/opt/cloud/bin/master.py" notify_backup "[RROUTER_BIN_PATH]/backup.sh" notify_fault "[RROUTER_BIN_PATH]/fault.sh" } diff --git a/test/systemvm/test_update_config.py b/test/systemvm/test_update_config.py index 5873babe03c..bff4eefe229 100644 --- a/test/systemvm/test_update_config.py +++ b/test/systemvm/test_update_config.py @@ -144,6 +144,18 @@ class UpdateConfigTestCase(SystemVMTestCase): } ] + def redundant(self, what): + with hide("everything"): + result = run("python /opt/cloud/bin/set_redundant.py %s" % what, + timeout=600, warn_only=True) + assert result.succeeded, 'Set redundancy to %s' % what + + def configure(self): + with hide("everything"): + result = run("python /opt/cloud/bin/configure.py", + timeout=600, warn_only=True) + assert result.succeeded, "Configure ran" + def update_config(self, config): config_json = json.dumps(config, indent=2) #print_doc('config.json', config_json) @@ -375,6 +387,15 @@ class UpdateConfigTestCase(SystemVMTestCase): config['add'] = False self.update_config(config) assert not ip.has_ip("%s/%s" % (config['router_guest_ip'], config['cidr']), config['device']) + # Now setup what we have redundant + self.redundant("-e") + self.configure() + assert process.is_up("keepalived"), "Keepalived should be running after enabling redundancy" + assert process.is_up("conntrackd"), "Conntrackd should be running after enabling redundancy" + self.redundant("-d") + self.configure() + assert not process.is_up("keepalived"), "Keepalived should be not running after disabling redundancy" + assert not process.is_up("conntrackd"), "Conntrackd should be not running after disabling redundancy" for o in configs: o['add'] = False self.update_config(o)