Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F25157583
D4033.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D4033.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 07:34 (14 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3587847
Default Alt Text
D4033.diff (6 KB)
Attached To
Mode
D4033: Attach OVH VIP to the CARP MASTER MAC
Attached
Detach File
Event Timeline
Log In to Comment