Michael Andersen bb93a61ee7 pep8 cleanup
2015-08-24 13:35:01 +02:00

333 lines
13 KiB
Python
Executable File

# -- 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 os
import logging
import CsHelper
from CsFile import CsFile
from CsProcess import CsProcess
from CsApp import CsPasswdSvc
from CsAddress import CsDevice
import socket
from time import sleep
class CsRedundant(object):
CS_RAMDISK_DIR = "/ramdisk"
CS_PRIO_UP = 1
CS_PRIO_DOWN = -1
CS_ROUTER_DIR = "%s/rrouter" % CS_RAMDISK_DIR
CS_TEMPLATES = [
"heartbeat.sh.templ", "check_heartbeat.sh.templ",
"arping_gateways.sh.templ"
]
CS_TEMPLATES_DIR = "/opt/cloud/templates"
CONNTRACKD_BIN = "/usr/sbin/conntrackd"
CONNTRACKD_KEEPALIVED_CONFLOCK = "/var/lock/conntrack.lock"
CONNTRACKD_CONF = "/etc/conntrackd/conntrackd.conf"
RROUTER_LOG = "/var/log/cloud.log"
KEEPALIVED_CONF = "/etc/keepalived/keepalived.conf"
def __init__(self, config):
self.cl = config.cmdline()
self.address = config.address()
self.config = config
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(self.CONNTRACKD_CONF)
CsHelper.rm(self.KEEPALIVED_CONF)
def _redundant_on(self):
guest = self.address.get_guest_if()
# No redundancy if there is no guest network
if self.cl.is_master() or guest is None:
for obj in [o for o in self.address.get_ips() if o.is_public()]:
self.check_is_up(obj.get_device())
if guest is None:
self._redundant_off()
return
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"), self.KEEPALIVED_CONF)
CsHelper.copy_if_needed(
"%s/%s" % (self.CS_TEMPLATES_DIR, "conntrackd.conf.templ"), self.CONNTRACKD_CONF)
CsHelper.copy_if_needed(
"%s/%s" % (self.CS_TEMPLATES_DIR, "checkrouter.sh.templ"), "/opt/cloud/bin/checkrouter.sh")
CsHelper.execute(
'sed -i "s/--exec\ \$DAEMON;/--exec\ \$DAEMON\ --\ --vrrp;/g" /etc/init.d/keepalived')
# checkrouter.sh configuration
check_router = CsFile("/opt/cloud/bin/checkrouter.sh")
check_router.greplace("[RROUTER_LOG]", self.RROUTER_LOG)
check_router.commit()
# keepalived configuration
keepalived_conf = CsFile(self.KEEPALIVED_CONF)
keepalived_conf.search(
" router_id ", " router_id %s" % self.cl.get_name())
keepalived_conf.search(
" interface ", " interface %s" % guest.get_device())
keepalived_conf.search(
" virtual_router_id ", " virtual_router_id %s" % self.cl.get_router_id())
keepalived_conf.greplace("[RROUTER_BIN_PATH]", self.CS_ROUTER_DIR)
keepalived_conf.section("authentication {", "}", [
" auth_type AH \n", " auth_pass %s\n" % self.cl.get_router_password()])
keepalived_conf.section(
"virtual_ipaddress {", "}", self._collect_ips())
keepalived_conf.commit()
# conntrackd configuration
connt = CsFile(self.CONNTRACKD_CONF)
if guest is not None:
connt.section("Multicast {", "}", [
"IPv4_address 225.0.0.50\n",
"Group 3780\n",
"IPv4_interface %s\n" % guest.get_ip(),
"Interface %s\n" % guest.get_device(),
"SndSocketBuffer 1249280\n",
"RcvSocketBuffer 1249280\n",
"Checksum on\n"])
connt.section("Address Ignore {", "}", self._collect_ignore_ips())
connt.commit()
if connt.is_changed():
CsHelper.service("conntrackd", "restart")
# Configure heartbeat cron job - runs every 30 seconds
heartbeat_cron = CsFile("/etc/cron.d/heartbeat")
heartbeat_cron.add("SHELL=/bin/bash", 0)
heartbeat_cron.add(
"PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin", 1)
heartbeat_cron.add(
"* * * * * root $SHELL %s/check_heartbeat.sh 2>&1 > /dev/null" % self.CS_ROUTER_DIR, -1)
heartbeat_cron.add(
"* * * * * root sleep 30; $SHELL %s/check_heartbeat.sh 2>&1 > /dev/null" % self.CS_ROUTER_DIR, -1)
heartbeat_cron.commit()
# Configure KeepaliveD cron job - runs at every reboot
keepalived_cron = CsFile("/etc/cron.d/keepalived")
keepalived_cron.add("SHELL=/bin/bash", 0)
keepalived_cron.add(
"PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin", 1)
keepalived_cron.add("@reboot root service keepalived start", -1)
keepalived_cron.commit()
# Configure ConntrackD cron job - runs at every reboot
conntrackd_cron = CsFile("/etc/cron.d/conntrackd")
conntrackd_cron.add("SHELL=/bin/bash", 0)
conntrackd_cron.add(
"PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin", 1)
conntrackd_cron.add("@reboot root service conntrackd start", -1)
conntrackd_cron.commit()
proc = CsProcess(['/usr/sbin/keepalived', '--vrrp'])
if not proc.find() or keepalived_conf.is_changed():
CsHelper.service("keepalived", "restart")
def release_lock(self):
try:
os.remove("/tmp/master_lock")
except OSError:
pass
def set_lock(self):
"""
Make sure that master state changes happen sequentially
"""
iterations = 10
time_between = 1
for iter in range(0, iterations):
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.bind('/tmp/master_lock')
return s
except socket.error, e:
error_code = e.args[0]
error_string = e.args[1]
print "Process already running (%d:%s). Exiting" % (error_code, error_string)
logging.info("Master is already running, waiting")
sleep(time_between)
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
self.set_lock()
logging.info("Router switched to fault mode")
ads = [o for o in self.address.get_ips() if o.is_public()]
for o in ads:
CsHelper.execute("ifconfig %s down" % o.get_device())
cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
CsHelper.execute("%s -s" % cmd)
CsHelper.service("ipsec", "stop")
CsHelper.service("xl2tpd", "stop")
CsHelper.service("dnsmasq", "stop")
ads = [o for o in self.address.get_ips() if o.needs_vrrp()]
for o in ads:
CsPasswdSvc(o.get_gateway()).stop()
self.cl.set_fault_state()
self.cl.save()
self.release_lock()
logging.info("Router switched to fault mode")
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
self.set_lock()
logging.debug("Setting router to backup")
ads = [o for o in self.address.get_ips() if o.is_public()]
dev = ''
for o in ads:
if dev == o.get_device():
continue
logging.info("Bringing public interface %s down" % o.get_device())
cmd2 = "ip link set %s up" % o.get_device()
CsHelper.execute(cmd2)
dev = o.get_device()
cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
CsHelper.execute("%s -d" % cmd)
CsHelper.service("ipsec", "stop")
CsHelper.service("xl2tpd", "stop")
ads = [o for o in self.address.get_ips() if o.needs_vrrp()]
for o in ads:
CsPasswdSvc(o.get_gateway()).stop()
CsHelper.service("dnsmasq", "stop")
self.cl.set_master_state(False)
self.cl.save()
self.release_lock()
logging.info("Router switched to backup mode")
def set_master(self):
""" Set the current router to master """
if not self.cl.is_redundant():
logging.error("Set master called on non-redundant router")
return
self.set_lock()
logging.debug("Setting router to master")
ads = [o for o in self.address.get_ips() if o.is_public()]
dev = ''
for o in ads:
if dev == o.get_device():
continue
cmd2 = "ip link set %s up" % o.get_device()
if CsDevice(o.get_device(), self.config).waitfordevice():
CsHelper.execute(cmd2)
dev = o.get_device()
logging.info("Bringing public interface %s up" %
o.get_device())
else:
logging.error(
"Device %s was not ready could not bring it up" % o.get_device())
# ip route add default via $gw table Table_$dev proto static
cmd = "%s -C %s" % (self.CONNTRACKD_BIN, self.CONNTRACKD_CONF)
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")
ads = [o for o in self.address.get_ips() if o.needs_vrrp()]
for o in ads:
CsPasswdSvc(o.get_gateway()).restart()
CsHelper.service("dnsmasq", "restart")
self.cl.set_master_state(True)
self.cl.save()
self.release_lock()
logging.info("Router switched to master mode")
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_gateway_cidr(), o.get_broadcast(), o.get_device())
lines.append(str)
self.check_is_up(o.get_device())
return lines
def check_is_up(self, device):
""" Ensure device is up """
cmd = "ip link show %s | grep 'state DOWN'" % device
for i in CsHelper.execute(cmd):
if " DOWN " in i:
cmd2 = "ip link set %s up" % device
CsHelper.execute(cmd2)