]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/nfs/cluster.py
5 from typing
import cast
, Dict
, List
, Any
, Union
, Optional
7 from ceph
.deployment
.service_spec
import NFSServiceSpec
, PlacementSpec
, IngressSpec
11 from .exception
import NFSInvalidOperation
, ClusterNotFound
12 from .utils
import POOL_NAME
, available_clusters
, restart_nfs_service
13 from .export
import NFSRados
, exception_handler
15 log
= logging
.getLogger(__name__
)
18 def resolve_ip(hostname
: str) -> str:
20 r
= socket
.getaddrinfo(hostname
, None, flags
=socket
.AI_CANONNAME
,
21 type=socket
.SOCK_STREAM
)
22 # pick first v4 IP, if present
24 if a
[0] == socket
.AF_INET
:
27 except socket
.gaierror
as e
:
28 raise NFSInvalidOperation(f
"Cannot resolve IP for host {hostname}: {e}")
31 def cluster_setter(func
):
32 def set_pool_ns_clusterid(nfs
, *args
, **kwargs
):
33 nfs
._set
_pool
_namespace
(kwargs
['cluster_id'])
34 nfs
._set
_cluster
_id
(kwargs
['cluster_id'])
35 return func(nfs
, *args
, **kwargs
)
36 return set_pool_ns_clusterid
39 def create_ganesha_pool(mgr
, pool
):
40 pool_list
= [p
['pool_name'] for p
in mgr
.get_osdmap().dump().get('pools', [])]
41 if pool
not in pool_list
:
42 mgr
.check_mon_command({'prefix': 'osd pool create', 'pool': pool
})
43 mgr
.check_mon_command({'prefix': 'osd pool application enable',
49 def __init__(self
, mgr
):
50 self
.pool_name
= POOL_NAME
54 def _set_cluster_id(self
, cluster_id
):
55 self
.cluster_id
= cluster_id
57 def _set_pool_namespace(self
, cluster_id
):
58 self
.pool_ns
= cluster_id
60 def _get_common_conf_obj_name(self
):
61 return f
'conf-nfs.{self.cluster_id}'
63 def _get_user_conf_obj_name(self
):
64 return f
'userconf-nfs.{self.cluster_id}'
66 def _call_orch_apply_nfs(self
, placement
, virtual_ip
=None):
69 # run NFS on non-standard port
70 spec
= NFSServiceSpec(service_type
='nfs', service_id
=self
.cluster_id
,
71 pool
=self
.pool_name
, namespace
=self
.pool_ns
,
72 placement
=PlacementSpec
.from_string(placement
),
73 # use non-default port so we don't conflict with ingress
75 completion
= self
.mgr
.apply_nfs(spec
)
76 orchestrator
.raise_if_exception(completion
)
77 ispec
= IngressSpec(service_type
='ingress',
78 service_id
='nfs.' + self
.cluster_id
,
79 backend_service
='nfs.' + self
.cluster_id
,
80 frontend_port
=2049, # default nfs port
82 virtual_ip
=virtual_ip
)
83 completion
= self
.mgr
.apply_ingress(ispec
)
84 orchestrator
.raise_if_exception(completion
)
87 spec
= NFSServiceSpec(service_type
='nfs', service_id
=self
.cluster_id
,
88 pool
=self
.pool_name
, namespace
=self
.pool_ns
,
89 placement
=PlacementSpec
.from_string(placement
))
90 completion
= self
.mgr
.apply_nfs(spec
)
91 orchestrator
.raise_if_exception(completion
)
93 def create_empty_rados_obj(self
):
94 common_conf
= self
._get
_common
_conf
_obj
_name
()
95 NFSRados(self
.mgr
, self
.pool_ns
).write_obj('', self
._get
_common
_conf
_obj
_name
())
96 log
.info(f
"Created empty object:{common_conf}")
98 def delete_config_obj(self
):
99 NFSRados(self
.mgr
, self
.pool_ns
).remove_all_obj()
100 log
.info(f
"Deleted {self._get_common_conf_obj_name()} object and all objects in "
104 def create_nfs_cluster(self
,
106 placement
: Optional
[str],
107 virtual_ip
: Optional
[str],
108 ingress
: Optional
[bool] = None):
110 if virtual_ip
and not ingress
:
111 raise NFSInvalidOperation('virtual_ip can only be provided with ingress enabled')
112 if not virtual_ip
and ingress
:
113 raise NFSInvalidOperation('ingress currently requires a virtual_ip')
114 invalid_str
= re
.search('[^A-Za-z0-9-_.]', cluster_id
)
116 raise NFSInvalidOperation(f
"cluster id {cluster_id} is invalid. "
117 f
"{invalid_str.group()} is char not permitted")
119 create_ganesha_pool(self
.mgr
, self
.pool_name
)
121 self
.create_empty_rados_obj()
123 if cluster_id
not in available_clusters(self
.mgr
):
124 self
._call
_orch
_apply
_nfs
(placement
, virtual_ip
)
125 return 0, "NFS Cluster Created Successfully", ""
126 return 0, "", f
"{cluster_id} cluster already exists"
127 except Exception as e
:
128 return exception_handler(e
, f
"NFS Cluster {cluster_id} could not be created")
131 def delete_nfs_cluster(self
, cluster_id
):
133 cluster_list
= available_clusters(self
.mgr
)
134 if cluster_id
in cluster_list
:
135 self
.mgr
.export_mgr
.delete_all_exports(cluster_id
)
136 completion
= self
.mgr
.remove_service('ingress.nfs.' + self
.cluster_id
)
137 orchestrator
.raise_if_exception(completion
)
138 completion
= self
.mgr
.remove_service('nfs.' + self
.cluster_id
)
139 orchestrator
.raise_if_exception(completion
)
140 self
.delete_config_obj()
141 return 0, "NFS Cluster Deleted Successfully", ""
142 return 0, "", "Cluster does not exist"
143 except Exception as e
:
144 return exception_handler(e
, f
"Failed to delete NFS Cluster {cluster_id}")
146 def list_nfs_cluster(self
):
148 return 0, '\n'.join(available_clusters(self
.mgr
)), ""
149 except Exception as e
:
150 return exception_handler(e
, "Failed to list NFS Cluster")
152 def _show_nfs_cluster_info(self
, cluster_id
: str) -> Dict
[str, Any
]:
153 self
._set
_cluster
_id
(cluster_id
)
154 completion
= self
.mgr
.list_daemons(daemon_type
='nfs')
155 orchestrator
.raise_if_exception(completion
)
156 backends
: List
[Dict
[str, Union
[str, int]]] = []
157 # Here completion.result is a list DaemonDescription objects
158 for cluster
in completion
.result
:
159 if self
.cluster_id
== cluster
.service_id():
164 c
= self
.mgr
.get_hosts()
165 orchestrator
.raise_if_exception(c
)
166 hosts
= [h
for h
in c
.result
167 if h
.hostname
== cluster
.hostname
]
169 ip
= resolve_ip(hosts
[0].addr
)
172 ip
= resolve_ip(cluster
.hostname
)
174 "hostname": cluster
.hostname
,
176 "port": cluster
.ports
[0]
178 except orchestrator
.OrchestratorError
:
181 r
: Dict
[str, Any
] = {
185 sc
= self
.mgr
.describe_service(service_type
='ingress')
186 orchestrator
.raise_if_exception(sc
)
188 spec
= cast(IngressSpec
, i
.spec
)
189 if spec
.backend_service
== f
'nfs.{cluster_id}':
190 r
['virtual_ip'] = i
.virtual_ip
.split('/')[0]
192 r
['port'] = i
.ports
[0]
194 r
['monitor_port'] = i
.ports
[1]
197 def show_nfs_cluster_info(self
, cluster_id
=None):
202 cluster_ls
= [cluster_id
]
204 cluster_ls
= available_clusters(self
.mgr
)
206 for cluster_id
in cluster_ls
:
207 res
= self
._show
_nfs
_cluster
_info
(cluster_id
)
209 info_res
[cluster_id
] = res
210 return (0, json
.dumps(info_res
, indent
=4), '')
211 except Exception as e
:
212 return exception_handler(e
, "Failed to show info for cluster")
215 def set_nfs_cluster_config(self
, cluster_id
, nfs_config
):
218 raise NFSInvalidOperation("Empty Config!!")
219 if cluster_id
in available_clusters(self
.mgr
):
220 rados_obj
= NFSRados(self
.mgr
, self
.pool_ns
)
221 if rados_obj
.check_user_config():
222 return 0, "", "NFS-Ganesha User Config already exists"
223 rados_obj
.write_obj(nfs_config
, self
._get
_user
_conf
_obj
_name
(),
224 self
._get
_common
_conf
_obj
_name
())
225 restart_nfs_service(self
.mgr
, cluster_id
)
226 return 0, "NFS-Ganesha Config Set Successfully", ""
227 raise ClusterNotFound()
228 except NotImplementedError:
229 return 0, "NFS-Ganesha Config Added Successfully "\
230 "(Manual Restart of NFS PODS required)", ""
231 except Exception as e
:
232 return exception_handler(e
, f
"Setting NFS-Ganesha Config failed for {cluster_id}")
235 def reset_nfs_cluster_config(self
, cluster_id
):
237 if cluster_id
in available_clusters(self
.mgr
):
238 rados_obj
= NFSRados(self
.mgr
, self
.pool_ns
)
239 if not rados_obj
.check_user_config():
240 return 0, "", "NFS-Ganesha User Config does not exist"
241 rados_obj
.remove_obj(self
._get
_user
_conf
_obj
_name
(),
242 self
._get
_common
_conf
_obj
_name
())
243 restart_nfs_service(self
.mgr
, cluster_id
)
244 return 0, "NFS-Ganesha Config Reset Successfully", ""
245 raise ClusterNotFound()
246 except NotImplementedError:
247 return 0, "NFS-Ganesha Config Removed Successfully "\
248 "(Manual Restart of NFS PODS required)", ""
249 except Exception as e
:
250 return exception_handler(e
, f
"Resetting NFS-Ganesha Config failed for {cluster_id}")