forked from pbel78/docker-db
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexaconf
executable file
·1719 lines (1668 loc) · 59.7 KB
/
exaconf
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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#! /usr/bin/env python3
import os, sys, argparse, time, getpass, random
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from libconfd.common import util
from libconfd.common.database import db_reorder_affinities
from libconfd.EXAConf import EXAConfError
import exacos
else:
try:
from libexadt.EXAConf import EXAConfError
from libexadt import util
assert EXAConfError, util #silence pyflakes
except ImportError:
sys.path.insert(0,'/usr/opt/EXASuite-7/EXAClusterOS-7.1.16/lib')
from libconfd.common import util
from libconfd.common.database import db_reorder_affinities
from libconfd.EXAConf import EXAConfError
import exacos
my_version = "7.1.16"
# {{{ Setup logging
_log_type_list = ('Error', 'Info') # only Error and Info is used now, more can be added
class _log: pass
log = _log()
def _create_log_stderr(log_type):
msgtemp = '[%%s] %s: %%s\n' % log_type
if log_type == 'Info': return lambda _msg: None # do not output info on console
return lambda msg: sys.stderr.write(msgtemp % (time.strftime('%Y-%m-%d %H:%M:%S'), msg))
def _create_log_logger(logger, log_type):
func = getattr(_logger, 'log%s' % _log_type)
func2 = _create_log_stderr(log_type)
return lambda msg: map(lambda f: f('EXACONF: %s' % str(msg)), (func, func2))
try:
if not os.path.exists('/var/run/ecos_unix_auth'):
raise RuntimeError('No COS used.')
import exacos
_logger = exacos.logger('ConfD')
for _log_type in _log_type_list:
setattr(log, _log_type.lower(), _create_log_logger(_logger, _log_type))
except:
for _log_type in _log_type_list:
setattr(log, _log_type.lower(), _create_log_stderr(_log_type))
# }}}
# {{{ str2bool
def str2bool(v):
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
# }}}
# {{{ Print version
def print_version(cmd):
print(my_version)
# }}}
# {{{ Read EXAConf
def read_exaconf(filename, ro = False, initialized = False):
try: exaconf = util.read_exaconf(filename, ro, initialized)
except EXAConfError as e:
log.error(str(e).replace('ERROR::EXAConf: ', ''))
sys.exit(1)
return exaconf
# }}}
# {{{ Add node
def add_node(cmd):
"""
Adds a new node to the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.add_node(nid = cmd.node_id, priv_net=cmd.priv_net, pub_net=cmd.pub_net)
# }}}
# {{{ Modify node
def modify_node(cmd):
"""
Modify an existing node.
"""
exaconf = read_exaconf(cmd.exaconf)
if cmd.node_id != "_all" and not exaconf.node_exists(cmd.node_id):
print("Node '%s' does not exist in EXAConf '%s'!" % (cmd.node_id, cmd.exaconf))
return 1
nodes = exaconf.get_nodes()
node_conf = nodes[cmd.node_id]
# modify supported parameters
if (cmd.pub_net and cmd.pub_ip) or (cmd.priv_net and cmd.priv_ip):
print("You can either change the IP or the network. Not both!")
return 1
if cmd.priv_net:
node_conf.private_net = cmd.priv_net
if 'private_ip' in node_conf:
del node_conf.private_ip
if cmd.pub_net:
node_conf.public_net = cmd.pub_net
if 'public_ip' in node_conf:
del node_conf.public_ip
if cmd.priv_ip:
node_conf.private_ip = cmd.priv_ip
if 'private_net' in node_conf:
del node_conf.private_net
if cmd.pub_ip:
node_conf.public_ip = cmd.pub_ip
if 'public_net' in node_conf:
del node_conf.public_net
exaconf.set_node_conf(node_conf, cmd.node_id)
# }}}
# {{{ Remove node
def remove_node(cmd):
"""
Removes a node from the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_node(nid = cmd.node_id, force=cmd.force)
# }}}
# {{{ Add node disk
def add_node_disk(cmd):
"""
Adds an empty storage disk to the given node in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
devices = [ d.strip() for d in cmd.devices.split(",") if d.strip() != "" ] if cmd.devices else None
drives = [ d.strip() for d in cmd.drives.split(",") if d.strip() != "" ] if cmd.drives else None
exaconf.add_node_disk(cmd.node, cmd.disk, component = cmd.component, devices = devices, drives = drives, overwrite_existing = cmd.overwrite_existing)
# }}}
# {{{ Remove node disk
def remove_node_disk(cmd):
"""
Removes the given storage disk (or all disks) from the given node in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_node_disk(cmd.node, cmd.disk)
# }}}
# {{{ Add node device
def add_node_device(cmd):
"""
Adds a device to an existing node in the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.add_node_device(cmd.node_id, cmd.disk, cmd.device, cmd.path)
# }}}
# {{{ Remove node device
def remove_node_device(cmd):
"""
Removes a device from an existing node in the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_node_device(cmd.node_id, cmd.disk, cmd.device)
# }}}
# {{{ Add volume
def add_volume(cmd):
"""
Adds a new volume to the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
vol_nodes = [ n.strip() for n in cmd.nodes.split(",") if n.strip() != "" ]
try: vol_owner = tuple( int(o.strip()) for o in cmd.owner.split(":") if o.strip() != "" )
except: vol_owner = None
if vol_owner is None or len(vol_owner) != 2:
print("Please specify user and group ID for owner (e.g. 1000:1000)")
return 1
exaconf.add_volume(
name = cmd.name,
vol_type = cmd.type,
size = cmd.size,
disk = cmd.disk,
redundancy = cmd.redundancy,
nodes = vol_nodes,
owner = vol_owner,
num_master_nodes = cmd.num_master_nodes,
perm = cmd.perm,
labels = cmd.labels,
block_size = cmd.block_size,
stripe_size = cmd.stripe_size
)
# }}}
# {{{ Modify volume
def modify_volume(cmd):
"""
Modify an existing volume.
"""
exaconf = read_exaconf(cmd.exaconf)
volumes = exaconf.get_volumes()
if cmd.name != "_all" and cmd.name not in volumes:
print("Volume '%s' does not exist in EXAConf '%s'!" % (cmd.name, cmd.exaconf))
return 1
vol_conf = volumes[cmd.name]
# change supported options
if cmd.owner:
vol_owner = tuple( int(o.strip()) for o in cmd.owner.split(":") if o.strip() != "" )
vol_conf.owner = vol_owner
if cmd.size:
vol_conf.size = cmd.size
if cmd.disk:
vol_conf.disk = cmd.disk
if cmd.nodes:
vol_conf.nodes = [ n.strip() for n in cmd.nodes.split(',') if n.strip() != '' ]
#adapt to new nr. of nodes, but may be overwritten below
vol_conf.num_master_nodes = len(vol_conf.nodes)
if cmd.num_master_nodes:
vol_conf.num_master_nodes = cmd.num_master_nodes
if cmd.labels:
vol_conf.labels = cmd.labels
if cmd.redundancy:
vol_conf.redundancy = cmd.redundancy
exaconf.set_volume_conf(vol_conf, cmd.name)
# }}}
# {{{ Add remote volume
def add_remote_volume(cmd):
"""
Adds a remote volume to the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
try: vol_owner = tuple( int(o.strip()) for o in cmd.owner.split(":") if o.strip() != "" )
except: vol_owner = None
if vol_owner is None or len(vol_owner) != 2:
print("Please specify user and group ID for owner (e.g. 1000:1000)")
return 1
exaconf.add_remote_volume(cmd.type, cmd.url, vol_owner,
remote_volume_name = cmd.name,
remote_volume_id = cmd.id,
labels = cmd.labels,
username = cmd.username,
password = cmd.passwd,
options = cmd.options)
# }}}
# {{{ Remove volume
def remove_volume(cmd):
"""
Removes the given volume from the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_volume(cmd.name, cmd.force)
# }}}
# {{{ Remove remote volume
def remove_remote_volume(cmd):
"""
Removes the given remote volume from the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_remote_volume(remote_volume_name = cmd.name, remote_volume_id = None, force=cmd.force)
# }}}
# {{{ Set storage conf
def set_storage_conf(cmd):
"""
Applies the given EXAStorage to the given EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
st_conf = exaconf.get_storage_conf()
if cmd.bg_rec_enabled is not None:
st_conf.bg_rec_enabled = cmd.bg_rec_enabled
if cmd.bg_rec_limit is not None:
st_conf.bg_rec_limit = cmd.bg_rec_limit
if cmd.space_warn_threshold is not None:
st_conf.space_warn_threshold = cmd.space_warn_threshold
exaconf.set_storage_conf(st_conf)
# }}}
# {{{ Encode shadow passwd
def encode_shadow_passwd(cmd):
"""
Encodes given password and into an /etc/shadow compatible SHA512 hash. Prompts for password if none is given.
"""
passwd = cmd.passwd
if passwd is None:
passwd = getpass.getpass()
print(util.encode_shadow_passwd(passwd))
# }}}
# {{{ Encode db passwd
def encode_db_passwd(cmd):
"""
Encodes given password and into an DB compatible tigerhash hash. Prompts for password if none is given.
"""
passwd = cmd.passwd
if passwd is None:
passwd = getpass.getpass()
print(exacos.get_db_passwd_hash(passwd, random.randint(1, 2**32)))
# }}}
# {{{ Reorder database afinities
def reorder_database_affinities(cmd):
"""
Reorder database nodes to match the volume nodes. Only usable in a running cluster.
"""
st = exacos.storage()
db = exacos.exa_db(cmd.db_name)
try: db_reorder_affinities(db, st, log = lambda X: sys.stdout.write('%s\n' % X))
except Exception as err:
print('ERROR: %s' % str(err))
return 1
# }}}
# {{{ List groups
def list_groups(cmd):
"""
Lists all groups in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
for g in exaconf.get_groups().items():
print("%s : %i" % (g[0], g[1].id))
# }}}
# {{{ Add group
def add_group(cmd):
"""
Add a group to EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.add_group(cmd.name, cmd.id)
# }}}
# {{{ Remove group
def remove_group(cmd):
"""
Remove a group from EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_group(cmd.name)
# }}}
# {{{ List users
def list_users(cmd):
"""
Lists all users in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
for u in exaconf.get_users().items():
print("= %s (%i) =" % (u[0], u[1].id))
print(" group : %s" % u[1].group)
print(" login : %s" % ("enabled" if u[1].login_enabled else "disabled"))
if "passwd" in u[1]:
print(" passwd : %s" % u[1].passwd)
if "additional_groups" in u[1]:
print(" groups : %s" % ", ".join(u[1].additional_groups))
if "authorized_keys" in u[1]:
print(" auth-keys : %s" % "\n ".join(u[1].authorized_keys))
# }}}
# {{{ Add user
def add_user(cmd):
"""
Add a user to EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
passwd = cmd.passwd
encode_passwd = False if cmd.encode_passwd is None else True
add_groups = [ g.strip() for g in cmd.groups.split(",") if g.strip() != "" ] if cmd.groups else None
auth_keys = None
if cmd.auth_keys:
auth_keys = [ k.strip() for k in cmd.auth_keys.split(",") if k.strip() != "" ]
elif cmd.auth_keys_file:
with open(cmd.auth_keys_file) as f:
auth_keys = [ l.strip() for l in f ]
# prompt for password if not given and always encode it
if passwd is None and cmd.prompt_passwd:
passwd = getpass.getpass()
encode_passwd = True
exaconf.add_user(username = cmd.name, userid = cmd.id,
group = cmd.group, login_enabled = cmd.login_enabled,
password = passwd,
encode_passwd = encode_passwd,
additional_groups = add_groups,
authorized_keys = auth_keys)
# }}}
# {{{ Modify user
def modify_user(cmd):
"""
Modify an existing user. Everything except ID and password.
"""
exaconf = read_exaconf(cmd.exaconf)
users = exaconf.get_users()
if cmd.name != "_all" and cmd.name not in users:
print("User '%s' does not exist in EXAConf '%s'!" % (cmd.name, cmd.exaconf))
return 1
user_conf = users[cmd.name]
# change supported options
if cmd.group:
user_conf.group = cmd.group
if cmd.login_enabled:
user_conf.login_enabled = cmd.login_enabled
if cmd.groups:
user_conf.additional_groups = [ g.strip() for g in cmd.groups.split(",") if g.strip() != "" ]
if cmd.auth_keys:
user_conf.authorized_keys = [ k.strip() for k in cmd.auth_keys.split(",") if k.strip() != "" ]
exaconf.set_user_conf(user_conf, cmd.name, extend_groups=cmd.extend_groups, extend_keys=cmd.extend_keys)
# }}}
# {{{ Passwd user
def passwd_user(cmd):
"""
Change user password.
"""
exaconf = read_exaconf(cmd.exaconf)
users = exaconf.get_users()
if not cmd.name in users:
print("User '%s' does not exist in EXAConf '%s'!" % (cmd.name, cmd.exaconf))
return 1
user_conf = users[cmd.name]
user_conf.passwd = cmd.passwd
encode_passwd = False if cmd.encode_passwd is None else True
# prompt for password if not given and always encode it
if user_conf.passwd is None:
user_conf.passwd = getpass.getpass()
encode_passwd = True
exaconf.set_user_conf(user_conf, cmd.name, encode_passwd=encode_passwd)
# }}}
# {{{ Remove user
def remove_user(cmd):
"""
Remove a user from EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_user(cmd.name)
# }}}
# {{{ Add BucketFS
def add_bucketfs(cmd):
"""
Add a BucketFS to EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
bfs_owner = tuple( int(o.strip()) for o in cmd.owner.split(":") if o.strip() != "" )
exaconf.add_bucketfs(bucketfs_name = cmd.name, owner = bfs_owner,
http_port = cmd.http_port, https_port = cmd.https_port,
mode = cmd.mode,
sync_key = cmd.sync_key,
sync_period = cmd.sync_period,
bucketvolume = cmd.bucketvolume,
path = cmd.path)
# }}}
# {{{ Modify BucketFS
def modify_bucketfs(cmd):
"""
Modify an existing BucketFS. Everything except sync_period and path.
"""
exaconf = read_exaconf(cmd.exaconf)
bucketfs = exaconf.get_bucketfs()
if cmd.name != "_all" and cmd.name not in bucketfs:
print("BucketFS '%s' does not exist in EXAConf '%s'!" % (cmd.name, cmd.exaconf))
return 1
bfs_conf = bucketfs[cmd.name]
# change supported options
if cmd.owner:
bfs_owner = tuple( int(o.strip()) for o in cmd.owner.split(":") if o.strip() != "" )
bfs_conf.owner = bfs_owner
if cmd.http_port:
bfs_conf.http_port = cmd.http_port
if cmd.https_port:
bfs_conf.https_port = cmd.https_port
if cmd.sync_period:
bfs_conf.sync_period = cmd.sync_period
exaconf.set_bucketfs_conf(bfs_conf, cmd.name)
# }}}
# {{{ Remove BucketFS
def remove_bucketfs(cmd):
"""
Remove a BucketFS from EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_bucketfs(cmd.name)
# }}}
# {{{ Add Bucket
def add_bucket(cmd):
"""
Add a Bucket to EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
additional_files = None
if cmd.additional_files:
additional_files = [ f.strip() for f in cmd.additional_files.split(",") if f.strip() != "" ]
exaconf.add_bucket(bucket_name = cmd.name, bucketfs_name = cmd.bfs_name,
public = cmd.public,
read_password = cmd.read_passwd,
write_password = cmd.write_passwd,
additional_files = additional_files)
# }}}
# {{{ Modify Bucket
def modify_bucket(cmd):
"""
Modify an existing Bucket in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
additional_files = None
if cmd.additional_files:
additional_files = [ f.strip() for f in cmd.additional_files.split(",") if f.strip() != "" ]
bucketfs = exaconf.get_bucketfs()
if cmd.bfs_name not in bucketfs:
print("BucketFS '%s' does not exist in EXAConf '%s'!" % (cmd.bfs_name, cmd.exaconf))
return 1
bfs_conf = bucketfs[cmd.bfs_name]
if cmd.name != "_all" and cmd.name not in bfs_conf.buckets:
print("Bucket '%s' does not exist in BucketFS '%s' in EXAConf '%s'!" % (cmd.bfs_name, cmd.name, cmd.exaconf))
return 1
b_conf = bfs_conf.buckets[cmd.name]
# change supported options
b_conf.public = cmd.public
if cmd.read_passwd:
b_conf.read_passwd = cmd.read_passwd
if cmd.write_passwd:
b_conf.write_passwd = cmd.write_passwd
if additional_files:
b_conf.additional_files = additional_files
exaconf.set_bucket_conf(b_conf, cmd.name, cmd.bfs_name)
# }}}
# {{{ Remove Bucket
def remove_bucket(cmd):
"""
Remove a Bucket from EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_bucket(cmd.name, cmd.bfs_name)
# }}}
# {{{ Add Logging
def add_Logging(cmd):
"""
Add a Logging to EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.add_Logging(LogRotationTypes = cmd.LogRotationTypes,
RemoteLogRotationVolume = cmd.RemoteLogRotationVolume,
RemoteLogRotationPrefix = cmd.RemoteLogRotationPrefix)
# }}}
# {{{ Add backup schedule
def add_backup_schedule(cmd):
"""
Add a backup schedule to an existing database in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.add_backup_schedule(db_name = cmd.db_name, backup_name = cmd.backup_name,
volume = cmd.volume, level = cmd.level, minute = cmd.minute,
hour = cmd.hour, day = cmd.day, month = cmd.month,
weekday = cmd.weekday, expire = cmd.expire, enabled = not cmd.disabled)
# }}}
# {{{ Remove backup schedule
def remove_backup_schedule(cmd):
"""
Remove an existing backup schedule from a database in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
exaconf.remove_backup_schedule(db_name = cmd.db_name, backup_name = cmd.backup_name)
# }}}
# {{{ Modify backup schedule
def modify_backup_schedule(cmd):
"""
Modify an existing backup schedule in EXAConf.
"""
exaconf = read_exaconf(cmd.exaconf)
databases = exaconf.get_databases()
if cmd.db_name not in databases:
print("Database '%s' does not exist in EXAConf '%s'!" % (cmd.db_name, cmd.exaconf))
return 1
db_conf = databases[cmd.db_name]
if 'backups' not in db_conf or (cmd.backup_name != "_all" and cmd.backup_name not in db_conf.backups):
print("Backup schedule '%s' of DB '%s' does not exist in EXAConf '%s'!" % (cmd.backup_name, cmd.db_name, cmd.exaconf))
return 1
ba_conf = db_conf.backups[cmd.backup_name]
# change supported options
if cmd.minute:
ba_conf.minute = cmd.minute
if cmd.hour:
ba_conf.hour = cmd.hour
if cmd.day:
ba_conf.day = cmd.day
if cmd.month:
ba_conf.month = cmd.month
if cmd.weekday:
ba_conf.weekday = cmd.weekday
if cmd.disabled:
ba_conf.enabled = not cmd.disabled
exaconf.set_backup_schedule_conf(ba_conf, cmd.db_name, cmd.backup_name)
# }}}
# {{{ Commit
def commit(cmd):
"""
Commits the current state of the local node to all online nodes in the cluster
by copying the given EXAConf to all nodes and using 'exalocalconf' to commit
the EXAConf on each node. This command only works within a cluster (i. e.
it must be executed on an Exasol cluster node / container).
"""
try:
from pipes import quote as shquote
from libconfd import file_sync
from exacos import process_handler
except ImportError:
print("Can't load required modules. This command only works within an Exasol cluster.")
return 1
command = 'exalocalconf --commit-local -c %s 2>&1' % shquote(cmd.exaconf)
print("=== Step 1: synchronizing '%s' ===" % shquote(cmd.exaconf))
exaconf = read_exaconf(cmd.exaconf)
sync = file_sync.file_sync(log_stderr=True, verbose=cmd.verbose)
sync.sync_file('%s' % shquote(cmd.exaconf))
print("--> Successful!")
print("=== Step 2: executing '%s' ===" % command)
ph = process_handler()
part, error = ph.startPartitionControlled({'all_nodes': True, 'params': ['/bin/sh', '-c', command]})
if part <= 0:
print("Failed to create 'exaconf' partition: %s. " % error)
return 1
ret, exit_status = ph.waitPartitionExitStatus(part)
if ret != 0:
print("'waitPartitionExitStatus()' returned with %i. " % ret)
ph.release(part)
return 1
if exit_status == 0:
print("--> Successful!")
else:
print("--> ERROR:")
print(ph.read(part))
ph.release(part)
return 1
ph.release(part)
print("=== Step 3: creating status file ===")
rev = str(exaconf.get_revision())
ts = time.strftime("%d.%m.%Y - %H:%M:%S")
sync.write_to_nodes("'%s' : revision %s : %s" % (cmd.exaconf, rev, ts),
"/exa/etc/EXAConf.commited.manually")
print("--> Successful!")
# }}}
# {{{ Main
def main():
parser = argparse.ArgumentParser(
description = 'Command line tool for modifying EXAConf.',
prog = 'exaconf')
cmdparser = parser.add_subparsers(
dest = 'command',
title = 'commands',
description = 'supported commands')
cmdparser.required = True
# version command
parser_ver = cmdparser.add_parser(
'version',
help='Print the exaconf CLI tool version number')
parser_ver.set_defaults(func=print_version)
# add node command
parser_an = cmdparser.add_parser(
'add-node',
help='Add a new node to EXAConf.')
parser_an.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file.')
parser_an.add_argument(
'--priv-net', '-p',
type=str,
required = True,
help="Private network (e.g. '10.10.10.12/24'). Characters 'x' and 'X' in the IP are replaced with the node ID.")
parser_an.add_argument(
'--pub-net', '-P',
type=str,
required = False,
help="Public network (e.g. '10.10.0.12/24'). Characters 'x' and 'X' in the IP are replaced with the node ID.")
parser_an.add_argument(
'--node-id', '-n',
type = int,
required = False,
help='ID for the new node (automatically selected if omitted).')
parser_an.set_defaults(func=add_node)
# modify node command
parser_mn = cmdparser.add_parser(
'modify-node',
help='Modify an existing node in EXAConf.')
parser_mn.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file.')
parser_mn.add_argument(
'--node-id', '-n',
type = str,
required = True,
help='ID of the node.')
parser_mn.add_argument(
'--priv-net', '-p',
type=str,
help="Private network (e.g. '10.10.10.12/24'). Characters 'x' and 'X' in the IP are replaced with the node ID.")
parser_mn.add_argument(
'--pub-net', '-P',
type=str,
help="Public network (e.g. '10.10.0.12/24'). Characters 'x' and 'X' in the IP are replaced with the node ID.")
parser_mn.add_argument(
'--priv-ip', '-i',
type=str,
help="Private IP address (e.g. '10.10.10.12'). The netmask is not modified.")
parser_mn.add_argument(
'--pub-ip', '-I',
type=str,
help="Public IP address (e.g. '10.10.0.12'). The netmask is not modified.")
parser_mn.set_defaults(func=modify_node)
# remove node command
parser_rn = cmdparser.add_parser(
'remove-node',
help='Remove a node from EXAConf.')
parser_rn.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file.')
parser_rn.add_argument(
'--node-id', '-n',
type = str,
required = True,
help='ID of the node that should be removed.')
parser_rn.add_argument(
'--force', '-f',
action='store_true',
default=False,
help="Remove node even if it's in use by a volume or database.")
parser_rn.set_defaults(func=remove_node)
# add node device command
parser_and = cmdparser.add_parser(
'add-node-device',
help='Add a device to an existing node in EXAConf.')
parser_and.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file.')
parser_and.add_argument(
'--node-id', '-n',
type = int,
required = True,
help='Node ID')
parser_and.add_argument(
'--disk', '-D',
type=str,
required = True,
help="The disk that should contain the given device. Created if it does not exist.")
parser_and.add_argument(
'--device', '-d',
type=str,
required = True,
help="Device name (only the basename of the device file).")
parser_and.add_argument(
'--path', '-p',
type=str,
required = False,
help="Absolute path to the directory that contains the device file (if it's not in '/exa/data/storage/').")
parser_and.set_defaults(func=add_node_device)
# remove node device command
parser_rnd = cmdparser.add_parser(
'remove-node-device',
help='Remove a device from an existing node in EXAConf. The disk is also removed if it does not contain other devices.')
parser_rnd.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_rnd.add_argument(
'--node-id', '-n',
type = int,
required = True,
help='Node ID')
parser_rnd.add_argument(
'--disk', '-D',
type=str,
required = True,
help="The disk that contains the given device")
parser_rnd.add_argument(
'--device', '-d',
type=str,
required = True,
help="Device name (only the basename of the device file)")
parser_rnd.set_defaults(func=remove_node_device)
# add node disk command
parser_andi = cmdparser.add_parser(
'add-node-disk',
help="Add an disk to an existing node in EXAConf. Use 'add-node-device' to add a disk with devices.")
parser_andi.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_andi.add_argument(
'--node-id', '-n',
type = int,
required = True,
help='Node ID')
parser_andi.add_argument(
'--disk', '-D',
type=str,
required = True,
help="Name of the disk that should be added.")
parser_andi.add_argument(
'--component', '-c',
type=str,
required = False,
help="The component that should use this disk (default: 'exastorage').")
parser_andi.add_argument(
'--devices', '-d',
type=str,
required = False,
help="Comma-separated list of device names.")
parser_andi.add_argument(
'--drives', '-r',
type=str,
required = False,
help="Comma-separated list of drive IDs.")
parser_andi.add_argument(
'--overwrite-existing', '-O',
action='store_true',
required = False,
help="Overwrite any existing disk with the same name.")
parser_andi.set_defaults(func=add_node_disk)
# remove node disk command
parser_rndi = cmdparser.add_parser(
'remove-node-disk',
help='Remove a disk (or all disks) from an existing node in EXAConf.')
parser_rndi.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_rndi.add_argument(
'--node-id', '-n',
type = int,
required = True,
help='Node ID')
parser_rndi.add_argument(
'--disk', '-D',
type=str,
required = True,
help="The disk that should be removed ('all' for all disks).")
parser_rndi.set_defaults(func=remove_node_disk)
# add volume command
parser_av = cmdparser.add_parser(
'add-volume',
help='Add a new volume to EXAConf.')
parser_av.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_av.add_argument(
'--name', '-n',
type = str,
required = True,
help='Name of the new volume (must be unqiue in the cluster).')
parser_av.add_argument(
'--type', '-t',
type=str,
required = True,
help="Type of the new volume ('data', 'archive' or 'remote').")
parser_av.add_argument(
'--size', '-s',
type=str,
required = True,
help="Size of the new volume, e.g. 1TiB, 20GiB, 20000000B, etc..")
parser_av.add_argument(
'--disk', '-d',
type=str,
required = True,
help="Disk to be used for the new volume.")
parser_av.add_argument(
'--redundancy', '-r',
type=str,
required = True,
help="Redundancy of the new volume.")
parser_av.add_argument(
'--nodes', '-N',
type=str,
required = True,
help="Comma-separated list of node IDs, e.g. '11,12,13'.")
parser_av.add_argument(
'--num-master-nodes', '-m',
type=int,
help="Number of master nodes (default: same as nr. of nodes in '--nodes').")
parser_av.add_argument(
'--owner', '-o',
type=str,
required=True,
help="User and group ID, e. g. '1000:10001'.")
parser_av.add_argument(
'--perm', '-p',
type=str,
help="Permissions for the new volume, e. g. 'rw-r--r--' (default: 'rwx------').")
parser_av.add_argument(
'--labels', '-l',
type=str,
help="Comma separated list of labels, e. g. 'best,volume,ever' (default: None).")
parser_av.add_argument(
'--block-size', '-B',
type=int,
help="Block-size in bytes (default: 4096).")
parser_av.add_argument(
'--stripe-size', '-S',
type=int,
help="Stripe-size in bytes (default: 262144).")
parser_av.set_defaults(func=add_volume)
# modify volume command
parser_mv = cmdparser.add_parser(
'modify-volume',
help='Modify an existing volume in EXAConf.')
parser_mv.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_mv.add_argument(
'--name', '-n',
type = str,
required = True,
help='Name of the volume.')
parser_mv.add_argument(
'--owner', '-o',
type = str,
required = False,
help="User and group ID of the volume (e. g. '1000:1001').")
parser_mv.add_argument(
'--size', '-s',
type=str,
help="Size of the volume, e.g. 1TiB, 20GiB, 20000000B, etc..")
parser_mv.add_argument(
'--disk', '-d',
type=str,
help="Disk to be used for the volume.")
parser_mv.add_argument(
'--nodes', '-N',
type=str,
help="Comma-separated list of node IDs, e.g. '11,12,13'.")
parser_mv.add_argument(
'--num-master-nodes', '-m',
type=int,
help="Number of master nodes (default: same as nr. of nodes in '--nodes').")
parser_mv.add_argument(
'--redundancy', '-r',
type=int,
help='Redundancy value.')
parser_mv.add_argument(
'--labels', '-l',
type=str,
help="Comma separated list of labels, e. g. 'best,volume,ever' (default: None).")
parser_mv.set_defaults(func=modify_volume)
# add remote volume command
parser_arv = cmdparser.add_parser(
'add-remote-volume',
help='Add a new volume to EXAConf.')
parser_arv.add_argument(
'exaconf',
type=str,
metavar='EXACONF',
default = '/exa/etc/EXAConf', nargs='?',
help='The EXAConf file')
parser_arv.add_argument(
'--name', '-n',
type = str,
required = True,
help='Name of the remote volume (must be unqiue in the cluster, generated autom. if omitted)')
parser_arv.add_argument(
'--type', '-t',
type=str,
required = True,
help="Type of the volume ('smb', 'ftp' or 's3').")
parser_arv.add_argument(
'--owner', '-o',
type = str,
required = False,
help="User and group ID of the remote volume (e. g. '1000:1001').")
parser_arv.add_argument(
'--id', '-i',
type=int,
help="ID of the volume (automatically generated if omitted).")
parser_arv.add_argument(
'--url', '-u',
type=str,
required = True,
help="URL for the remote volume, e.g. 'smb://my.remote.server/share (default: None).")
parser_arv.add_argument(
'--username', '-U',
type=str,
help="Username for accessing the remote volume' (default: None).")
parser_arv.add_argument(
'--passwd', '-P',
type=str,
help="Password for accessing the remote volume' (default: None).")
parser_arv.add_argument(
'--options', '-f',
type=str,