mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Getting redundancy to work via teh configuration files
Some refactoring to make class loading a little more logical Removed fw global (does not persist accross modules) Added first tests Added command line call to set and disable redundancy Added command line call to set master
This commit is contained in:
parent
093749eaae
commit
8a2391336c
397
systemvm/patches/debian/config/opt/cloud/bin/CsAddress.py
Normal file
397
systemvm/patches/debian/config/opt/cloud/bin/CsAddress.py
Normal file
@ -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
|
||||
93
systemvm/patches/debian/config/opt/cloud/bin/CsApp.py
Normal file
93
systemvm/patches/debian/config/opt/cloud/bin/CsApp.py
Normal file
@ -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("<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()
|
||||
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 )
|
||||
])
|
||||
|
||||
|
||||
@ -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):
|
||||
@ -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):
|
||||
|
||||
27
systemvm/patches/debian/config/opt/cloud/bin/CsProcess.py
Normal file
27
systemvm/patches/debian/config/opt/cloud/bin/CsProcess.py
Normal file
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
57
systemvm/patches/debian/config/opt/cloud/bin/CsRoute.py
Normal file
57
systemvm/patches/debian/config/opt/cloud/bin/CsRoute.py
Normal file
@ -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)
|
||||
26
systemvm/patches/debian/config/opt/cloud/bin/CsRule.py
Normal file
26
systemvm/patches/debian/config/opt/cloud/bin/CsRule.py
Normal file
@ -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
|
||||
@ -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("<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()
|
||||
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,9 +394,6 @@ 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:
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
27
systemvm/patches/debian/config/opt/cloud/bin/master.py
Executable file
27
systemvm/patches/debian/config/opt/cloud/bin/master.py
Executable file
@ -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()
|
||||
41
systemvm/patches/debian/config/opt/cloud/bin/set_redundant.py
Executable file
41
systemvm/patches/debian/config/opt/cloud/bin/set_redundant.py
Executable file
@ -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()
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user