Charmed-Kubernetes/keepalived/reactive/keepalived.py

129 lines
3.6 KiB
Python

import os
import re
from subprocess import check_output
from charms.reactive import set_flag, when, when_not, hook
from charms.reactive.flags import clear_flag
from charmhelpers.core.templating import render
from charmhelpers.fetch import apt_update, apt_install
from charmhelpers.core.hookenv import config, is_leader
from charmhelpers.core.host import service_restart
from charmhelpers.core.host import service_pause, service_resume
from charms.layer import status
SYSCTL_FILE = os.path.join(os.sep, "etc", "sysctl.d", "50-keepalived.conf")
KEEPALIVED_CONFIG_FILE = os.path.join(os.sep, "etc", "keepalived", "keepalived.conf")
@when_not("keepalived.package.installed")
def install_keepalived_package():
"""Install keepalived package"""
status.maintenance("Installing keepalived")
apt_update(fatal=True)
apt_install(["keepalived", "net-tools"], fatal=True)
set_flag("keepalived.package.installed")
def default_route_interface():
"""Returns the network interface of the system's default route"""
cmd = ["route"]
output = check_output(cmd).decode("utf8")
for line in output.split("\n"):
if "default" in line:
default_interface = line.split(" ")[-1]
return default_interface
@when("keepalived.package.installed")
@when_not("keepalived.started")
@when_not("upgrade.series.in-progress")
def configure_keepalived_service():
"""Set up the keepalived service"""
virtual_ip = config().get("virtual_ip")
if virtual_ip == "":
status.blocked("Please configure virtual ips")
return
network_interface = config().get("network_interface")
if network_interface == "":
network_interface = default_route_interface()
context = {
"is_leader": is_leader(),
"virtual_ip": virtual_ip,
"network_interface": network_interface,
"router_id": config().get("router_id"),
"service_port": config().get("port"),
"healthcheck_interval": config().get("healthcheck_interval"),
}
render(
source="keepalived.conf",
target=KEEPALIVED_CONFIG_FILE,
context=context,
perms=0o644,
)
service_restart("keepalived")
render(
source="50-keepalived.conf",
target=SYSCTL_FILE,
context={"sysctl": {"net.ipv4.ip_nonlocal_bind": 1}},
perms=0o644,
)
service_restart("procps")
status.active("VIP ready")
set_flag("keepalived.started")
@when("config.changed")
def reconfigure():
clear_flag("keepalived.started")
@when("website.available", "keepalived.started")
def website_available(website):
ipaddr = re.split("/", config()["virtual_ip"])[0]
vip_hostname = config()["vip_hostname"]
hostname = vip_hostname if vip_hostname else ipaddr
# a port to export over a relation
# TODO: this could be more tightly coupled with the actual
# service via a relation
port = config()["port"]
website.configure(port=port, private_address=ipaddr, hostname=hostname)
@when("loadbalancer.available", "keepalived.started")
def loadbalancer_available(loadbalancer):
"""Send the virtual IP"""
ipaddr = re.split("/", config()["virtual_ip"])[0]
port = config()["port"]
loadbalancer.set_address_port(ipaddr, port)
@hook("upgrade-charm")
def upgrade_charm():
clear_flag("keepalived.started")
@hook("pre-series-upgrade")
def pre_series_upgrade():
service_pause("keepalived")
service_pause("procps")
status.blocked("Series upgrade in progress")
@hook("post-series-upgrade")
def post_series_upgrade():
service_resume("keepalived")
service_resume("procps")
clear_flag("keepalived.package.installed")
clear_flag("keepalived.started")