Support Trilio 4.2 Share hashes

The Trilio 4.2 changes the way that hashes are generated for nfs
mount points. The hash is now generated from the directory only,
the source IP address is ignored. This patch updates the ghost-share
action to accommodate that.

Change-Id: I64a1cc95a3a78ce79d57f5b840edb29996f04f9c
This commit is contained in:
Liam Young 2022-06-13 16:33:05 +00:00
parent d0c540a651
commit 055fb499b8
3 changed files with 140 additions and 5 deletions

View File

@ -25,6 +25,7 @@ from charms_openstack.plugins.trilio import (
TrilioVaultCharm,
TrilioVaultSubordinateCharm,
TrilioVaultCharmGhostAction,
TrilioVault42CharmGhostAction,
)
__all__ = (
@ -35,4 +36,5 @@ __all__ = (
"TrilioVaultCharm",
"TrilioVaultSubordinateCharm",
"TrilioVaultCharmGhostAction",
"TrilioVault42CharmGhostAction",
)

View File

@ -598,6 +598,20 @@ class TrilioVaultCharmGhostAction(object):
for local_share, ghost_share in share_mappings:
self._ghost_nfs_share(local_share, ghost_share)
def trilio_share_mounted(self, share_path):
"""Check if share_path is mounted
:param local_share: Local NFS share URL
:type local_share: str
:returns: Whether share is mounted
:rtype: bool
"""
_share_path = os.path.join(
TV_MOUNTS,
self._encode_endpoint(share_path))
current_mounts = [mount[0] for mount in ch_core.host.mounts()]
return _share_path in current_mounts
def _ghost_nfs_share(self, local_share, ghost_share):
"""Bind mount a local unit NFS share to another sites location
@ -614,9 +628,7 @@ class TrilioVaultCharmGhostAction(object):
TV_MOUNTS, self._encode_endpoint(ghost_share)
)
current_mounts = [mount[0] for mount in ch_core.host.mounts()]
if nfs_share_path not in current_mounts:
if not self.trilio_share_mounted(local_share):
# Trilio has not mounted the NFS share so return
raise NFSShareNotMountedException(
"nfs-share ({}) not mounted".format(
@ -624,7 +636,7 @@ class TrilioVaultCharmGhostAction(object):
)
)
if ghost_share_path in current_mounts:
if self.trilio_share_mounted(ghost_share):
# bind mount already setup so return
raise GhostShareAlreadyMountedException(
"ghost mountpoint ({}) already bound".format(ghost_share_path)
@ -634,3 +646,39 @@ class TrilioVaultCharmGhostAction(object):
os.mkdir(ghost_share_path)
ch_core.host.mount(nfs_share_path, ghost_share_path, options="bind")
class TrilioVault42CharmGhostAction(TrilioVaultCharmGhostAction):
def _encode_endpoint_uri(self, backup_endpoint):
"""base64 encode a backup uri for cross mounting support"""
return base64.b64encode(backup_endpoint.encode()).decode()
def _encode_endpoint_path(self, backup_endpoint):
"""base64 encode an backup path for cross mounting support"""
return base64.b64encode(
str.encode(urlparse(backup_endpoint).path)).decode()
def _encode_endpoint(self, backup_endpoint):
"""base64 encode an backup endpoint for cross mounting support"""
return self._encode_endpoint_path(backup_endpoint)
def trilio_share_mounted(self, share_path):
"""Check if share_path is mounted
:param local_share: Local NFS share URL
:type local_share: str
:returns: Whether share is mounted
:rtype: bool
"""
mount_paths = [
os.path.join(
TV_MOUNTS,
self._encode_endpoint_path(share_path)
),
os.path.join(
TV_MOUNTS,
self._encode_endpoint_uri(share_path)
)]
current_mounts = [mount[0] for mount in ch_core.host.mounts()]
return any(m in current_mounts for m in mount_paths)

View File

@ -30,7 +30,7 @@ class TrilioVaultFoobarSubordinate(trilio.TrilioVaultSubordinateCharm):
class TestTrilioCharmGhostAction(BaseOpenStackCharmTest):
_nfs_share = "10.20.30.40:/srv/trilioshare"
_ghost_share = "50.20.30.40:/srv/trilioshare"
_ghost_share = "50.20.30.40:/srv/trilioghostshare"
def setUp(self):
super().setUp(trilio.TrilioVaultCharmGhostAction, {})
@ -112,6 +112,91 @@ class TestTrilioCharmGhostAction(BaseOpenStackCharmTest):
)
class TestTrilioVault42CharmGhostAction(BaseOpenStackCharmTest):
_nfs_share = "10.20.30.40:/srv/trilioshare"
_ghost_share = "50.20.30.40:/srv/trilioghostshare"
def setUp(self):
super().setUp(trilio.TrilioVaultCharmGhostAction, {})
self.patch_object(trilio.ch_core.hookenv, "config")
self.patch_object(trilio.ch_core.host, "mounts")
self.patch_object(trilio.ch_core.host, "mount")
self.patch_object(trilio.os.path, "exists")
self.patch_object(trilio.os, "mkdir")
self.trilio_charm = trilio.TrilioVault42CharmGhostAction()
self._nfs_path = os.path.join(
trilio.TV_MOUNTS,
self.trilio_charm._encode_endpoint(self._nfs_share),
)
self._ghost_path = os.path.join(
trilio.TV_MOUNTS,
self.trilio_charm._encode_endpoint(self._ghost_share),
)
def test__ghost_nfs_share(self):
self.config.return_value = self._nfs_share
self.mounts.return_value = [
["/srv/nova", "/dev/sda"],
[self._nfs_path, self._nfs_share],
]
self.exists.return_value = False
self.trilio_charm._ghost_nfs_share(self._nfs_share,
self._ghost_share)
self.exists.assert_called_once_with(self._ghost_path)
self.mkdir.assert_called_once_with(self._ghost_path)
self.mount.assert_called_once_with(
self._nfs_path, self._ghost_path, options="bind"
)
def test__ghost_nfs_share_already_bound(self):
self.config.return_value = self._nfs_share
self.mounts.return_value = [
["/srv/nova", "/dev/sda"],
[self._nfs_path, self._nfs_share],
[self._ghost_path, self._nfs_share],
]
with self.assertRaises(trilio.GhostShareAlreadyMountedException):
self.trilio_charm._ghost_nfs_share(self._nfs_share,
self._ghost_share)
self.mount.assert_not_called()
def test__ghost_nfs_share_nfs_unmounted(self):
self.config.return_value = self._nfs_share
self.mounts.return_value = [["/srv/nova", "/dev/sda"]]
self.exists.return_value = False
with self.assertRaises(trilio.NFSShareNotMountedException):
self.trilio_charm._ghost_nfs_share(self._nfs_share,
self._ghost_share)
self.mount.assert_not_called()
def test_ghost_nfs_share(self):
self.patch_object(self.trilio_charm, "_ghost_nfs_share")
self.config.return_value = (
"10.20.30.40:/srv/trilioshare,10.20.30.40:/srv/trilioshare2"
)
self.trilio_charm.ghost_nfs_share(
"50.20.30.40:/srv/trilioshare,50.20.30.40:/srv/trilioshare2"
)
self._ghost_nfs_share.assert_has_calls([
mock.call("10.20.30.40:/srv/trilioshare",
"50.20.30.40:/srv/trilioshare"),
mock.call("10.20.30.40:/srv/trilioshare2",
"50.20.30.40:/srv/trilioshare2")
])
def test_ghost_nfs_share_mismatch(self):
self.patch_object(self.trilio_charm, "_ghost_nfs_share")
self.config.return_value = (
"10.20.30.40:/srv/trilioshare,10.20.30.40:/srv/trilioshare2"
)
with self.assertRaises(trilio.MismatchedConfigurationException):
self.trilio_charm.ghost_nfs_share(
"50.20.30.40:/srv/trilioshare"
)
class TestTrilioCommonBehaviours(BaseOpenStackCharmTest):
def setUp(self):