]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/nfs/export_utils.py
import ceph pacific 16.2.5
[ceph.git] / ceph / src / pybind / mgr / nfs / export_utils.py
1 class GaneshaConfParser:
2 def __init__(self, raw_config):
3 self.pos = 0
4 self.text = ""
5 self.clean_config(raw_config)
6
7 def clean_config(self, raw_config):
8 for line in raw_config.split("\n"):
9 self.text += line
10 if line.startswith("%"):
11 self.text += "\n"
12
13 def remove_whitespaces_quotes(self):
14 if self.text.startswith("%url"):
15 self.text = self.text.replace('"', "")
16 else:
17 self.text = "".join(self.text.split())
18
19 def stream(self):
20 return self.text[self.pos:]
21
22 def parse_block_name(self):
23 idx = self.stream().find('{')
24 if idx == -1:
25 raise Exception("Cannot find block name")
26 block_name = self.stream()[:idx]
27 self.pos += idx+1
28 return block_name
29
30 def parse_block_or_section(self):
31 if self.stream().startswith("%url "):
32 # section line
33 self.pos += 5
34 idx = self.stream().find('\n')
35 if idx == -1:
36 value = self.stream()
37 self.pos += len(value)
38 else:
39 value = self.stream()[:idx]
40 self.pos += idx+1
41 block_dict = {'block_name': '%url', 'value': value}
42 return block_dict
43
44 block_dict = {'block_name': self.parse_block_name().upper()}
45 self.parse_block_body(block_dict)
46 if self.stream()[0] != '}':
47 raise Exception("No closing bracket '}' found at the end of block")
48 self.pos += 1
49 return block_dict
50
51 def parse_parameter_value(self, raw_value):
52 if raw_value.find(',') != -1:
53 return [self.parse_parameter_value(v.strip())
54 for v in raw_value.split(',')]
55 try:
56 return int(raw_value)
57 except ValueError:
58 if raw_value == "true":
59 return True
60 if raw_value == "false":
61 return False
62 if raw_value.find('"') == 0:
63 return raw_value[1:-1]
64 return raw_value
65
66 def parse_stanza(self, block_dict):
67 equal_idx = self.stream().find('=')
68 if equal_idx == -1:
69 raise Exception("Malformed stanza: no equal symbol found.")
70 semicolon_idx = self.stream().find(';')
71 parameter_name = self.stream()[:equal_idx].lower()
72 parameter_value = self.stream()[equal_idx+1:semicolon_idx]
73 block_dict[parameter_name] = self.parse_parameter_value(parameter_value)
74 self.pos += semicolon_idx+1
75
76 def parse_block_body(self, block_dict):
77 while True:
78 if self.stream().find('}') == 0:
79 # block end
80 return
81
82 last_pos = self.pos
83 semicolon_idx = self.stream().find(';')
84 lbracket_idx = self.stream().find('{')
85 is_semicolon = (semicolon_idx != -1)
86 is_lbracket = (lbracket_idx != -1)
87 is_semicolon_lt_lbracket = (semicolon_idx < lbracket_idx)
88
89 if is_semicolon and ((is_lbracket and is_semicolon_lt_lbracket) or not is_lbracket):
90 self.parse_stanza(block_dict)
91 elif is_lbracket and ((is_semicolon and not is_semicolon_lt_lbracket) or
92 (not is_semicolon)):
93 if '_blocks_' not in block_dict:
94 block_dict['_blocks_'] = []
95 block_dict['_blocks_'].append(self.parse_block_or_section())
96 else:
97 raise Exception("Malformed stanza: no semicolon found.")
98
99 if last_pos == self.pos:
100 raise Exception("Infinite loop while parsing block content")
101
102 def parse(self):
103 self.remove_whitespaces_quotes()
104 blocks = []
105 while self.stream():
106 blocks.append(self.parse_block_or_section())
107 return blocks
108
109 @staticmethod
110 def _indentation(depth, size=4):
111 conf_str = ""
112 for _ in range(0, depth*size):
113 conf_str += " "
114 return conf_str
115
116 @staticmethod
117 def write_block_body(block, depth=0):
118 def format_val(key, val):
119 if isinstance(val, list):
120 return ', '.join([format_val(key, v) for v in val])
121 if isinstance(val, bool):
122 return str(val).lower()
123 if isinstance(val, int) or (block['block_name'] == 'CLIENT'
124 and key == 'clients'):
125 return '{}'.format(val)
126 return '"{}"'.format(val)
127
128 conf_str = ""
129 for key, val in block.items():
130 if key == 'block_name':
131 continue
132 elif key == '_blocks_':
133 for blo in val:
134 conf_str += GaneshaConfParser.write_block(blo, depth)
135 elif val:
136 conf_str += GaneshaConfParser._indentation(depth)
137 conf_str += '{} = {};\n'.format(key, format_val(key, val))
138 return conf_str
139
140 @staticmethod
141 def write_block(block, depth=0):
142 if block['block_name'] == "%url":
143 return '%url "{}"\n\n'.format(block['value'])
144
145 conf_str = ""
146 conf_str += GaneshaConfParser._indentation(depth)
147 conf_str += format(block['block_name'])
148 conf_str += " {\n"
149 conf_str += GaneshaConfParser.write_block_body(block, depth+1)
150 conf_str += GaneshaConfParser._indentation(depth)
151 conf_str += "}\n"
152 return conf_str
153
154
155 class CephFSFSal:
156 def __init__(self, name, user_id=None, fs_name=None, sec_label_xattr=None,
157 cephx_key=None):
158 self.name = name
159 self.fs_name = fs_name
160 self.user_id = user_id
161 self.sec_label_xattr = sec_label_xattr
162 self.cephx_key = cephx_key
163
164 @classmethod
165 def from_fsal_block(cls, fsal_block):
166 return cls(fsal_block['name'],
167 fsal_block.get('user_id', None),
168 fsal_block.get('filesystem', None),
169 fsal_block.get('sec_label_xattr', None),
170 fsal_block.get('secret_access_key', None))
171
172 def to_fsal_block(self):
173 result = {
174 'block_name': 'FSAL',
175 'name': self.name,
176 }
177 if self.user_id:
178 result['user_id'] = self.user_id
179 if self.fs_name:
180 result['filesystem'] = self.fs_name
181 if self.sec_label_xattr:
182 result['sec_label_xattr'] = self.sec_label_xattr
183 if self.cephx_key:
184 result['secret_access_key'] = self.cephx_key
185 return result
186
187 @classmethod
188 def from_dict(cls, fsal_dict):
189 return cls(fsal_dict['name'], fsal_dict['user_id'],
190 fsal_dict['fs_name'], fsal_dict['sec_label_xattr'], None)
191
192 def to_dict(self):
193 return {
194 'name': self.name,
195 'user_id': self.user_id,
196 'fs_name': self.fs_name,
197 'sec_label_xattr': self.sec_label_xattr
198 }
199
200
201 class Client:
202 def __init__(self, addresses, access_type=None, squash=None):
203 self.addresses = addresses
204 self.access_type = access_type
205 self.squash = squash
206
207 @classmethod
208 def from_client_block(cls, client_block):
209 addresses = client_block['clients']
210 if not isinstance(addresses, list):
211 addresses = [addresses]
212 return cls(addresses,
213 client_block.get('access_type', None),
214 client_block.get('squash', None))
215
216 def to_client_block(self):
217 result = {
218 'block_name': 'CLIENT',
219 'clients': self.addresses,
220 }
221 if self.access_type:
222 result['access_type'] = self.access_type
223 if self.squash:
224 result['squash'] = self.squash
225 return result
226
227 @classmethod
228 def from_dict(cls, client_dict):
229 return cls(client_dict['addresses'], client_dict['access_type'],
230 client_dict['squash'])
231
232 def to_dict(self):
233 return {
234 'addresses': self.addresses,
235 'access_type': self.access_type,
236 'squash': self.squash
237 }
238
239
240 class Export:
241 def __init__(self, export_id, path, cluster_id, pseudo, access_type, squash, security_label,
242 protocols, transports, fsal, clients=None):
243 self.export_id = export_id
244 self.path = path
245 self.fsal = fsal
246 self.cluster_id = cluster_id
247 self.pseudo = pseudo
248 self.access_type = access_type
249 self.squash = squash
250 self.attr_expiration_time = 0
251 self.security_label = security_label
252 self.protocols = protocols
253 self.transports = transports
254 self.clients = clients
255
256 @classmethod
257 def from_export_block(cls, export_block, cluster_id):
258 fsal_block = [b for b in export_block['_blocks_']
259 if b['block_name'] == "FSAL"]
260
261 client_blocks = [b for b in export_block['_blocks_']
262 if b['block_name'] == "CLIENT"]
263
264 protocols = export_block.get('protocols')
265 if not isinstance(protocols, list):
266 protocols = [protocols]
267
268 transports = export_block.get('transports')
269 if not isinstance(transports, list):
270 transports = [transports]
271
272 return cls(export_block['export_id'],
273 export_block['path'],
274 cluster_id,
275 export_block['pseudo'],
276 export_block['access_type'],
277 export_block['squash'],
278 export_block['security_label'],
279 protocols,
280 transports,
281 CephFSFSal.from_fsal_block(fsal_block[0]),
282 [Client.from_client_block(client)
283 for client in client_blocks])
284
285 def to_export_block(self):
286 result = {
287 'block_name': 'EXPORT',
288 'export_id': self.export_id,
289 'path': self.path,
290 'pseudo': self.pseudo,
291 'access_type': self.access_type,
292 'squash': self.squash,
293 'attr_expiration_time': self.attr_expiration_time,
294 'security_label': self.security_label,
295 'protocols': self.protocols,
296 'transports': self.transports,
297 }
298 result['_blocks_'] = [self.fsal.to_fsal_block()]
299 result['_blocks_'].extend([client.to_client_block()
300 for client in self.clients])
301 return result
302
303 @classmethod
304 def from_dict(cls, export_id, ex_dict):
305 return cls(export_id,
306 ex_dict['path'],
307 ex_dict['cluster_id'],
308 ex_dict['pseudo'],
309 ex_dict.get('access_type', 'R'),
310 ex_dict.get('squash', 'no_root_squash'),
311 ex_dict.get('security_label', True),
312 ex_dict.get('protocols', [4]),
313 ex_dict.get('transports', ['TCP']),
314 CephFSFSal.from_dict(ex_dict['fsal']),
315 [Client.from_dict(client) for client in ex_dict['clients']])
316
317 def to_dict(self):
318 return {
319 'export_id': self.export_id,
320 'path': self.path,
321 'cluster_id': self.cluster_id,
322 'pseudo': self.pseudo,
323 'access_type': self.access_type,
324 'squash': self.squash,
325 'security_label': self.security_label,
326 'protocols': sorted([p for p in self.protocols]),
327 'transports': sorted([t for t in self.transports]),
328 'fsal': self.fsal.to_dict(),
329 'clients': [client.to_dict() for client in self.clients]
330 }