Ian Southam 8a2391336c 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
2015-03-16 11:35:29 +01:00

171 lines
7.1 KiB
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.
# -------------------------------------------------------------------- #
# Notes
# -------------------------------------------------------------------- #
# Vrouter
#
# eth0 router gateway IP for isolated network
# eth1 Control IP for hypervisor
# eth2 public ip(s)
#
# VPC Router
#
# eth0 control interface
# eth1 public ip
# eth2+ Guest networks
# -------------------------------------------------------------------- #
import sys
import os
from pprint import pprint
from CsDatabag import CsDataBag, CsCmdLine
import logging
import CsHelper
from CsFile import CsFile
class CsRedundant(object):
CS_RAMDISK_DIR = "/ramdisk"
CS_ROUTER_DIR = "%s/rrouter" % CS_RAMDISK_DIR
CS_TEMPLATES = [
"enable_pubip.sh.templ",
"master.sh.templ", "backup.sh.templ", "fault.sh.templ",
"primary-backup.sh.templ", "heartbeat.sh.templ", "check_heartbeat.sh.templ",
"arping_gateways.sh.templ", "check_bumpup.sh", "disable_pubip.sh",
"services.sh",
]
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, cl, address):
self.cl = cl
self.address = address
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/conntrackd/conntrackd.conf")
CsHelper.rm("/etc/keepalived/keepalived.conf")
def _redundant_on(self):
CsHelper.mkdir(self.CS_RAMDISK_DIR, 0755, False)
CsHelper.mount_tmpfs(self.CS_RAMDISK_DIR)
CsHelper.mkdir(self.CS_ROUTER_DIR, 0755, False)
for s in self.CS_TEMPLATES:
d = s
if s.endswith(".templ"):
d = s.replace(".templ", "")
CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, s), "%s/%s" % (self.CS_ROUTER_DIR, d))
CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "keepalived.conf.templ"), "/etc/keepalived/keepalived.conf")
CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "conntrackd.conf.templ"), "/etc/conntrackd/conntrackd.conf")
CsHelper.copy_if_needed("%s/%s" % (self.CS_TEMPLATES_DIR, "checkrouter.sh.templ"), "/opt/cloud/bin/checkrouter.sh")
# keepalived configuration
file = CsFile("/etc/keepalived/keepalived.conf")
file.search(" router_id ", " router_id %s" % self.cl.get_name())
file.search(" priority ", " priority %s" % 20)
file.search(" weight ", " weight %s" % 2)
file.greplace("[RROUTER_BIN_PATH]", self.CS_ROUTER_DIR)
file.section("virtual_ipaddress {", "}", self._collect_ips())
file.commit()
# conntrackd configuration
control = self.address.get_control_if()
connt = CsFile("/etc/conntrackd/conntrackd.conf")
connt.search("[\s\t]IPv4_interface ", "\t\tIPv4_interface %s" % control.get_ip())
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
# Configure heartbeat cron job
cron = CsFile("/etc/cron.d/heartbeat")
cron.add("SHELL=/bin/bash", 0)
cron.add("PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin", 1)
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
by conntrackd
"""
lines = []
lines.append("\t\t\tIPv4_address %s\n" % "127.0.0.1")
lines.append("\t\t\tIPv4_address %s\n" % self.address.get_control_if().get_ip())
# FIXME - Do we need to also add any internal network gateways?
return lines
def _collect_ips(self):
"""
Construct a list containing all the ips that need to be looked afer by vrrp
This is based upon the address_needs_vrrp method in CsAddress which looks at
the network type and decides if it is an internal address or an external one
In a DomR there will only ever be one address in a VPC there can be many
The new code also gives the possibility to cloudstack to have a hybrid device
thet could function as a router and VPC router at the same time
"""
lines = []
for o in self.address.get_ips():
if o.needs_vrrp():
str = " %s brd %s dev %s\n" % (o.get_cidr(), o.get_broadcast(), o.get_ip())
lines.append(str)
return lines