backup fault and master implemented

Some more refactoring to decluter the bin directory
New config class to carry around the log, command line and firewall data
This commit is contained in:
Ian Southam 2014-09-24 16:06:19 +02:00 committed by wilderrodrigues
parent 8a2391336c
commit 7b95b78223
20 changed files with 342 additions and 122 deletions

View File

@ -1,27 +0,0 @@
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

View File

@ -1,26 +0,0 @@
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

View File

@ -20,7 +20,6 @@ import sys
import os
from merge import dataBag
from CsDatabag import CsDataBag, CsCmdLine
from pprint import pprint
import subprocess
import logging
@ -38,7 +37,16 @@ from CsFile import CsFile
from CsAddress import CsAddress, CsInterface, CsDevice, CsIP
from CsApp import CsApache, CsPasswdSvc, CsDnsmasq
from CsRoute import CsRoute
from fcntl import flock, LOCK_EX, LOCK_UN
from cs.CsDatabag import CsDataBag, CsCmdLine
import cs.CsHelper
from cs.CsNetfilter import CsNetfilters
from cs.CsDhcp import CsDhcp
from cs.CsRedundant import *
from cs.CsFile import CsFile
from cs.CsAddress import CsAddress
from cs.CsApp import CsApache, CsPasswdSvc, CsDnsmasq
class CsPassword(CsDataBag):
"""
@ -445,40 +453,42 @@ class CsForwardingRules(CsDataBag):
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", fw)
address = CsAddress("ips", fw)
config = CsConfig(False)
logging.basicConfig(filename= config.get_logger(),
level=config.get_level(),
format=config.get_format())
config.set_cl()
cl = config.get_cmdline()
address = CsAddress("ips", config)
address.compare()
address.process()
password = CsPassword("vmpassword", fw)
password = CsPassword("vmpassword", config)
password.process()
metadata = CsVmMetadata('vmdata', fw)
metadata = CsVmMetadata('vmdata', config)
metadata.process()
acls = CsAcl('networkacl', fw)
acls = CsAcl('networkacl', config)
acls.process()
fwd = CsForwardingRules("forwardingrules", fw)
fwd = CsForwardingRules("forwardingrules", config)
fwd.process()
vpns = CsSite2SiteVpn("site2sitevpn", fw)
vpns = CsSite2SiteVpn("site2sitevpn", config)
vpns.process()
red = CsRedundant(cl, address)
red = CsRedundant(config, address)
red.set()
nf = CsNetfilters()
nf.compare(fw)
nf.compare(config.get_fw())
dh = CsDataBag("dhcpentry", fw)
dhcp = CsDhcp(dh.get_bag(), cl)
dh = CsDataBag("dhcpentry")
dhcp = CsDhcp(dh.get_bag(), config.get_cmdline())
if __name__ == "__main__":
main(sys.argv)

View File

@ -24,11 +24,13 @@ import subprocess
from CsRoute import CsRoute
from CsRule import CsRule
VRRP_TYPES = [ 'guest', 'public' ]
class CsAddress(CsDataBag):
def compare(self):
for dev in CsDevice('', self.fw).list():
ip = CsIP(dev, self.fw)
for dev in CsDevice('', self.config).list():
ip = CsIP(dev, self.config)
ip.compare(self.dbag)
def get_ips(self):
@ -44,7 +46,7 @@ class CsAddress(CsDataBag):
"""
Returns if the ip needs to be managed by keepalived or not
"""
if "nw_type" in o and o['nw_type'] in [ 'guest' ]:
if "nw_type" in o and o['nw_type'] in VRRP_TYPES:
return True
return False
@ -61,7 +63,7 @@ class CsAddress(CsDataBag):
for dev in self.dbag:
if dev == "id":
continue
ip = CsIP(dev, self.fw)
ip = CsIP(dev, self.config)
addcnt = 0
for address in self.dbag[dev]:
if not address["nw_type"] == "control":
@ -72,7 +74,7 @@ class CsAddress(CsDataBag):
ip.post_configure()
else:
logging.info("Address %s on device %s not configured", ip.ip(), dev)
if CsDevice(dev, self.fw).waitfordevice():
if CsDevice(dev, self.config).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
@ -132,7 +134,7 @@ class CsInterface:
"""
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' ]:
if "nw_type" in self.address and self.address['nw_type'] in VRRP_TYPES:
return True
return False
@ -146,7 +148,7 @@ class CsInterface:
class CsDevice:
""" Configure Network Devices """
def __init__(self, dev, fw):
def __init__(self, dev, config):
self.devlist = []
self.dev = dev
self.buildlist()
@ -155,7 +157,8 @@ class CsDevice:
if dev != '':
self.tableNo = dev[3]
self.table = "Table_%s" % dev
self.fw = fw
self.fw = config.get_fw()
self.cl = config.get_cmdline()
def configure_rp(self):
"""
@ -191,26 +194,18 @@ class CsDevice:
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):
def __init__(self, dev, config):
self.dev = dev
self.iplist = {}
self.address = {}
self.list()
self.fw = fw
self.fw = config.get_fw()
self.cl = config.get_cmdline()
self.config = config
def setAddress(self, address):
self.address = address
@ -230,11 +225,28 @@ class CsIP:
route = CsRoute(self.dev)
route.routeTable()
CsRule(self.dev).addMark()
CsDevice(self.dev, self.fw).setUp()
self.check_is_up()
self.set_mark()
self.arpPing()
CsRpsrfs(self.dev).enable()
self.post_config_change("add")
def check_is_up(self):
""" Ensure device is up """
cmd = "ip link show %s | grep 'state DOWN'" % self.getDevice()
for i in CsHelper.execute(cmd):
if " DOWN " in i:
cmd2 = "ip link set %s up" % self.getDevice()
# Do not change the state of ips on a master redundant router that are managed by vrrp
if self.cl.is_master() or \
not self.needs_vrrp():
CsHelper.execute(cmd2)
def set_mark(self):
cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark 0x%s/0xffffffff" % \
(self.getDevice(), self.getDevice()[3])
self.fw.append(["mangle", "", cmd])
def get_type(self):
""" Return the type of the IP
guest
@ -260,7 +272,7 @@ class CsIP:
# 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()
CsDevice(self.dev, self.config).configure_rp()
self.fw.append(["nat", "front",
"-A POSTROUTING -s %s -o %s -j SNAT --to-source %s" % \
@ -283,14 +295,11 @@ class CsIP:
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"]
if self.cl.get_type() == "vpcrouter":
vpccidr = self.cl.get_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":
elif self.cl.get_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)
@ -309,6 +318,14 @@ class CsIP:
return True
return False
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 VRRP_TYPES:
return True
return False
def ip(self):
return str(self.address['cidr'])

View File

@ -0,0 +1,51 @@
# -- 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 CsCmdLine
import logging
class CsConfig(object):
"""
A class to cache all the stuff that the other classes need
"""
__LOG_FILE = "/var/log/cloud.log"
__LOG_LEVEL = "DEBUG"
__LOG_FORMAT = "%(asctime)s %(levelname)-8s %(message)s"
def __init__(self, load = False):
if load:
self.cl = self_set_cl()
self.fw = []
def set_cl(self):
self.cl = CsCmdLine("cmdline")
def get_cmdline(self):
return self.cl
def get_fw(self):
return self.fw
def get_logger(self):
return self.__LOG_FILE
def get_level(self):
return self.__LOG_LEVEL
def get_format(self):
return self.__LOG_FORMAT

View File

@ -19,13 +19,16 @@ from merge import dataBag
class CsDataBag(object):
def __init__(self, key, fw = None):
def __init__(self, key, config = None):
self.data = {}
self.db = dataBag()
self.db.setKey(key)
self.db.load()
self.dbag = self.db.getDataBag()
self.fw = fw
if config:
self.fw = config.get_fw()
self.cl = config.get_cmdline()
self.config = config
def dump(self):
print self.dbag
@ -45,6 +48,7 @@ class CsDataBag(object):
class CsCmdLine(CsDataBag):
""" Get cmdline config parameters """
def is_redundant(self):
if "redundant_router" in self.dbag['config']:
return self.dbag['config']['redundant_router'] == "true"
@ -62,3 +66,16 @@ class CsCmdLine(CsDataBag):
else:
return "unknown"
def get_vpccidr(self):
if "vpccidr" in self.dbag['config']:
return self.dbag['config']['vpccidr']
else:
return "unknown"
def is_master(self):
if not self.is_redundant():
return False
if "redundant_master" in self.dbag['config']:
return self.dbag['config']['redundant_master'] == "true"
return False

View File

@ -1,3 +1,20 @@
# -- 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.
""" General helper functions
for use in the configuation process

View File

@ -0,0 +1,44 @@
# -- 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 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

View File

@ -37,6 +37,7 @@ from CsDatabag import CsDataBag, CsCmdLine
import logging
import CsHelper
from CsFile import CsFile
from CsConfig import CsConfig
class CsRedundant(object):
@ -55,8 +56,8 @@ class CsRedundant(object):
CONNTRACKD_CONFIG = "/etc/conntrackd/conntrackd.conf"
def __init__(self, cl, address):
self.cl = cl
def __init__(self, config, address):
self.cl = config.get_cmdline()
self.address = address
def set(self):
@ -119,11 +120,53 @@ 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_fault(self):
""" Set fault mode on this router """
if not self.cl.is_redundant():
logging.error("Set fault called on non-redundant router")
return
logging.info("Router switched to fault mode")
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())
cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG)
CsHelper.execute("%s -s" % cmd)
CsHelper.service("ipsec", "stop")
CsHelper.service("xl2tpd", "stop")
CsHelper.service("cloud-passwd-srvr", "stop")
CsHelper.service("dnsmasq", "stop")
cl.dbag['config']['redundant_master'] = "false"
cl.save()
def set_backup(self):
""" Set the current router to backup """
if not self.cl.is_redundant():
logging.error("Set backup called on non-redundant router")
return
if not self.cl.is_master():
logging.error("Set backup called on node that is already backup")
return
logging.info("Router switched to backup mode")
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())
cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONFIG)
CsHelper.execute("%s -d" % cmd)
CsHelper.service("ipsec", "stop")
CsHelper.service("xl2tpd", "stop")
CsHelper.service("cloud-passwd-srvr", "stop")
CsHelper.service("dnsmasq", "stop")
self.cl.dbag['config']['redundant_master'] = "false"
self.cl.save()
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
"""
""" Set the current router to master """
if not self.cl.is_redundant():
logging.error("Set master called on non-redundant router")
return
if self.cl.is_master():
logging.error("Set master called on master node")
return
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())
@ -140,6 +183,9 @@ class CsRedundant(object):
CsHelper.service("xl2tpd", "restart")
CsHelper.service("cloud-passwd-srvr", "restart")
CsHelper.service("dnsmasq", "restart")
self.cl.dbag['config']['redundant_master'] = "true"
self.cl.save()
logging.info("Router switched to master mode")
def _collect_ignore_ips(self):
"""

View File

@ -0,0 +1,40 @@
# -- 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.
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

View File

@ -17,11 +17,36 @@
# specific language governing permissions and limitations
# under the License.
from CsRedundant import CsRedundant
from CsDatabag import CsCmdLine
from CsAddress import CsAddress
from cs.CsRedundant import CsRedundant
from cs.CsDatabag import CsCmdLine
from cs.CsAddress import CsAddress
from cs.CsConfig import CsConfig
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-m", "--master",
action="store_true", default=False, dest="master",
help="Set router master")
parser.add_option("-b", "--backup",
action="store_true", default=False, dest="backup",
help="Set router backup")
parser.add_option("-f", "--fault",
action="store_true", default=False, dest="fault",
help="Notify Fault")
(options, args) = parser.parse_args()
config = CsConfig(False)
logging.basicConfig(filename= config.get_logger(),
level=config.get_level(),
format=config.get_format())
config.set_cl()
cl = CsCmdLine("cmdline", config)
cl = CsCmdLine("cmdline")
address = CsAddress("ips")
red = CsRedundant(cl, address)
red.set_master()
red = CsRedundant(config, address)
if options.master:
red.set_master()
if options.backup:
red.set_backup()

View File

@ -19,8 +19,9 @@
# This file is used by the tests to switch the redundancy status
from CsDatabag import CsCmdLine
from cs.CsConfig import CsConfig
from optparse import OptionParser
import logging
parser = OptionParser()
parser.add_option("-e", "--enable",
@ -32,10 +33,15 @@ parser.add_option("-d", "--disable",
(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"
config = CsConfig(False)
logging.basicConfig(filename= config.get_logger(),
level=config.get_level(),
format=config.get_format())
config.set_cl()
cl.save()
if options.enable:
config.get_cmdline().dbag['config']['redundant_router'] = "true"
if options.disable:
config.get_cmdline().dbag['config']['redundant_router'] = "false"
config.get_cmdline().save()

View File

@ -51,7 +51,7 @@ vrrp_instance inside_network {
heartbeat
}
notify_master "/opt/cloud/bin/master.py"
notify_backup "[RROUTER_BIN_PATH]/backup.sh"
notify_fault "[RROUTER_BIN_PATH]/fault.sh"
notify_master "/opt/cloud/bin/master.py --master"
notify_backup "/opt/cloud/bin/master.py --backup"
notify_fault "/opt/cloud/bin/master.py --fault"
}