Add preferred info to ceph nfs export locations

Change the Ceph NFS helpers to update the export locations
preferred field based on the configuration. The preferred export
location will always relate to the Ceph ADM deployed Ganesha
cluster.

Partial-Bug: #2035137
Change-Id: I9b05a6444b8ac98f79f297fec9c74a45ef11429d
This commit is contained in:
silvacarloss 2023-03-24 15:02:21 -03:00
parent 8fa97dfb7b
commit 3eb34c3ce7
3 changed files with 141 additions and 12 deletions

View File

@ -996,19 +996,22 @@ class NFSProtocolHelperMixin():
# `cephfs_ganesha_server_ip` wasn't possibly set and the used # `cephfs_ganesha_server_ip` wasn't possibly set and the used
# address is the hostname # address is the hostname
try: try:
server_address = driver_helpers.escaped_address(export_ip) server_address = driver_helpers.escaped_address(
export_ip['ip'])
except ValueError: except ValueError:
server_address = export_ip server_address = export_ip['ip']
export_path = "{server_address}:{mount_path}".format( export_path = "{server_address}:{mount_path}".format(
server_address=server_address, mount_path=subvolume_path) server_address=server_address, mount_path=subvolume_path)
LOG.info("Calculated export path for share %(id)s: %(epath)s", LOG.info("Calculated export path for share %(id)s: %(epath)s",
{"id": share['id'], "epath": export_path}) {"id": share['id'], "epath": export_path})
export_location = { export_location = {
'path': export_path, 'path': export_path,
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': export_ip['preferred']
} }
export_locations.append(export_location) export_locations.append(export_location)
return export_locations return export_locations
@ -1039,7 +1042,7 @@ class NFSProtocolHelperMixin():
for export_ip in self.export_ips: for export_ip in self.export_ips:
self.configured_ip_versions.add( self.configured_ip_versions.add(
ipaddress.ip_address(str(export_ip)).version) ipaddress.ip_address(str(export_ip['ip'])).version)
except Exception: except Exception:
# export_ips contained a hostname, safest thing is to # export_ips contained a hostname, safest thing is to
# claim support for IPv4 and IPv6 address families # claim support for IPv4 and IPv6 address families
@ -1145,9 +1148,13 @@ class NFSProtocolHelper(NFSProtocolHelperMixin, ganesha.GaneshaNASHelper2):
rados_command(self.rados_client, "fs subvolume deauthorize", argdict) rados_command(self.rados_client, "fs subvolume deauthorize", argdict)
def _get_export_ips(self): def _get_export_ips(self):
export_ips = self.config.cephfs_ganesha_export_ips ganesha_export_ips = self.config.cephfs_ganesha_export_ips
if not export_ips: if not ganesha_export_ips:
export_ips = [self.ganesha_host] ganesha_export_ips = [self.ganesha_host]
export_ips = []
for ip in ganesha_export_ips:
export_ips.append({'ip': ip, 'preferred': False})
return export_ips return export_ips
@ -1186,11 +1193,22 @@ class NFSClusterProtocolHelper(NFSProtocolHelperMixin, ganesha.NASHelperBase):
return self._nfs_clusterid return self._nfs_clusterid
def _get_configured_export_ips(self):
ganesha_server_ips = (
self.configuration.safe_get('cephfs_ganesha_export_ips') or [])
if not ganesha_server_ips:
ganesha_server_ips = (
self.configuration.safe_get('cephfs_ganesha_server_ip'))
ganesha_server_ips = (
[ganesha_server_ips] if ganesha_server_ips else [])
return ganesha_server_ips
def _get_export_ips(self): def _get_export_ips(self):
"""Get NFS cluster export ips.""" """Get NFS cluster export ips."""
nfs_clusterid = self.nfs_clusterid nfs_clusterid = self.nfs_clusterid
export_ips = [] ceph_nfs_export_ips = []
ganesha_export_ips = self._get_configured_export_ips()
argdict = { argdict = {
"cluster_id": nfs_clusterid, "cluster_id": nfs_clusterid,
} }
@ -1207,17 +1225,28 @@ class NFSClusterProtocolHelper(NFSProtocolHelperMixin, ganesha.NASHelperBase):
if not vip: if not vip:
hosts = nfs_cluster_info[nfs_clusterid]["backend"] hosts = nfs_cluster_info[nfs_clusterid]["backend"]
for host in hosts: for host in hosts:
export_ips.append(host["ip"]) ceph_nfs_export_ips.append(host["ip"])
else: else:
export_ips.append(vip) ceph_nfs_export_ips.append(vip)
# there are no export IPs, there are no NFS servers we can use # there are no export IPs, there are no NFS servers we can use
if not export_ips: if not ceph_nfs_export_ips:
msg = _("There are no NFS servers available to use. " msg = _("There are no NFS servers available to use. "
"Please check the health of your Ceph cluster " "Please check the health of your Ceph cluster "
"and restart the manila share service.") "and restart the manila share service.")
raise exception.ShareBackendException(msg=msg) raise exception.ShareBackendException(msg=msg)
export_ips = []
for ip in ceph_nfs_export_ips:
export_ips.append({'ip': ip, 'preferred': True})
# It's possible for deployers to state additional
# NFS interfaces directly via manila.conf. If they do,
# these are represented as non-preferred export paths.
# This is mostly to allow NFS-Ganesha server migrations.
for ip in ganesha_export_ips:
export_ips.append({'ip': ip, 'preferred': False})
return export_ips return export_ips
def check_for_setup_error(self): def check_for_setup_error(self):

