]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/cephadm/services/nfs.py
e5773bfb1942304b7b8661d1daa17035450efb27
[ceph.git] / ceph / src / pybind / mgr / cephadm / services / nfs.py
1 import errno
2 import logging
3 from typing import Dict, Tuple, Any, List, cast, Optional
4
5 from mgr_module import HandleCommandResult
6
7 from ceph.deployment.service_spec import NFSServiceSpec
8 import rados
9
10 from orchestrator import DaemonDescription
11
12 from cephadm.services.cephadmservice import AuthEntity, CephadmDaemonDeploySpec, CephService
13
14 logger = logging.getLogger(__name__)
15
16
17 class NFSService(CephService):
18 TYPE = 'nfs'
19
20 def config(self, spec: NFSServiceSpec, daemon_id: str) -> None: # type: ignore
21 assert self.TYPE == spec.service_type
22 assert spec.pool
23 self.mgr._check_pool_exists(spec.pool, spec.service_name())
24
25 def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonDeploySpec:
26 assert self.TYPE == daemon_spec.daemon_type
27 daemon_spec.final_config, daemon_spec.deps = self.generate_config(daemon_spec)
28 return daemon_spec
29
30 def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
31 assert self.TYPE == daemon_spec.daemon_type
32
33 daemon_type = daemon_spec.daemon_type
34 daemon_id = daemon_spec.daemon_id
35 host = daemon_spec.host
36 spec = cast(NFSServiceSpec, self.mgr.spec_store[daemon_spec.service_name].spec)
37
38 deps: List[str] = []
39
40 # create the RADOS recovery pool keyring
41 rados_user = f'{daemon_type}.{daemon_id}'
42 rados_keyring = self.create_keyring(daemon_spec)
43
44 # create the rados config object
45 self.create_rados_config_obj(spec)
46
47 # create the RGW keyring
48 rgw_user = f'{rados_user}-rgw'
49 rgw_keyring = self.create_rgw_keyring(daemon_spec)
50
51 # generate the ganesha config
52 def get_ganesha_conf() -> str:
53 context = dict(user=rados_user,
54 nodeid=daemon_spec.name(),
55 pool=spec.pool,
56 namespace=spec.namespace if spec.namespace else '',
57 rgw_user=rgw_user,
58 url=spec.rados_config_location())
59 return self.mgr.template.render('services/nfs/ganesha.conf.j2', context)
60
61 # generate the cephadm config json
62 def get_cephadm_config() -> Dict[str, Any]:
63 config: Dict[str, Any] = {}
64 config['pool'] = spec.pool
65 if spec.namespace:
66 config['namespace'] = spec.namespace
67 config['userid'] = rados_user
68 config['extra_args'] = ['-N', 'NIV_EVENT']
69 config['files'] = {
70 'ganesha.conf': get_ganesha_conf(),
71 }
72 config.update(
73 self.get_config_and_keyring(
74 daemon_type, daemon_id,
75 keyring=rados_keyring,
76 host=host
77 )
78 )
79 config['rgw'] = {
80 'cluster': 'ceph',
81 'user': rgw_user,
82 'keyring': rgw_keyring,
83 }
84 logger.debug('Generated cephadm config-json: %s' % config)
85 return config
86
87 return get_cephadm_config(), deps
88
89 def create_rados_config_obj(self,
90 spec: NFSServiceSpec,
91 clobber: bool = False) -> None:
92 with self.mgr.rados.open_ioctx(spec.pool) as ioctx:
93 if spec.namespace:
94 ioctx.set_namespace(spec.namespace)
95
96 obj = spec.rados_config_name()
97 exists = True
98 try:
99 ioctx.stat(obj)
100 except rados.ObjectNotFound:
101 exists = False
102
103 if exists and not clobber:
104 # Assume an existing config
105 logger.info('Rados config object exists: %s' % obj)
106 else:
107 # Create an empty config object
108 logger.info('Creating rados config object: %s' % obj)
109 ioctx.write_full(obj, ''.encode('utf-8'))
110
111 def create_keyring(self, daemon_spec: CephadmDaemonDeploySpec) -> str:
112 daemon_id = daemon_spec.daemon_id
113 spec = cast(NFSServiceSpec, self.mgr.spec_store[daemon_spec.service_name].spec)
114 entity: AuthEntity = self.get_auth_entity(daemon_id)
115
116 osd_caps = 'allow rw pool=%s' % (spec.pool)
117 if spec.namespace:
118 osd_caps = '%s namespace=%s' % (osd_caps, spec.namespace)
119
120 logger.info('Creating key for %s' % entity)
121 keyring = self.get_keyring_with_caps(entity,
122 ['mon', 'allow r',
123 'osd', osd_caps])
124
125 return keyring
126
127 def create_rgw_keyring(self, daemon_spec: CephadmDaemonDeploySpec) -> str:
128 daemon_id = daemon_spec.daemon_id
129 entity: AuthEntity = self.get_auth_entity(f'{daemon_id}-rgw')
130
131 logger.info('Creating key for %s' % entity)
132 keyring = self.get_keyring_with_caps(entity,
133 ['mon', 'allow r',
134 'osd', 'allow rwx tag rgw *=*'])
135
136 return keyring
137
138 def remove_rgw_keyring(self, daemon: DaemonDescription) -> None:
139 assert daemon.daemon_id is not None
140 daemon_id: str = daemon.daemon_id
141 entity: AuthEntity = self.get_auth_entity(f'{daemon_id}-rgw')
142
143 logger.info(f'Removing key for {entity}')
144 ret, out, err = self.mgr.check_mon_command({
145 'prefix': 'auth rm',
146 'entity': entity,
147 })
148
149 def post_remove(self, daemon: DaemonDescription) -> None:
150 super().post_remove(daemon)
151 self.remove_rgw_keyring(daemon)
152
153 def ok_to_stop(self,
154 daemon_ids: List[str],
155 force: bool = False,
156 known: Optional[List[str]] = None) -> HandleCommandResult:
157 # if only 1 nfs, alert user (this is not passable with --force)
158 warn, warn_message = self._enough_daemons_to_stop(self.TYPE, daemon_ids, 'NFS', 1, True)
159 if warn:
160 return HandleCommandResult(-errno.EBUSY, '', warn_message)
161
162 # if reached here, there is > 1 nfs daemon.
163 if force:
164 return HandleCommandResult(0, warn_message, '')
165
166 # if reached here, > 1 nfs daemon and no force flag.
167 # Provide warning
168 warn_message = "WARNING: Removing NFS daemons can cause clients to lose connectivity. "
169 return HandleCommandResult(-errno.EBUSY, '', warn_message)