trilio: Support multiple NFS share configurations

TrilioVault supports use of multiple NFS shares using a comma
separated list.  Update the ghost-nfs-share action code to
support this type of configuration.

The assumption is that the order of shares provided to the
action is the same as the order of shares in the nfs-shares
configuration option in terms of replication between sites.

Mismatched length lists will generate an exception which can
be propagated back to the operator.

Change-Id: I7d501a3e44d4e7c732e7625fa2a8cde9a34ff564
Closes-Bug: 1894817
This commit is contained in:
James Page 2020-09-08 11:49:49 +01:00
parent 7e8c5c1461
commit 4c2486660f
2 changed files with 78 additions and 21 deletions

View File

@ -44,6 +44,12 @@ class GhostShareAlreadyMountedException(Exception):
pass
class MismatchedConfigurationException(Exception):
"""Signal that nfs-shares and ghost-shares are mismatched"""
pass
def _configure_triliovault_source():
"""Configure triliovault specific package sources in addition to
any general openstack package sources (via openstack-origin)
@ -155,15 +161,37 @@ class TrilioVaultCharmGhostAction(object):
"""base64 encode an backup endpoint for cross mounting support"""
return base64.b64encode(backup_endpoint.encode()).decode()
def ghost_nfs_share(self, ghost_share):
"""Bind mount the local units nfs share to another sites location
def ghost_nfs_share(self, ghost_shares):
"""Bind mount local NFS shares to remote NFS paths
:param ghost_shares: Comma separated NFS shares URL to ghost
:type ghost_shares: str
"""
ghost_shares = ghost_shares.split(',')
nfs_shares = ch_core.hookenv.config("nfs-shares").split(',')
try:
share_mappings = [
(nfs_shares[i], ghost_shares[i])
for i in range(0, len(nfs_shares))
]
except IndexError:
raise MismatchedConfigurationException(
"ghost-shares and nfs-shares are different lengths"
)
for local_share, ghost_share in share_mappings:
self._ghost_nfs_share(local_share, ghost_share)
def _ghost_nfs_share(self, local_share, ghost_share):
"""Bind mount a local unit NFS share to another sites location
:param local_share: Local NFS share URL
:type local_share: str
:param ghost_share: NFS share URL to ghost
:type ghost_share: str
"""
nfs_share_path = os.path.join(
TV_MOUNTS,
self._encode_endpoint(ch_core.hookenv.config("nfs-shares"))
self._encode_endpoint(local_share)
)
ghost_share_path = os.path.join(
TV_MOUNTS, self._encode_endpoint(ghost_share)
@ -174,8 +202,8 @@ class TrilioVaultCharmGhostAction(object):
if nfs_share_path not in current_mounts:
# Trilio has not mounted the NFS share so return
raise NFSShareNotMountedException(
"nfs-shares ({}) not mounted".format(
ch_core.hookenv.config("nfs-shares")
"nfs-share ({}) not mounted".format(
local_share
)
)

View File

@ -1,3 +1,4 @@
import unittest.mock as mock
import os
from unit_tests.charms_openstack.charm.utils import BaseOpenStackCharmTest
@ -22,8 +23,8 @@ class TrilioVaultFoobarSubordinate(trilio.TrilioVaultSubordinateCharm):
class TestTrilioCharmGhostAction(BaseOpenStackCharmTest):
_nfs_shares = "10.20.30.40:/srv/trilioshare"
_ghost_shares = "50.20.30.40:/srv/trilioshare"
_nfs_share = "10.20.30.40:/srv/trilioshare"
_ghost_share = "50.20.30.40:/srv/trilioshare"
def setUp(self):
super().setUp(trilio.TrilioVaultCharmGhostAction, {})
@ -36,46 +37,74 @@ class TestTrilioCharmGhostAction(BaseOpenStackCharmTest):
self.trilio_charm = trilio.TrilioVaultCharmGhostAction()
self._nfs_path = os.path.join(
trilio.TV_MOUNTS,
self.trilio_charm._encode_endpoint(self._nfs_shares),
self.trilio_charm._encode_endpoint(self._nfs_share),
)
self._ghost_path = os.path.join(
trilio.TV_MOUNTS,
self.trilio_charm._encode_endpoint(self._ghost_shares),
self.trilio_charm._encode_endpoint(self._ghost_share),
)
def test_ghost_share(self):
self.config.return_value = self._nfs_shares
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_shares],
[self._nfs_path, self._nfs_share],
]
self.exists.return_value = False
self.trilio_charm.ghost_nfs_share(self._ghost_shares)
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_share_already_bound(self):
self.config.return_value = self._nfs_shares
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_shares],
[self._ghost_path, self._nfs_shares],
[self._nfs_path, self._nfs_share],
[self._ghost_path, self._nfs_share],
]
with self.assertRaises(trilio.GhostShareAlreadyMountedException):
self.trilio_charm.ghost_nfs_share(self._ghost_shares)
self.trilio_charm._ghost_nfs_share(self._nfs_share,
self._ghost_share)
self.mount.assert_not_called()
def test_ghost_share_nfs_unmounted(self):
self.config.return_value = self._nfs_shares
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._ghost_shares)
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):