]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/cephadm/services/nfs.py
import 15.2.4
[ceph.git] / ceph / src / pybind / mgr / cephadm / services / nfs.py
1 import logging
2
3 import rados
4 from typing import Dict, Optional, Tuple, Any, List, cast
5
6 from ceph.deployment.service_spec import NFSServiceSpec
7
8 import orchestrator
9 from orchestrator import OrchestratorError
10
11 import cephadm
12 from .. import utils
13
14 from .cephadmservice import CephadmService
15 logger = logging.getLogger(__name__)
16
17
18 class NFSService(CephadmService):
19 def _generate_nfs_config(self, daemon_type, daemon_id, host):
20 # type: (str, str, str) -> Tuple[Dict[str, Any], List[str]]
21 deps = [] # type: List[str]
22
23 # find the matching NFSServiceSpec
24 # TODO: find the spec and pass via _create_daemon instead ??
25 dd = orchestrator.DaemonDescription()
26 dd.daemon_type = daemon_type
27 dd.daemon_id = daemon_id
28 dd.hostname = host
29
30 service_name = dd.service_name()
31 specs = self.mgr.spec_store.find(service_name)
32
33 if not specs:
34 raise OrchestratorError('Cannot find service spec %s' % (service_name))
35 elif len(specs) > 1:
36 raise OrchestratorError('Found multiple service specs for %s' % (service_name))
37 else:
38 # cast to keep mypy happy
39 spec = cast(NFSServiceSpec, specs[0])
40
41 nfs = NFSGanesha(self.mgr, daemon_id, spec)
42
43 # create the keyring
44 entity = nfs.get_keyring_entity()
45 keyring = nfs.get_or_create_keyring(entity=entity)
46
47 # update the caps after get-or-create, the keyring might already exist!
48 nfs.update_keyring_caps(entity=entity)
49
50 # create the rados config object
51 nfs.create_rados_config_obj()
52
53 # generate the cephadm config
54 cephadm_config = nfs.get_cephadm_config()
55 cephadm_config.update(
56 self.mgr._get_config_and_keyring(
57 daemon_type, daemon_id,
58 keyring=keyring))
59
60 return cephadm_config, deps
61
62 def config(self, spec):
63 self.mgr._check_pool_exists(spec.pool, spec.service_name())
64 logger.info('Saving service %s spec with placement %s' % (
65 spec.service_name(), spec.placement.pretty_str()))
66 self.mgr.spec_store.save(spec)
67
68 def create(self, daemon_id, host, spec):
69 logger.info('Create daemon %s on host %s with spec %s' % (
70 daemon_id, host, spec))
71 return self.mgr._create_daemon('nfs', daemon_id, host)
72
73
74 class NFSGanesha(object):
75 def __init__(self,
76 mgr,
77 daemon_id,
78 spec):
79 # type: (cephadm.CephadmOrchestrator, str, NFSServiceSpec) -> None
80 assert spec.service_id and daemon_id.startswith(spec.service_id)
81 self.mgr = mgr
82 self.daemon_id = daemon_id
83 self.spec = spec
84
85 def get_daemon_name(self):
86 # type: () -> str
87 return '%s.%s' % (self.spec.service_type, self.daemon_id)
88
89 def get_rados_user(self):
90 # type: () -> str
91 return '%s.%s' % (self.spec.service_type, self.daemon_id)
92
93 def get_keyring_entity(self):
94 # type: () -> str
95 return utils.name_to_config_section(self.get_rados_user())
96
97 def get_or_create_keyring(self, entity=None):
98 # type: (Optional[str]) -> str
99 if not entity:
100 entity = self.get_keyring_entity()
101
102 logger.info('Create keyring: %s' % entity)
103 ret, keyring, err = self.mgr.mon_command({
104 'prefix': 'auth get-or-create',
105 'entity': entity,
106 })
107
108 if ret != 0:
109 raise OrchestratorError(
110 'Unable to create keyring %s: %s %s' \
111 % (entity, ret, err))
112 return keyring
113
114 def update_keyring_caps(self, entity=None):
115 # type: (Optional[str]) -> None
116 if not entity:
117 entity = self.get_keyring_entity()
118
119 osd_caps='allow rw pool=%s' % (self.spec.pool)
120 if self.spec.namespace:
121 osd_caps='%s namespace=%s' % (osd_caps, self.spec.namespace)
122
123 logger.info('Updating keyring caps: %s' % entity)
124 ret, out, err = self.mgr.mon_command({
125 'prefix': 'auth caps',
126 'entity': entity,
127 'caps': ['mon', 'allow r',
128 'osd', osd_caps],
129 })
130
131 if ret != 0:
132 raise OrchestratorError(
133 'Unable to update keyring caps %s: %s %s' \
134 % (entity, ret, err))
135
136 def create_rados_config_obj(self, clobber=False):
137 # type: (Optional[bool]) -> None
138 obj = self.spec.rados_config_name()
139
140 with self.mgr.rados.open_ioctx(self.spec.pool) as ioctx:
141 if self.spec.namespace:
142 ioctx.set_namespace(self.spec.namespace)
143
144 exists = True
145 try:
146 ioctx.stat(obj)
147 except rados.ObjectNotFound as e:
148 exists = False
149
150 if exists and not clobber:
151 # Assume an existing config
152 logger.info('Rados config object exists: %s' % obj)
153 else:
154 # Create an empty config object
155 logger.info('Creating rados config object: %s' % obj)
156 ioctx.write_full(obj, ''.encode('utf-8'))
157
158 def get_ganesha_conf(self):
159 # type: () -> str
160 context = dict(user=self.get_rados_user(),
161 nodeid=self.get_daemon_name(),
162 pool=self.spec.pool,
163 namespace=self.spec.namespace if self.spec.namespace else '',
164 url=self.spec.rados_config_location())
165 return self.mgr.template.render('services/nfs/ganesha.conf.j2', context)
166
167 def get_cephadm_config(self):
168 # type: () -> Dict
169 config = {'pool' : self.spec.pool} # type: Dict
170 if self.spec.namespace:
171 config['namespace'] = self.spec.namespace
172 config['userid'] = self.get_rados_user()
173 config['extra_args'] = ['-N', 'NIV_EVENT']
174 config['files'] = {
175 'ganesha.conf' : self.get_ganesha_conf(),
176 }
177 logger.debug('Generated cephadm config-json: %s' % config)
178 return config