← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad-layers:turnip-storage-literal-ipv6-address into launchpad-layers:main

 

Colin Watson has proposed merging ~cjwatson/launchpad-layers:turnip-storage-literal-ipv6-address into launchpad-layers:main.

Commit message:
Handle literal IPv6 addresses in turnip-storage

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-layers/+git/launchpad-layers/+merge/450692

My local turnip deployment sometimes gets itself into a state where the individual units still have IPv6 addresses but no IPv4 addresses (until I run `systemctl restart systemd-networkd.service` on each of them manually).  This has the very annoying consequence of getting the turnip charm hooks badly wedged, because they try to mount the NFS storage volume using its IPv6 address without using the correct literal address syntax.

While the units losing their IPv4 addresses is probably a bug somewhere else, it makes sense to handle mounting over IPv6 anyway, and it makes it much less difficult to recover from this situation.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad-layers:turnip-storage-literal-ipv6-address into launchpad-layers:main.
diff --git a/turnip-storage/lib/charms/turnip/storage.py b/turnip-storage/lib/charms/turnip/storage.py
index 6b3505d..6922525 100644
--- a/turnip-storage/lib/charms/turnip/storage.py
+++ b/turnip-storage/lib/charms/turnip/storage.py
@@ -1,7 +1,7 @@
 # Copyright 2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-
+import ipaddress
 import os
 import subprocess
 
@@ -26,6 +26,20 @@ def ensure_mounted():
         subprocess.check_call(["mountpoint", "-q", data_dir()])
 
 
+def make_mount_path(hostname, mountpoint):
+    """Turn a hostname (or IP address) and mount point into a mount path."""
+    literal_ipv6_address = False
+    try:
+        if ipaddress.ip_address(hostname).version == 6:
+            literal_ipv6_address = True
+    except ValueError:
+        pass
+    if literal_ipv6_address:
+        return f"[{hostname}]:{mountpoint}"
+    else:
+        return f"{hostname}:{mountpoint}"
+
+
 def mount_data(mount_info):
     # We use a systemd.mount(5) unit rather than a line in /etc/fstab partly
     # because it's easier to deal with a file we can completely overwrite,
@@ -35,6 +49,9 @@ def mount_data(mount_info):
     data_mount_conf = f"/lib/systemd/system/{data_mount}"
     context = dict(mount_info)
     context["data_dir"] = data_dir()
+    context["mount_path"] = make_mount_path(
+        mount_info["hostname"], mount_info["mountpoint"]
+    )
     templating.render("data.mount.j2", data_mount_conf, context, perms=0o644)
     host.service("unmask", data_mount)
     reload_systemd()
diff --git a/turnip-storage/templates/data.mount.j2 b/turnip-storage/templates/data.mount.j2
index b115460..77ad6eb 100644
--- a/turnip-storage/templates/data.mount.j2
+++ b/turnip-storage/templates/data.mount.j2
@@ -2,7 +2,7 @@
 Description=Turnip data file system
 
 [Mount]
-What={{ hostname }}:{{ mountpoint }}
+What={{ mount_path }}
 Where={{ data_dir }}
 Type={{ fstype }}
 Options={{ options }}