View File

@ -1070,7 +1070,8 @@ class NFSProtocolHelperTestCase(test.TestCase):
[{ [{
'path': '1.2.3.4:/foo/bar', 'path': '1.2.3.4:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {} 'metadata': {},
'preferred': False
}], ret) }], ret)
def test_get_export_locations_with_export_ips_configured(self): def test_get_export_locations_with_export_ips_configured(self):
@ -1099,16 +1100,19 @@ class NFSProtocolHelperTestCase(test.TestCase):
'path': '127.0.0.1:/foo/bar', 'path': '127.0.0.1:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': False
}, },
{ {
'path': '[fd3f:c057:1192:1::1]:/foo/bar', 'path': '[fd3f:c057:1192:1::1]:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': False
}, },
{ {
'path': '[::1]:/foo/bar', 'path': '[::1]:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': False
}, },
], ret) ], ret)
@ -1394,10 +1398,12 @@ class NFSClusterProtocolHelperTestCase(test.TestCase):
'path': '10.0.0.10:/foo/bar', 'path': '10.0.0.10:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': True
}, { }, {
'path': '10.0.0.11:/foo/bar', 'path': '10.0.0.11:/foo/bar',
'is_admin_only': False, 'is_admin_only': False,
'metadata': {}, 'metadata': {},
'preferred': True
}] }]
export_locations = ( export_locations = (
@ -1410,6 +1416,76 @@ class NFSClusterProtocolHelperTestCase(test.TestCase):
self.assertEqual(expected_export_locations, export_locations) self.assertEqual(expected_export_locations, export_locations)
@ddt.data(
('cephfs_ganesha_server_ip', '10.0.0.1'),
('cephfs_ganesha_export_ips', ['10.0.0.2, 10.0.0.3'])
)
@ddt.unpack
def test_get_export_locations_ganesha_still_configured(self, opt, val):
cluster_info_prefix = "nfs cluster info"
nfs_clusterid = self._nfscluster_protocol_helper.nfs_clusterid
self.fake_conf.set_default(opt, val)
cluster_info_dict = {
"cluster_id": nfs_clusterid,
}
cluster_info = {"fs-manila": {
"virtual_ip": None,
"backend": [
{"hostname": "fake-ceph-node-1",
"ip": "10.0.0.10",
"port": "1010"},
{"hostname": "fake-ceph-node-2",
"ip": "10.0.0.11",
"port": "1011"}
]
}}
driver.rados_command.return_value = json.dumps(cluster_info)
fake_cephfs_subvolume_path = "/foo/bar"
expected_export_locations = [{
'path': '10.0.0.10:/foo/bar',
'is_admin_only': False,
'metadata': {},
'preferred': True
}, {
'path': '10.0.0.11:/foo/bar',
'is_admin_only': False,
'metadata': {},
'preferred': True
}]
if isinstance(val, list):
for ip in val:
expected_export_locations.append(
{
'path': f'{ip}:/foo/bar',
'is_admin_only': False,
'metadata': {},
'preferred': False
}
)
else:
expected_export_locations.append(
{
'path': f'{val}:/foo/bar',
'is_admin_only': False,
'metadata': {},
'preferred': False
}
)
export_locations = (
self._nfscluster_protocol_helper.get_export_locations(
self._share, fake_cephfs_subvolume_path))
driver.rados_command.assert_called_once_with(
self._rados_client,
cluster_info_prefix, cluster_info_dict)
self.assertEqual(expected_export_locations, export_locations)
@ddt.ddt @ddt.ddt
class CephFSDriverAltConfigTestCase(test.TestCase): class CephFSDriverAltConfigTestCase(test.TestCase):

View File

@ -0,0 +1,24 @@
---
features:
- |
It is now possible to configure `cephfs_ganesha_export_ips` (or
alternatively, `cephfs_ganesha_server_ip`) alongside
`cephfs_nfs_cluster_id`. Setting these options will allow the CephFS driver
to report additional export paths. These additional export paths will have
the "preferred" metadata key set to False. The export paths pertaining to
the NFS service host discovered by the driver will have the "preferred"
metadata key set to True. It is expected that administrators will configure
additional IP addresses when preparing to migrate from a standalone
NFS-Ganesha service to a NFS service cluster setup facilitated by the Ceph
orchestration service. Eventually, when the migration has completed, these
configuration options can be removed and the corresponding share export
path records will be dropped from Manila. Note that the CephFS driver will
not create or manipulate access rules within the NFS service configured via
`cephfs_ganesha_export_ips` or `cephfs_ganesha_server_ip`.
upgrades:
- |
In order to assist the user experience when migrating from a standalone
CephFS NFS (NFS-Ganesha) service to an NFS service created with the
Ceph Orchestrator, the CephFS driver allows configuring
`cephfs_ganesha_export_ips` (or alternatively, `cephfs_ganesha_server_ip`)
alongside `cephfs_nfs_cluster_id`.