Add action to support nfs share ghosting.
This commit is contained in:
parent
4a8a0a5025
commit
72429cac85
8
src/actions.yaml
Normal file
8
src/actions.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
ghost-share:
|
||||
description: "Bind mount NFS share 'host' for secondary Trilio Vault deployment"
|
||||
properties:
|
||||
nfs-shares:
|
||||
type: string
|
||||
description: Exact nfs-shares configuration option from secondary deployment
|
||||
required:
|
||||
- nfs-shares
|
64
src/actions/actions.py
Executable file
64
src/actions/actions.py
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/local/sbin/charm-env python3
|
||||
# Copyright 2018,2020 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Load modules from $CHARM_DIR/lib
|
||||
sys.path.append("lib")
|
||||
|
||||
from charms.layer import basic
|
||||
|
||||
basic.bootstrap_charm_deps()
|
||||
basic.init_config_states()
|
||||
|
||||
import charmhelpers.core.hookenv as hookenv
|
||||
|
||||
import charms_openstack.charm
|
||||
|
||||
# import the trilio_wlm module to get the charm definitions created.
|
||||
import charm.openstack.trilio_dm # noqa
|
||||
|
||||
|
||||
def ghost_share(*args):
|
||||
"""Ghost mount secondard TV deployment nfs-share
|
||||
"""
|
||||
secondary_nfs_share = hookenv.function_get("nfs-shares")
|
||||
with charms_openstack.charm.provide_charm_instance() as trilio_wlm_charm:
|
||||
trilio_wlm_charm.ghost_nfs_share(secondary_nfs_share)
|
||||
|
||||
|
||||
# Actions to function mapping, to allow for illegal python action names that
|
||||
# can map to a python function.
|
||||
ACTIONS = {
|
||||
"ghost-share": ghost_share,
|
||||
}
|
||||
|
||||
|
||||
def main(args):
|
||||
action_name = os.path.basename(args[0])
|
||||
try:
|
||||
action = ACTIONS[action_name]
|
||||
except KeyError:
|
||||
return "Action %s undefined" % action_name
|
||||
else:
|
||||
try:
|
||||
action(args)
|
||||
except Exception as e:
|
||||
hookenv.function_fail(str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
1
src/actions/ghost-share
Symbolic link
1
src/actions/ghost-share
Symbolic link
@ -0,0 +1 @@
|
||||
actions.py
|
@ -14,8 +14,10 @@
|
||||
|
||||
import base64
|
||||
import collections
|
||||
import os
|
||||
|
||||
import charmhelpers.core.hookenv as hookenv
|
||||
import charmhelpers.core.host as host
|
||||
import charmhelpers.fetch as fetch
|
||||
|
||||
import charms_openstack.charm
|
||||
@ -23,6 +25,19 @@ import charms_openstack.adapters as os_adapters
|
||||
|
||||
|
||||
VALID_BACKUP_TARGETS = ["nfs"]
|
||||
TV_MOUNTS = "/var/triliovault-mounts"
|
||||
|
||||
|
||||
class NFSShareNotMountedException(Exception):
|
||||
"""Signal that the trilio nfs share is not mount"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GhostShareAlreadyMountedException(Exception):
|
||||
"""Signal that a ghost share is already mounted"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TrilioDataMoverCharm(charms_openstack.charm.OpenStackCharm):
|
||||
@ -77,10 +92,45 @@ class TrilioDataMoverCharm(charms_openstack.charm.OpenStackCharm):
|
||||
def restart_map(self):
|
||||
return {self.data_mover_conf: self.services}
|
||||
|
||||
def install(self):
|
||||
self.configure_source()
|
||||
super().install()
|
||||
|
||||
def _encode_endpoint(self, backup_endpoint):
|
||||
"""base64 encode an backup endpoint for cross mounting support"""
|
||||
return base64.b64encode(backup_endpoint.encode()).decode()
|
||||
|
||||
def install(self):
|
||||
self.configure_source()
|
||||
super().install()
|
||||
# TODO: refactor into a layer/module
|
||||
def ghost_nfs_share(self, ghost_share):
|
||||
"""Bind mount the local units nfs share to another sites location
|
||||
|
||||
:param ghost_share: NFS share URL to ghost
|
||||
:type ghost_share: str
|
||||
"""
|
||||
nfs_share_path = os.path.join(
|
||||
TV_MOUNTS, self._encode_endpoint(hookenv.config("nfs-shares"))
|
||||
)
|
||||
ghost_share_path = os.path.join(
|
||||
TV_MOUNTS, self._encode_endpoint(ghost_share)
|
||||
)
|
||||
|
||||
current_mounts = [mount[0] for mount in host.mounts()]
|
||||
|
||||
if nfs_share_path not in current_mounts:
|
||||
# Trilio has not mounted the NFS share so return
|
||||
raise NFSShareNotMountedException(
|
||||
"nfs-shares ({}) not mounted".format(
|
||||
hookenv.config("nfs-shares")
|
||||
)
|
||||
)
|
||||
|
||||
if ghost_share_path in current_mounts:
|
||||
# bind mount already setup so return
|
||||
raise GhostShareAlreadyMountedException(
|
||||
"ghost mountpoint ({}) already bound".format(ghost_share_path)
|
||||
)
|
||||
|
||||
if not os.path.exists(ghost_share_path):
|
||||
os.mkdir(ghost_share_path)
|
||||
|
||||
host.mount(nfs_share_path, ghost_share_path, options="bind")
|
||||
|
5
tox.ini
5
tox.ini
@ -50,6 +50,11 @@ basepython = python3.7
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py38]
|
||||
basepython = python3.8
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
|
@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
import charm.openstack.trilio_dm as trilio_dm
|
||||
import charms_openstack.test_utils as test_utils
|
||||
@ -51,3 +52,61 @@ class TestTrilioDataMoverCharms(Helper):
|
||||
self.assertEqual(
|
||||
dm_charm.services, ["tvault-contego", "tvault-object-store"]
|
||||
)
|
||||
|
||||
|
||||
# TODO: refactor into a layer/module
|
||||
class TestTrilioDataMoverCharmGhostShareAction(Helper):
|
||||
|
||||
_nfs_shares = "10.20.30.40:/srv/trilioshare"
|
||||
_ghost_shares = "50.20.30.40:/srv/trilioshare"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.patch_object(trilio_dm.hookenv, "config")
|
||||
self.patch_object(trilio_dm.host, "mounts")
|
||||
self.patch_object(trilio_dm.host, "mount")
|
||||
self.patch_object(trilio_dm.os.path, "exists")
|
||||
self.patch_object(trilio_dm.os, "mkdir")
|
||||
|
||||
self.trilio_wlm_charm = trilio_dm.TrilioDataMoverCharm()
|
||||
self._nfs_path = os.path.join(
|
||||
trilio_dm.TV_MOUNTS,
|
||||
self.trilio_wlm_charm._encode_endpoint(self._nfs_shares),
|
||||
)
|
||||
self._ghost_path = os.path.join(
|
||||
trilio_dm.TV_MOUNTS,
|
||||
self.trilio_wlm_charm._encode_endpoint(self._ghost_shares),
|
||||
)
|
||||
|
||||
def test_ghost_share(self):
|
||||
self.config.return_value = self._nfs_shares
|
||||
self.mounts.return_value = [
|
||||
["/srv/nova", "/dev/sda"],
|
||||
[self._nfs_path, self._nfs_shares],
|
||||
]
|
||||
self.exists.return_value = False
|
||||
self.trilio_wlm_charm.ghost_nfs_share(self._ghost_shares)
|
||||
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
|
||||
self.mounts.return_value = [
|
||||
["/srv/nova", "/dev/sda"],
|
||||
[self._nfs_path, self._nfs_shares],
|
||||
[self._ghost_path, self._nfs_shares],
|
||||
]
|
||||
with self.assertRaises(trilio_dm.GhostShareAlreadyMountedException):
|
||||
self.trilio_wlm_charm.ghost_nfs_share(self._ghost_shares)
|
||||
self.mount.assert_not_called()
|
||||
|
||||
def test_ghost_share_nfs_unmounted(self):
|
||||
self.config.return_value = self._nfs_shares
|
||||
self.mounts.return_value = [["/srv/nova", "/dev/sda"]]
|
||||
self.exists.return_value = False
|
||||
with self.assertRaises(trilio_dm.NFSShareNotMountedException):
|
||||
self.trilio_wlm_charm.ghost_nfs_share(self._ghost_shares)
|
||||
self.mount.assert_not_called()
|
||||
|
Loading…
x
Reference in New Issue
Block a user