From 57f0075acd83e2ba00da7270b1062db4263de1e8 Mon Sep 17 00:00:00 2001
From: "Peter Klein (Let's make sense GmbH)"
Date: Sun, 26 Nov 2017 19:03:04 +0100
Subject: [PATCH 1/5] Add monitor failover function
---
hetzner/failover.py | 28 ++++++++++++++++++++++++++++
hetznerctl | 30 +++++++++++++++---------------
2 files changed, 43 insertions(+), 15 deletions(-)
diff --git a/hetzner/failover.py b/hetzner/failover.py
index 2ebdd7f..64ce39b 100644
--- a/hetzner/failover.py
+++ b/hetzner/failover.py
@@ -1,3 +1,6 @@
+import re
+import subprocess
+
from hetzner import RobotError
__all__ = ['Failover', 'FailoverManager']
@@ -58,3 +61,28 @@ 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 = []
+ ips = self._get_active_ips()
+ print(ips)
+ 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
+
diff --git a/hetznerctl b/hetznerctl
index 77fb109..20bf09d 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,25 @@ 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()
+ for msg in msgs.values():
+ self.putline(str(msg))
class Admin(SubCommand):
From ed7cb7727afe75b2a6475613945661000704d1e3 Mon Sep 17 00:00:00 2001
From: "Peter Klein (Let's make sense GmbH)"
Date: Sun, 26 Nov 2017 23:32:09 +0100
Subject: [PATCH 2/5] Add monitor failover function
---
hetzner/failover.py | 24 +++++++++++++++++++++---
hetznerctl | 5 +++--
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/hetzner/failover.py b/hetzner/failover.py
index 64ce39b..48c2be5 100644
--- a/hetzner/failover.py
+++ b/hetzner/failover.py
@@ -62,14 +62,22 @@ def set(self, ip, new_destination):
% 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 = []
- ips = self._get_active_ips()
- print(ips)
+ 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):
@@ -86,3 +94,13 @@ def _get_active_ips(self):
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 20bf09d..4f4467a 100755
--- a/hetznerctl
+++ b/hetznerctl
@@ -260,8 +260,9 @@ class Failover(SubCommand):
msgs.extend(robot.failover.monitor())
else:
msgs = robot.failover.list()
- for msg in msgs.values():
- self.putline(str(msg))
+ if len(msgs) > 0:
+ for msg in msgs.values():
+ self.putline(str(msg))
class Admin(SubCommand):
From 38d6f29b055688742fabc08cf88b05e389a427c3 Mon Sep 17 00:00:00 2001
From: "Peter Klein (Let's make sense GmbH)"
Date: Mon, 27 Nov 2017 11:45:55 +0100
Subject: [PATCH 3/5] Corrected: messages are list not dictionary
---
hetznerctl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hetznerctl b/hetznerctl
index 4f4467a..3d94728 100755
--- a/hetznerctl
+++ b/hetznerctl
@@ -261,7 +261,7 @@ class Failover(SubCommand):
else:
msgs = robot.failover.list()
if len(msgs) > 0:
- for msg in msgs.values():
+ for msg in msgs:
self.putline(str(msg))
From 57811bb72614b99346fae5a2425478400cff6bdd Mon Sep 17 00:00:00 2001
From: "Peter Klein (Let's make sense GmbH)"
Date: Mon, 27 Nov 2017 11:55:07 +0100
Subject: [PATCH 4/5] Set complete failover information as output message
---
hetznerctl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hetznerctl b/hetznerctl
index 3d94728..711c555 100755
--- a/hetznerctl
+++ b/hetznerctl
@@ -259,7 +259,7 @@ class Failover(SubCommand):
elif args.monitorfailover:
msgs.extend(robot.failover.monitor())
else:
- msgs = robot.failover.list()
+ msgs = robot.failover.list().values()
if len(msgs) > 0:
for msg in msgs:
self.putline(str(msg))
From 9ad242607d279910d465a0096b85c2ef0020ff5a Mon Sep 17 00:00:00 2001
From: Peter Klein
Date: Mon, 11 May 2020 11:16:37 +0200
Subject: [PATCH 5/5] Accept ip list as new destination
---
.gitignore | 1 +
hetzner/failover.py | 7 ++++---
2 files changed, 5 insertions(+), 3 deletions(-)
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 48c2be5..0ae1115 100644
--- a/hetzner/failover.py
+++ b/hetzner/failover.py
@@ -48,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"