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