Page MenuHomeDevCentral

D4033.id10542.diff
No OneTemporary

D4033.id10542.diff

diff --git a/roles/router/carp/files/carp-ovh-switch.sh b/roles/router/carp/files/carp-ovh-switch.sh
new file mode 100644
--- /dev/null
+++ b/roles/router/carp/files/carp-ovh-switch.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# -------------------------------------------------------------
+# Network — CARP OVH failover script bash
+# -------------------------------------------------------------
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/router/carp/files/carp-ovh-switch.sh
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+SUBSYSTEM="$1" # first argument : interface
+STATE="$2" # second argument : MASTER or BACKUP
+
+IFACE=$(echo "$SUBSYSTEM" | cut -d@ -f2)
+
+logger "CARP event: interface=$IFACE state=$STATE"
+
+/usr/local/bin/python3 /usr/local/scripts/carp/carp-ovh.py "$IFACE" "$STATE"
diff --git a/roles/router/carp/files/carp-ovh.py b/roles/router/carp/files/carp-ovh.py
new file mode 100644
--- /dev/null
+++ b/roles/router/carp/files/carp-ovh.py
@@ -0,0 +1,188 @@
+#!/usr/local/bin/python3
+
+# -------------------------------------------------------------
+# Network — CARP OVH failover script python
+# -------------------------------------------------------------
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/router/carp/files/carp-ovh.py
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+import ovh
+import secretsmith
+from secretsmith.vault import secrets
+import sys
+import time
+import subprocess
+import socket
+
+# ---------------- CONFIG ----------------
+
+SERVICE = "ns3173530.ip-51-210-99.eu"
+
+VIP = "51.68.252.230"
+
+ALL_MACS = [
+ "00:50:56:09:3c:f2", # router-002
+ "00:50:56:09:98:fc", # router-003
+]
+
+VM_NAME = socket.gethostname()
+
+VAULT_CONFIG = "/usr/local/etc/secrets/carp-secretsmith.yaml"
+
+
+# ---------------- ARGUMENTS ----------------
+
+iface = sys.argv[1] # interface
+state = sys.argv[2] # BACKUP or MASTER
+
+# Only act when the router becomes MASTER.
+# We don't want to modify the OVH configuration when the node is BACKUP,
+# otherwise both routers could try to assign the VIP at the same time.
+if state != "MASTER":
+ print("Not MASTER → exit")
+ exit(0)
+
+
+# ---------------- VAULT ----------------
+
+vault_client = secretsmith.login(config_path=VAULT_CONFIG)
+
+secret = secrets.read_secret(vault_client, "apps", "network/carp-hyper-001-switch")
+
+
+# ---------------- OVH ----------------
+
+client = ovh.Client(
+ endpoint="ovh-eu",
+ application_key=secret["application_key"],
+ application_secret=secret["application_secret"],
+ consumer_key=secret["consumer_key"],
+)
+
+
+# ---------------- FUNCTIONS --------------
+
+
+def log(message):
+ """
+ Log a message both to the system logs.
+
+ This is useful because the script is triggered by devd,
+ so messages need to be visible in /var/log/messages for debugging.
+ """
+ subprocess.run(["logger", "-t", "carp-ovh", message])
+
+
+def get_my_mac(interface):
+ """
+ Retrieve the MAC address of the interface passed by devd.
+ """
+ output = subprocess.check_output(["ifconfig", interface]).decode()
+
+ for line in output.splitlines():
+ if "ether" in line:
+ mac = line.split()[1]
+ log(f"Detected MAC on {interface}: {mac}")
+ return mac
+
+ raise Exception(f"MAC address not found on interface {interface}")
+
+
+def get_ips(mac):
+ """
+ Get the list of IPs associated with a MAC via the OVH API.
+ Used to check if the VIP is already assigned.
+ """
+ url = f"/dedicated/server/{SERVICE}/virtualMac/{mac}/virtualAddress"
+
+ log(f"Checking IPs for MAC {mac}")
+ log(f"URL used: {url}")
+
+ try:
+ result = client.get(url)
+ log(f"OVH returned: {result}")
+ return result
+ except Exception as e:
+ log(f"Error in get_ips for {mac}: {repr(e)}")
+ raise
+
+
+def delete_vip(mac):
+ """
+ Delete the VIP from a given MAC address on OVH.
+ """
+ log(f"Deleting VIP from {mac}")
+
+ try:
+ client.delete(
+ f"/dedicated/server/{SERVICE}/virtualMac/{mac}/virtualAddress/{VIP}"
+ )
+ log("Success (HTTP 200 OK)")
+
+ except Exception as e:
+ log(f"Error while deleting VIP: {e}")
+
+
+def add_vip(mac):
+ """
+ Add the VIP to a given MAC address on OVH.
+ """
+ log(f"Adding VIP to {mac}")
+ client.post(
+ f"/dedicated/server/{SERVICE}/virtualMac/{mac}/virtualAddress",
+ ipAddress=VIP,
+ virtualMachineName=VM_NAME,
+ )
+
+
+# ---------------- LOGIC ----------------
+
+MY_MAC = get_my_mac(iface)
+
+OTHER_MAC = [mac for mac in ALL_MACS if mac != MY_MAC][0]
+
+log("Checking current state...")
+
+# Step 0: already correct?
+if VIP in get_ips(MY_MAC):
+ log("VIP is already on correct MAC -> nothing to do")
+ exit(0)
+
+# Step 1: delete old mapping if VIP is on the other MAC
+if VIP in get_ips(OTHER_MAC):
+ delete_vip(OTHER_MAC)
+
+ # Step 2: wait until deletion is effective (exponential backoff)
+ delay = 1
+ deleted = False
+
+ for i in range(5):
+ log(f"Waiting deletion... attempt {i+1}/5 (sleep {delay}s)")
+ time.sleep(delay)
+
+ ips = get_ips(OTHER_MAC)
+
+ if VIP not in ips:
+ log(f"Deletion confirmed: VIP removed from {OTHER_MAC}")
+ deleted = True
+ break
+
+ delay *= 2
+
+ if not deleted:
+ log(f"WARNING: VIP still present on {OTHER_MAC} after retries")
+
+# Step 3: add VIP on this router
+log("Proceeding to add VIP on this router")
+add_vip(MY_MAC)
+
+log("Script finished successfully")
diff --git a/roles/router/carp/init.sls b/roles/router/carp/init.sls
--- a/roles/router/carp/init.sls
+++ b/roles/router/carp/init.sls
@@ -40,3 +40,15 @@
vault:
approle: {{ salt["credentials.read_secret"]("network/router/vault") }}
addr: {{ pillar["nasqueron_services"]["vault_url"] }}
+
+/usr/local/scripts/carp/carp-ovh.py:
+ file.managed:
+ - source: salt://roles/router/carp/files/carp-ovh.py
+ - makedirs: True
+ - mode: '0755'
+
+/usr/local/scripts/carp/carp-ovh-switch.sh:
+ file.managed:
+ - source: salt://roles/router/carp/files/carp-ovh-switch.sh
+ - makedirs: True
+ - mode: '0755'

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 3, 07:07 (17 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3587847
Default Alt Text
D4033.id10542.diff (6 KB)

Event Timeline