forked from saltstack/salt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathglusterfs.py
More file actions
414 lines (312 loc) · 10.9 KB
/
Copy pathglusterfs.py
File metadata and controls
414 lines (312 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# -*- coding: utf-8 -*-
'''
Manage a glusterfs pool
'''
from __future__ import absolute_import
# Import python libs
import logging
# Import 3rd-party libs
from salt.ext.six.moves import range, shlex_quote as _cmd_quote # pylint: disable=import-error,redefined-builtin
try:
from shlex import quote as _cmd_quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _cmd_quote
# Import salt libs
import salt.utils
import salt.utils.cloud as suc
log = logging.getLogger(__name__)
def __virtual__():
'''
Only load this module if the gluster command exists
'''
if salt.utils.which('gluster'):
return True
return False
def list_peers():
'''
Return a list of gluster peers
CLI Example:
.. code-block:: bash
salt '*' glusterfs.list_peers
GLUSTER direct CLI example (to show what salt is sending to gluster):
$ gluster peer status
GLUSTER CLI 3.4.4 return example (so we know what we are parsing):
Number of Peers: 2
Hostname: ftp2
Port: 24007
Uuid: cbcb256b-e66e-4ec7-a718-21082d396c24
State: Peer in Cluster (Connected)
Hostname: ftp3
Uuid: 5ea10457-6cb2-427b-a770-7897509625e9
State: Peer in Cluster (Connected)
'''
get_peer_list = 'gluster peer status | awk \'/Hostname/ {print $2}\''
result = __salt__['cmd.run'](get_peer_list, python_shell=True)
if 'No peers present' in result:
return None
else:
return result.splitlines()
def peer(name):
'''
Add another node into the peer list.
name
The remote host to probe.
CLI Example:
.. code-block:: bash
salt 'one.gluster.*' glusterfs.peer two
GLUSTER direct CLI example (to show what salt is sending to gluster):
$ gluster peer probe ftp2
GLUSTER CLI 3.4.4 return example (so we know what we are parsing):
#if the "peer" is the local host:
peer probe: success: on localhost not needed
#if the peer was just added:
peer probe: success
#if the peer was already part of the cluster:
peer probe: success: host ftp2 port 24007 already in peer list
'''
if suc.check_name(name, 'a-zA-Z0-9._-'):
return 'Invalid characters in peer name'
cmd = 'gluster peer probe {0}'.format(name)
return __salt__['cmd.run'](cmd)
def create(name, bricks, stripe=False, replica=False, device_vg=False,
transport='tcp', start=False):
'''
Create a glusterfs volume.
name
Name of the gluster volume
bricks
Bricks to create volume from, in <peer>:<brick path> format. For \
multiple bricks use list format: '["<peer1>:<brick1>", \
"<peer2>:<brick2>"]'
stripe
Stripe count, the number of bricks should be a multiple of the stripe \
count for a distributed striped volume
replica
Replica count, the number of bricks should be a multiple of the \
replica count for a distributed replicated volume
device_vg
If true, specifies volume should use block backend instead of regular \
posix backend. Block device backend volume does not support multiple \
bricks
transport
Transport protocol to use, can be 'tcp', 'rdma' or 'tcp,rdma'
start
Start the volume after creation
CLI Example:
.. code-block:: bash
salt host1 glusterfs.create newvolume host1:/brick
salt gluster1 glusterfs.create vol2 '["gluster1:/export/vol2/brick", \
"gluster2:/export/vol2/brick"]' replica=2 start=True
'''
# If single brick given as a string, accept it
if isinstance(bricks, str):
bricks = [bricks]
# Error for block devices with multiple bricks
if device_vg and len(bricks) > 1:
return 'Error: Block device backend volume does not support multipl' +\
'bricks'
# Validate bricks syntax
for brick in bricks:
try:
peer_name, path = brick.split(':')
if not path.startswith('/'):
return 'Error: Brick paths must start with /'
except ValueError:
return 'Error: Brick syntax is <peer>:<path>'
# Format creation call
cmd = 'gluster volume create {0} '.format(name)
if stripe:
cmd += 'stripe {0} '.format(stripe)
if replica:
cmd += 'replica {0} '.format(replica)
if device_vg:
cmd += 'device vg '
if transport != 'tcp':
cmd += 'transport {0} '.format(transport)
cmd += ' '.join(bricks)
log.debug('Clustering command:\n{0}'.format(cmd))
ret = __salt__['cmd.run'](cmd)
if 'failed' in ret:
return ret
if start:
result = __salt__['cmd.run']('gluster volume start {0}'.format(name))
if result.endswith('success'):
return 'Volume {0} created and started'.format(name)
else:
return result
else:
return 'Volume {0} created. Start volume to use'.format(name)
def list_volumes():
'''
List configured volumes
CLI Example:
.. code-block:: bash
salt '*' glusterfs.list_volumes
'''
results = __salt__['cmd.run']('gluster volume list').splitlines()
if results[0] == 'No volumes present in cluster':
return []
else:
return results
def status(name):
'''
Check the status of a gluster volume.
name
Volume name
CLI Example:
.. code-block:: bash
salt '*' glusterfs.status myvolume
'''
# Get volume status
cmd = 'gluster volume status {0}'.format(name)
result = __salt__['cmd.run'](cmd).splitlines()
if 'does not exist' in result[0]:
return result[0]
if 'is not started' in result[0]:
return result[0]
ret = {'bricks': {}, 'nfs': {}, 'healers': {}}
# Iterate line by line, concatenating lines the gluster cli separated
for line_number in range(len(result)):
line = result[line_number]
if line.startswith('Brick'):
# See if this line is broken up into multiple lines
while len(line.split()) < 5:
line_number = line_number + 1
line = line.rstrip() + result[line_number]
# Parse Brick data
brick, port, online, pid = line.split()[1:]
host, path = brick.split(':')
data = {'port': port, 'pid': pid, 'host': host, 'path': path}
if online == 'Y':
data['online'] = True
else:
data['online'] = False
# Store, keyed by <host>:<brick> string
ret['bricks'][brick] = data
elif line.startswith('NFS Server on'):
# See if this line is broken up into multiple lines
while len(line.split()) < 5:
line_number = line_number + 1
line = line.rstrip() + result[line_number]
# Parse NFS Server data
host, port, online, pid = line.split()[3:]
data = {'port': port, 'pid': pid}
if online == 'Y':
data['online'] = True
else:
data['online'] = False
# Store, keyed by hostname
ret['nfs'][host] = data
elif line.startswith('Self-heal Daemon on'):
# See if this line is broken up into multiple lines
while len(line.split()) < 5:
line_number = line_number + 1
line = line.rstrip() + result[line_number]
# Parse NFS Server data
host, port, online, pid = line.split()[3:]
data = {'port': port, 'pid': pid}
if online == 'Y':
data['online'] = True
else:
data['online'] = False
# Store, keyed by hostname
ret['healers'][host] = data
return ret
def start_volume(name):
'''
Start a gluster volume.
name
Volume name
CLI Example:
.. code-block:: bash
salt '*' glusterfs.start mycluster
'''
volumes = list_volumes()
if name in volumes:
if isinstance(status(name), dict):
return 'Volume already started'
cmd = 'gluster volume start {0}'.format(name)
result = __salt__['cmd.run'](cmd)
if result.endswith('success'):
return 'Volume {0} started'.format(name)
else:
return result
return 'Volume does not exist'
def stop_volume(name):
'''
Stop a gluster volume.
name
Volume name
CLI Example:
.. code-block:: bash
salt '*' glusterfs.stop_volume mycluster
'''
vol_status = status(name)
if isinstance(vol_status, dict):
cmd = 'yes | gluster volume stop {0}'.format(_cmd_quote(name))
result = __salt__['cmd.run'](cmd, python_shell=True)
if result.splitlines()[0].endswith('success'):
return 'Volume {0} stopped'.format(name)
else:
return result
return vol_status
def delete(target, stop=True):
'''
Deletes a gluster volume
target
Volume to delete
stop
Stop volume before delete if it is started, True by default
'''
if target not in list_volumes():
return 'Volume does not exist'
cmd = 'yes | gluster volume delete {0}'.format(_cmd_quote(target))
# Stop volume if requested to and it is running
if stop is True and isinstance(status(target), dict):
stop_volume(target)
stopped = True
else:
stopped = False
# Warn volume is running if stop not requested
if isinstance(status(target), dict):
return 'Error: Volume must be stopped before deletion'
result = __salt__['cmd.run'](cmd, python_shell=True)
if result.splitlines()[0].endswith('success'):
if stopped:
return 'Volume {0} stopped and deleted'.format(target)
else:
return 'Volume {0} deleted'.format(target)
else:
return result
def add_volume_bricks(name, bricks):
'''
Add brick(s) to an existing volume
name
Volume name
bricks
List of bricks to add to the volume
'''
new_bricks = []
cmd = 'echo yes | gluster volume add-brick {0}'.format(name)
if isinstance(bricks, str):
bricks = [bricks]
volume_bricks = status(name)
if 'does not exist' in volume_bricks:
return volume_bricks
if 'is not started' in volume_bricks:
return volume_bricks
for brick in bricks:
if brick in volume_bricks['bricks']:
log.debug('Brick {0} already in volume {1}...excluding from command'.format(brick, name))
else:
new_bricks.append(brick)
if len(new_bricks) > 0:
for brick in new_bricks:
cmd += ' '+str(brick)
result = __salt__['cmd.run'](cmd)
if result.endswith('success'):
return '{0} bricks successfully added to the volume {1}'.format(len(new_bricks), name)
else:
return result
else:
return 'Bricks already in volume {0}'.format(name)