diff --git a/.gitignore b/.gitignore index b565d21..cf0b7fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc /build/ /result +.idea diff --git a/hetzner/failover.py b/hetzner/failover.py index 2ebdd7f..0ae1115 100644 --- a/hetzner/failover.py +++ b/hetzner/failover.py @@ -1,3 +1,6 @@ +import re +import subprocess + from hetzner import RobotError __all__ = ['Failover', 'FailoverManager'] @@ -45,12 +48,13 @@ def set(self, ip, new_destination): "Invalid IP address '%s'. Failover IP addresses are %s" % (ip, failovers.keys())) failover = failovers.get(ip) - if new_destination == failover.active_server_ip: + dest_list = new_destination.split(' ') + if failover.active_server_ip in dest_list: raise RobotError( "%s is already the active destination of failover IP %s" % (new_destination, ip)) - available_dests = [s.ip for s in list(self.servers)] - if new_destination not in available_dests: + available_dests = set([s.ip for s in list(self.servers)]) + if len(available_dests.intersection(set(dest_list))) == 0: raise RobotError( "Invalid destination '%s'. " "The destination is not in your server list: %s" @@ -58,3 +62,46 @@ def set(self, ip, new_destination): result = self.conn.post('/failover/%s' % ip, {'active_server_ip': new_destination}) return Failover(result.get('failover')) + + def monitor(self): + """Check if container with failover IP is running on host + and if IP is not mapped to host change settings + """ + msgs = [] + failovers = self.list() + if len(failovers) > 0: + ips = self._get_active_ips() + host_ip = self._get_host_ip() + for failover_ip, failover in failovers.items(): + if failover_ip in ips and failover.active_server_ip != host_ip: + new_failover = self.set(failover_ip, host_ip) + if new_failover: + msgs.append("Failover IP successfully assigned to new" + " destination") + msgs.append(str(failover)) + return msgs + + def _get_active_ips(self): + ips = [] + try: + out = subprocess.check_output(["lxc-ls", "--active", "-fF", "IPV4"]) + except subprocess.CalledProcessError as e: + raise RobotError(str(e)) + except Exception as e: + raise RobotError(str(e)) + else: + [ips.extend([ip.strip() for ip in line.strip().split(',')]) + for line in out.split('\n') + if re.search(r'\d+\.\d+\.\d+\.\d', line)] + return ips + + def _get_host_ip(self): + try: + host_ip = subprocess.check_output(["hostname", "--ip-address"]) + except subprocess.CalledProcessError as e: + raise RobotError(str(e)) + except Exception as e: + raise RobotError(str(e)) + else: + return host_ip.strip() + diff --git a/hetznerctl b/hetznerctl index 77fb109..711c555 100755 --- a/hetznerctl +++ b/hetznerctl @@ -233,6 +233,9 @@ class Failover(SubCommand): make_option('-s', '--set', dest='setfailover', action='store_true', default=False, help="Assign failover IP address to server"), + make_option('-m', '--monitor', dest='monitorfailover', + action='store_true', default=False, + help="Assign failover IP address to server"), make_option('ip', nargs='?', default=None, help="Failover IP address to assign"), make_option('destination', nargs='?', default=None, @@ -240,28 +243,26 @@ class Failover(SubCommand): ] def execute(self, robot, parser, args): + msgs = [] if args.setfailover: - errs = [] if not args.ip: - errs.append("Error: you need to set the failover IP you" + msgs.append("Error: you need to set the failover IP you" " want to assign. Option 'ip'") if not args.destination: - errs.append("Error: you need to set the new destination of" + msgs.append("Error: you need to set the new destination of" " the failover IP. Option 'dest'") - if len(errs) > 0: - for err in errs: - self.putline(err) - else: + if len(msgs) == 0: failover = robot.failover.set(args.ip, args.destination) - self.putline("Failover IP successfully assigned to new" - " destination") - self.putline(str(failover)) + msgs.append("Failover IP successfully assigned to new" + " destination") + msgs.append(failover) + elif args.monitorfailover: + msgs.extend(robot.failover.monitor()) else: - failovers = robot.failover.list() - if len(failovers) > 0: - self.putline("Found %s failover IPs" % len(failovers)) - for failover in failovers.values(): - self.putline(str(failover)) + msgs = robot.failover.list().values() + if len(msgs) > 0: + for msg in msgs: + self.putline(str(msg)) class Admin(SubCommand):