forked from pbel78/docker-db
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexadt
executable file
·1684 lines (1628 loc) · 66.5 KB
/
exadt
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 sys, os, argparse, pprint, subprocess, time, tarfile, io, docker, ipaddr
from libexadt import exadt_conf, docker_handler, device_handler, docker_rpc_handler, EXAConf, util
from io import BytesIO
import shutil, yaml
from libexadt.EXAConf import config
# Flush STDOUT continuously
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
my_version = "7.1.16"
exaconf="/usr/opt/EXASuite-7/EXAClusterOS-7.1.16/bin/exaconf"
info_arc_prefix = "exasol_docker_info"
confirm_yes = False
quiet_output = False
# {{{ static variables for the "self contained image"
sc_root = os.getenv ('SC_ROOT','/exa')
sc_etc = os.path.join (sc_root, 'etc')
sc_user = 500
sc_group = 500
sc_saas = False
sc_init_sql = None
sc_sys_passwd = None
sc_add_sys_passwd = None
sc_idp_config = None
sc_db_params = None
sc_ui_domain = None
sc_no_db = False
sc_no_odirect = False
sc_cloud_data_volume = None
sc_cache_volume_disk = None
sc_cluster_name = "cl4"
sc_image = "exasol/docker-db:latest" #not relevant
sc_device_type = "file"
sc_license = "/.license.xml"
cos_auth_socket = "/var/run/ecos_unix_auth"
parser = None
parser_isc = None
# }}}
# {{{ Log functions
def log_err(msg):
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}", file=sys.stderr, flush=True)
def log_msg(msg):
if not quiet_output:
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}", file=sys.stdout, flush=True)
# }}}
# {{{ Get script path
def get_script_path():
"""
Returns the absolute path of the exadt script that is currently executed.
"""
script = os.path.realpath(__file__)
# __file__ may point to the .pyc file
if script.endswith(".pyc"):
script = script[:-1]
return script
# }}}
# {{{ Confirm
def confirm(msg):
"""
Asks the user for confirmation.
"""
if confirm_yes:
return True
choice = ""
while choice not in set(['y','Y','n','N']):
choice = input("%s (y/n): " % msg).lower()
return choice == 'y'
# }}}
# {{{ call_exaconf
def call_exaconf(cmd):
"""
Execute exaconf with the given arguments.
"""
exaconf_args = [exaconf,]
for a in cmd.args.split(' '):
if str(a).strip().lower() == 'none':
exaconf_args.append(None)
else:
exaconf_args.append(str(a).strip())
subprocess.call(exaconf_args)
# }}}
# {{{ Cluster started
def cluster_started(root):
"""
Checks if the cluster with the given root has been started
(i. e. there are containers of this cluster).
"""
try:
exaconf = EXAConf.EXAConf(root, False)
except EXAConf.EXAConfError as e:
print(e)
# return False (necessary e. g. if the root-directory has already been deleted)
return False
# EXAConf may be un-inititalized if delete-cluster is
# called directly after create-cluster
if not exaconf.initialized():
return False
try:
# validate manually if initialized (see above)
exaconf.validate()
dh = docker_handler.docker_handler()
dh.set_exaconf(exaconf)
res = dh.cluster_started()
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
return res
# }}}
# {{{ Create cluster
def create_cluster(cmd):
conf = exadt_conf.exadt_conf()
# always us absolute path
root = os.path.abspath(cmd.root)
root = os.path.normpath(root)
# create root-directory if requested
if cmd.create_root:
try:
os.makedirs(root)
except OSError:
if not os.path.isdir(root):
print("Failed to create root-directory '%s'!" % root)
raise
elif not os.path.isdir(root):
print("Root directory '%s' does not exist (or is a file)!" % root)
sys.exit(1)
# create entry in config
try:
conf.create_cluster(cmd.cluster, root)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
# }}}
# {{{ Delete cluster
def delete_cluster(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
# check if has been started (i. e. containers exist)
if cluster_started(root):
print("Cluster '%s' has existing containers. It has to be stopped before it can be deleted!" % cmd.cluster)
sys.exit(1)
# ask for confirmation
msg = "Do you really want to delete cluster '%s'%s? " % \
(cmd.cluster, " (and all file-devices)" if not (cmd.keep_root or cmd.keep_mapped_devices) else "")
if not confirm(msg):
print("Aborted.")
sys.exit(1)
# check if cluster has been initialized (may be un-initialized
# if delete-cluster is called directly after create-cluster)
initialized = False
try:
exaconf = EXAConf.EXAConf(root, False)
initialized = exaconf.initialized()
except EXAConf.EXAConfError as e:
print(e)
# interpret exception as "not initialized" and proceed
# delete root-dir if not prevented
# NOTE : this is done by starting new containers and executing 'rm -rf /exa' because
# some of the files belong to root (due to the privileged mode) and can't
# be deleted if exadt has been called by a normal user.
if initialized and not cmd.keep_root:
print("Deleting root directory '%s'." % root)
try:
dh = docker_handler.docker_handler(quiet=True)
dh.set_exaconf(exaconf)
# -> wait for the 'rm containers' to finish
# (avoids conflicts due to deleting the same files)
dh.start_cluster(cmd = "rm -rf /exa", auto_remove=True, dummy_mode=True,
wait=True, wait_timeout=10)
except docker_handler.DockerError as e:
print("Errors occured while trying to delete the root directory: %s" % e)
# delete the root-directory itself and the node directories
shutil.rmtree(root, True)
# un-initialized clusters have to be deleted without accessing EXAConf
elif not cmd.keep_root:
shutil.rmtree(root, True)
# check if deletion was successfull (may fail if there are root-only files in an unitialized cluster)
if os.path.isdir(root):
print("WARNING: root directory '%s' could not be completely deleted! Please delete it manually before reusing it." % root)
# delete mapped devices if not prevented
# --> we delete the complete '$name' directories (they are created automatically)
# NOTE : we do this AFTER deleting the root directory, otherwise the docker-handler would
# fail to mount the volumes specified in EXAConf (when executing the deletion command)
if initialized and not cmd.keep_mapped_devices:
try:
# validate manually if initalized
exaconf.validate()
nodes_conf = exaconf.get_nodes()
except EXAConf.EXAConfError as e:
print(e)
for node in nodes_conf.values():
for disk in node.disks.values():
if "mapping" in disk:
for dev,path in disk.mapping:
try:
# follow symlink
path = os.path.realpath(path)
# path can also be a file within '$name/'!
if os.path.isfile(path):
path = os.path.dirname(path)
print("Deleting directory '%s'." % path)
shutil.rmtree(path, True)
except OSError as e:
print("Failed to delete '%s': %s." % (path, e))
# delete entry from config
conf.delete_cluster(cmd.cluster)
# }}}
# {{{ Extract versions
def extract_versions(cmd=None, env=None, image=None):
"""
Extracts DB, OS, RE and image version from either:
- the given command,
- the given environment or
- the given docker image
and returns the first occurence of each version number.
"""
db_version = None
os_version = None
re_version = None
img_version = None
# 1. use versions given in the command
if cmd:
if cmd.db_version and cmd.db_version.strip() != "":
db_version = cmd.db_version.strip()
if cmd.os_version and cmd.os_version.strip() != "":
os_version = cmd.os_version.strip()
if cmd.re_version and cmd.re_version.strip() != "":
re_version = cmd.re_version.strip()
if cmd.img_version and cmd.img_version.strip() != "":
img_version = cmd.img_version.strip()
# 2. extract versions from environment
if env:
if not db_version and "EXA_DB_VERSION" in env:
db_version = env["EXA_DB_VERSION"]
if not os_version and "EXA_OS_VERSION" in env:
os_version = env["EXA_OS_VERSION"]
if not re_version and "EXA_RE_VERSION" in env:
re_version = env["EXA_RE_VERSION"]
if not img_version and "EXA_IMG_VERSION" in env:
img_version = env["EXA_IMG_VERSION"]
# 3. extract versions from docker image
if image:
if not db_version or not os_version or not re_version or not img_version:
try:
dh = docker_handler.docker_handler()
ic = dh.get_image_conf(image)
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
if not db_version:
db_version = ic.labels.dbversion
if not os_version:
os_version = ic.labels.osversion
if not re_version:
re_version = ic.labels.reversion
if not img_version:
img_version = ic.labels.version
return (db_version, os_version, re_version, img_version)
# }}}
# {{{ Get versioned image name
def get_versioned_image_name(image):
"""
Replaces 'latest' in the given image name with the actual version.
"""
image = image.strip()
if image.split(":")[1] == "latest":
try:
dh = docker_handler.docker_handler()
ic = dh.get_image_conf(image)
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
image = image.split(":")[0] + ":" + ic.labels.version
return image
# }}}
# {{{ Init cluster
def init_cluster(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
# check if root and license actually exist
if not os.path.isdir(root):
print("Root directory '%s' does not exist (or is a file)!" % root)
sys.exit(1)
if not os.path.isfile(cmd.license):
print("License '%s' does not exist (or is a directory)!" % os.path.abspath(cmd.license))
sys.exit(1)
# check params
if cmd.device_type and cmd.device_type not in ['block','file']:
print("Device-type must be 'block' or 'file'!")
sys.exit(1)
if cmd.auto_storage and cmd.device_type != 'file':
print("'--auto-storage' is only supported for device-type 'file'!")
sys.exit(1)
# check if has been started (i. e. containers exist)
if cluster_started(root):
print("Cluster '%s' has existing containers. It has to be stopped before it can be re-initialized!" % cmd.cluster)
sys.exit(1)
# extract versions
db_version, os_version, re_version, img_version = extract_versions(cmd=cmd,
image=cmd.image)
# check and re-init root if requested
files = os.listdir(root)
if files:
if cmd.force:
for f in files:
name = os.path.join(root, f)
if os.path.isdir(name):
shutil.rmtree(name, True)
else:
os.unlink(name)
else:
print("Root directory '%s' is not empty! Use --force to force re-initialization." % root)
sys.exit(1)
# create EXAConf
try:
exaconf = EXAConf.EXAConf(root, False)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
# create directory structure
for node in range(1, cmd.num_nodes+1):
node_root = os.path.join(root, exaconf.node_root_prefix + str(exaconf.max_reserved_node_id + node))
create_node_dirs(node_root, exaconf)
# initialize EXAConf
exaconf.initialize(cmd.cluster, cmd.image,
cmd.num_nodes, cmd.device_type, cmd.force, "Docker",
db_version = db_version,
os_version = os_version,
re_version = re_version,
img_version = img_version,
license = cmd.license)
# create devices and volumes if requested
if cmd.auto_storage:
try:
devh = device_handler.device_handler(exaconf)
devh.auto_create_file_devices(max_space=util.units2bytes(cmd.max_space) if cmd.max_space else None)
except device_handler.DeviceError as e:
print(e)
sys.exit(1)
print("Successfully initialized root directory '%s'." % root)
# }}}
# {{{ Init sc
def init_sc(cmd):
"""
Called from within a 'standalone-container': creates the container filesystem,
storage devices and EXAConf. Finally calls exainit, which continues the startup
process.
"""
if cmd.config:
args = vars (cmd)
for k, v in args.items ():
if k in ['command', 'config']:
continue
if v is not None and v != parser_isc.get_default (k) and v != parser.get_default (k):
print (f'Sorry, --config is not supposed to work together with other arguments ({k}: {v})')
sys.exit (1)
do_full_init = False
# 1. Create /exa/etc if it does not exist
# --> needed in order to create an exaconf instance
if not os.path.exists(sc_etc):
log_msg("exadt:: '%s' does not exist! Doing full initialization." % sc_etc)
do_full_init = True
os.makedirs(sc_etc)
else:
log_msg("exadt:: '%s' already exists! Doing partial initialization." % sc_etc)
# 2. Determine the local IP adress
log_msg("exadt:: searching for the first interface with state UP")
first_if = util.get_first_interface(timeout=15)
local_ifs = util.get_all_interfaces(timeout=15, up_only=True)
# check if a local IP address has been specified
local_ip = os.environ.get('EXA_NODE_IP_ADDRESS', None)
if local_ip:
local_ip = str(ipaddr.IPNetwork(str(local_ip).strip()).ip)
log_msg("exadt:: using IP address '%s' for node '%s' (from env)." % (local_ip, str(cmd.node_id)))
elif cmd.ip_address:
local_ip = str(ipaddr.IPNetwork(cmd.ip_address).ip)
log_msg("exadt:: using IP address '%s' for node '%s' (from args)." % (local_ip, str(cmd.node_id)))
# 3. Create EXAConf (may be uninitialized)
try:
exaconf = EXAConf.EXAConf(sc_etc, False)
except EXAConf.EXAConfError as e:
log_err(e)
sys.exit(1)
doc_key: str = 'scope'
docs: list = []
if cmd.config:
try:
with open (cmd.config) as fd:
for doc in yaml.load_all (fd, yaml.SafeLoader):
docs.append (doc)
d = vars (cmd)
for k, v in doc.items ():
if k != doc_key:
d.update ({k : v})
except Exception as e:
raise RuntimeError (f'Loading from {cmd.config} failed: {e}')
global sc_user, sc_group, sc_saas, sc_init_sql, sc_sys_passwd, sc_add_sys_passwd, sc_idp_config, sc_ui_domain
global sc_no_db, sc_no_odirect, sc_cloud_data_volume, sc_cache_volume_disk, sc_db_params
if cmd.user: sc_user = cmd.user
if cmd.group: sc_group = cmd.group
if cmd.saas: sc_saas = cmd.saas
if cmd.init_sql: sc_init_sql = cmd.init_sql
if cmd.sys_passwd: sc_sys_passwd = cmd.sys_passwd
if cmd.add_sys_passwd: sc_add_sys_passwd = cmd.add_sys_passwd
if cmd.idp_config: sc_idp_config = cmd.idp_config
if cmd.ui_domain: sc_ui_domain = cmd.ui_domain
if cmd.no_db: sc_no_db = cmd.no_db
if cmd.no_odirect: sc_no_odirect = cmd.no_odirect
if cmd.cloud_data_volume: sc_cloud_data_volume = cmd.cloud_data_volume.split(":", maxsplit = 6)
if cmd.cache_volume_disk: sc_cache_volume_disk = cmd.cache_volume_disk.split(":")
if cmd.db_params: sc_db_params = cmd.db_params
db_version, os_version, re_version, img_version = extract_versions(cmd=cmd,
env=os.environ)
# 4. check compatibility before doing anything else
# --> the persistent volume may need to be updated
if exaconf.initialized():
(c, res) = exaconf.check_update_needed(db_version=db_version,
os_version=os_version,
re_version = re_version,
img_version = img_version)
if c == True:
print("The given docker volume is not compatible with this docker image because of the following version mismatch(es):\n%sPlease update the volume (using 'update-sc')!" % (res))
sys.exit(1)
# 5. Create all other directories if they don't exist
create_node_dirs(sc_root, exaconf)
# 6. copy license to the correct destination
sc_etc_lic = os.path.join(sc_etc, exaconf.license_filename)
if not os.path.exists(sc_etc_lic):
log_msg("exadt:: copying license file '%s' to '%s'" % (repr(sc_license), repr(sc_etc_lic)))
try: shutil.copy(sc_license, sc_etc_lic)
except: log_err("exadt:: copy file '%s' failed" % (repr(sc_license)))
else:
log_msg("exadt:: using license file '%s'" % repr(sc_etc_lic))
# 7. Init EXAConf
# NOTE : the version numbers are taken from the environment,
# the image name is not relevant and the license has already
# been copied to the right place.
if not exaconf.initialized():
log_msg("exadt:: initializing EXAConf")
if cmd.config:
for doc in docs:
method_name = f'initialize_{doc[doc_key]}'
method = getattr (exaconf, method_name)
if callable (method):
args = {k : v for k, v in doc.items () if k != doc_key}
method (args)
else:
raise RuntimeError (f'initializer {method_name} for {doc[doc_key]} is not ready in EXAConf')
exaconf.commit ()
else:
exaconf.initialize(sc_cluster_name, sc_image,
cmd.num_nodes, sc_device_type,
False, "Docker",
license = os.path.join("/exa/etc/", exaconf.license_filename),
db_version = db_version,
os_version = os_version,
img_version = img_version,
add_archive_volume=False,
def_owner = (sc_user, sc_group),
saas = sc_saas, initial_sql = sc_init_sql,
default_sys_passwd_hash = sc_sys_passwd,
additional_sys_passwd_hashes = sc_add_sys_passwd,
idp_config = sc_idp_config, ui_domain = sc_ui_domain,
no_db = sc_no_db, no_odirect = sc_no_odirect,
cloud_data_volume = sc_cloud_data_volume,
cache_volume_disk = sc_cache_volume_disk,
quiet = True, template_mode = cmd.template, db_params = sc_db_params)
# Set local IP in EXAConf
# --> skip in 'template' mode (will probably be replaced anyway)
if not cmd.template:
# use given one (if any)
if local_ip:
local_ip_found = False
for curr_if in local_ifs:
if local_ip == str(ipaddr.IPNetwork(curr_if[1]).ip):
log_msg("exadt:: using interface '%s' with address '%s'." % (curr_if[0], curr_if[1]))
try:
exaconf.set_node_network(cmd.node_id, private=first_if[1])
local_ip_found = True
break
except EXAConf.EXAConfError as e:
log_err(e)
sys.exit(1)
if not local_ip_found:
log_err("exadt:: no interface found with IP address '%s'." % local_ip)
sys.exit(1)
# use first interface in UP state (if none given)
else:
log_msg("exadt:: using interface '%s' with address '%s'." % (first_if[0], first_if[1]))
try:
exaconf.set_node_network(cmd.node_id, private=first_if[1])
except EXAConf.EXAConfError as e:
log_err(e)
sys.exit(1)
# if EXAConf is already initialized, we compare the current local IP
# with the one in the EXAConf or change it (if a local address has been
# specified)
elif not cmd.template:
# change local IP first, if requested
if local_ip:
log_msg("exadt:: changing IP address in EXAConf to '%s'." % local_ip)
try:
exaconf.set_node_ip(cmd.node_id, private=local_ip)
except EXAConf.EXAConfError as e:
log_err(e)
sys.exit(1)
# check IP address against local interfaces ('UP' state only)
nodes_conf = exaconf.get_nodes()
if cmd.node_id in nodes_conf:
priv_net = nodes_conf[cmd.node_id].private_net
priv_net_up = False
for up_if in local_ifs:
# NOTE: we only compare the IP address (without the network mask),
# in order to support weird network configurations like "192.168.10.10/32"
# (used within Google Cloud instances)
if ipaddr.IPNetwork(up_if[1]).ip == ipaddr.IPNetwork(priv_net).ip:
priv_net_up = True
break
if not priv_net_up:
log_err("exadt::ERROR: the private interface with address '%s' either does not exist or is down."
% (priv_net))
sys.exit(1)
else:
log_err("exadt::ERROR: can't find node ID '%s' in EXAConf." % cmd.node_id)
sys.exit(1)
# 8. set root password if requested
if cmd.root_passwd:
root_conf = exaconf.get_users()['root']
root_conf.passwd = cmd.root_passwd
exaconf.set_user_conf(root_conf, 'root', encode_passwd = cmd.encode_passwd)
# 9. set and export hostname
hostname = "n" + str(cmd.node_id)
nodes_conf = exaconf.get_nodes()
if cmd.node_id in nodes_conf:
hostname = nodes_conf[cmd.node_id].name
log_msg("exadt:: setting hostname to '%s'" % hostname)
os.environ['EXA_NODE_ID'] = str(cmd.node_id)
os.environ['HOSTNAME'] = hostname
subprocess.call(['/bin/hostname', hostname])
# 10. remove authentication socket if it exists
if os.path.exists(cos_auth_socket) and os.getpid() == 1:
log_msg("exadt:: removing '%s'" % cos_auth_socket)
os.remove(cos_auth_socket)
# 11. create storage devices
if do_full_init and not cmd.template:
log_msg("exadt:: creating EXAStorage devices")
try:
devh = device_handler.device_handler(exaconf)
devh.auto_create_file_devices(container_internal=True, no_odirect = sc_no_odirect)
except device_handler.DeviceError as e:
log_err(e)
sys.exit(1)
exaconf.commit()
# 12. call exainit
# --> or exit if requested
if cmd.template:
if cmd.print_conf:
log_msg("exadt:: template mode: printing EXAConf and exiting:\n")
exaconf.config.write(outfile=sys.stdout.buffer)
sys.exit(0)
else:
init_cmd = exaconf.get_init_cmd()
log_msg("exadt:: calling '%s'" % str(init_cmd))
os.execv(init_cmd[0], init_cmd[1])
# }}}
# {{{ Create node dirs
def create_node_dirs(node_root, exaconf):
"""
Creates all default directories in the given node root directory.
"""
if not os.path.exists(node_root):
os.makedirs(node_root)
if not os.path.exists(os.path.join(node_root, exaconf.etc_dir)):
os.makedirs(os.path.join(node_root, exaconf.etc_dir))
if not os.path.exists(os.path.join(node_root, exaconf.tmp_dir)):
os.makedirs(os.path.join(node_root, exaconf.tmp_dir))
if not os.path.exists(os.path.join(node_root, exaconf.support_dir)):
os.makedirs(os.path.join(node_root, exaconf.support_dir))
if not os.path.exists(os.path.join(node_root, exaconf.spool_dir)):
os.makedirs(os.path.join(node_root, exaconf.spool_dir))
if not os.path.exists(os.path.join(node_root, exaconf.sync_dir)):
os.makedirs(os.path.join(node_root, exaconf.sync_dir))
if not os.path.exists(os.path.join(node_root, exaconf.coredump_dir)):
os.makedirs(os.path.join(node_root, exaconf.coredump_dir))
if not os.path.exists(os.path.join(node_root, exaconf.job_dir)):
os.makedirs(os.path.join(node_root, exaconf.job_dir))
if not os.path.exists(os.path.join(node_root, exaconf.job_queue_dir)):
os.makedirs(os.path.join(node_root, exaconf.job_queue_dir))
if not os.path.exists(os.path.join(node_root, exaconf.job_run_dir)):
os.makedirs(os.path.join(node_root, exaconf.job_run_dir))
if not os.path.exists(os.path.join(node_root, exaconf.job_finish_dir)):
os.makedirs(os.path.join(node_root, exaconf.job_finish_dir))
if not os.path.exists(os.path.join(node_root, exaconf.job_archive_dir)):
os.makedirs(os.path.join(node_root, exaconf.job_archive_dir))
if not os.path.exists(os.path.join(node_root, exaconf.conf_ssl_dir)):
os.makedirs(os.path.join(node_root, exaconf.conf_ssl_dir))
if not os.path.exists(os.path.join(node_root, exaconf.conf_dwad_dir)):
os.makedirs(os.path.join(node_root, exaconf.conf_dwad_dir))
if not os.path.exists(os.path.join(node_root, exaconf.conf_remote_volumes_dir)):
os.makedirs(os.path.join(node_root, exaconf.conf_remote_volumes_dir))
if not os.path.exists(os.path.join(node_root, exaconf.md_dir)):
os.makedirs(os.path.join(node_root, exaconf.md_dir))
if not os.path.exists(os.path.join(node_root, exaconf.md_storage_dir)):
os.makedirs(os.path.join(node_root, exaconf.md_storage_dir))
if not os.path.exists(os.path.join(node_root, exaconf.md_dwad_dir)):
os.makedirs(os.path.join(node_root, exaconf.md_dwad_dir))
if not os.path.exists(os.path.join(node_root, exaconf.log_dir)):
os.makedirs(os.path.join(node_root, exaconf.log_dir))
if not os.path.exists(os.path.join(node_root, exaconf.logd_dir)):
os.makedirs(os.path.join(node_root, exaconf.logd_dir))
if not os.path.exists(os.path.join(node_root, exaconf.cored_log_dir)):
os.makedirs(os.path.join(node_root, exaconf.cored_log_dir))
if not os.path.exists(os.path.join(node_root, exaconf.db_log_dir)):
os.makedirs(os.path.join(node_root, exaconf.db_log_dir))
if not os.path.exists(os.path.join(node_root, exaconf.data_dir)):
os.makedirs(os.path.join(node_root, exaconf.data_dir))
if not os.path.exists(os.path.join(node_root, exaconf.bucketfs_dir)):
os.makedirs(os.path.join(node_root, exaconf.bucketfs_dir))
if not os.path.exists(os.path.join(node_root, exaconf.storage_dir)):
os.makedirs(os.path.join(node_root, exaconf.storage_dir))
if not os.path.exists(os.path.join(node_root, exaconf.bucketfs_dir, exaconf.def_bucketfs)):
os.makedirs(os.path.join(node_root, exaconf.bucketfs_dir, exaconf.def_bucketfs))
if not os.path.exists(os.path.join(node_root, exaconf.docker_log_dir)):
os.makedirs(os.path.join(node_root, exaconf.docker_log_dir))
if not os.path.exists(os.path.join(node_root, exaconf.syslog_dir)):
os.makedirs(os.path.join(node_root, exaconf.syslog_dir))
if not os.path.exists(os.path.join(node_root, exaconf.device_pool_dir)):
os.makedirs(os.path.join(node_root, exaconf.device_pool_dir))
# }}}
# {{{ List clusters
def list_clusters(cmd):
conf = exadt_conf.exadt_conf()
clusters = conf.get_clusters()
if len(clusters) == 0:
print("No clusters found in %s." % conf.get_conf_paths())
sys.exit(0)
# build dict containg all information
width = [len("CLUSTER"), len("ROOT"), len("IMAGE NAME"), len("IMAGE VERSION"), len("DB VERSION"), len("OS VERSION")]
clusters_info = config()
# quiet output
if quiet_output is True:
for cluster in clusters:
print(cluster)
return
# normal output
for cluster,root in clusters.items():
ci = config({"root" : os.path.normpath(root),
"image" : "<uninitialized>",
"version" : "<unknown>",
"db_version" : "<unknown>",
"os_version" : "<unknown>"})
#cluster may be uninitialized
try:
exaconf = EXAConf.EXAConf(root, True)
ci.image = exaconf.get_docker_image()
except EXAConf.EXAConfError:
pass
#query image info if it's initialized
if ci.image != "<uninitialized>":
try:
dh = docker_handler.docker_handler()
ic = dh.get_image_conf(ci.image)
ci.version = ic.labels.version
ci.db_version = ic.labels.dbversion
ci.os_version = ic.labels.osversion
except docker_handler.DockerError as e:
print(e)
clusters_info[cluster] = ci
# update column widths
width[0] = max(width[0], len(cluster))
width[1] = max(width[1], len(ci.root))
width[2] = max(width[2], len(ci.image))
width[3] = max(width[3], len(ci.version))
width[4] = max(width[4], len(ci.db_version))
width[5] = max(width[5], len(ci.os_version))
print(" %- *s %- *s %- *s %- *s %- *s %- *s" % (width[0], "CLUSTER",
width[1], "ROOT",
width[2], "IMAGE NAME",
width[3], "IMAGE VERSION",
width[4], "DB VERSION",
width[5], "OS VERSION"))
for cluster,ci in clusters_info.items():
print(" %- *s %- *s %- *s %- *s %- *s %- *s" % (width[0], cluster,
width[1], ci.root,
width[2], ci.image,
width[3], ci.version,
width[4], ci.db_version,
width[5], ci.os_version))
# }}}
# {{{ Docker version
def docker_version(cmd):
try:
dh = docker_handler.docker_handler(verbose=True)
print("====== Docker Version ======")
pp = pprint.PrettyPrinter()
pp.pprint(dh.version())
print("====== Docker-py Version ======")
print(docker.version)
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
# }}}
# {{{ Start cluster
def start_cluster(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
try:
exaconf = EXAConf.EXAConf(root, True)
# Check for compatibility between the current EXAConf and the image
# (only done before starting a cluster because all other operations
# still have to work, especially the update)
(c, ev, iv) = exaconf.check_img_compat()
if c == False:
print("EXAConf version (%s) is not compatible with image version (%s). Please update your installation!" % (ev, iv))
sys.exit(1)
# check if the volumes all have disks assigned
for name,vol in exaconf.get_volumes().items():
if vol.disk is None:
print("Volume '%s' has no disk! Aborting cluster startup." % name)
sys.exit(1)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
# call docker handler
try:
dh = docker_handler.docker_handler(verbose=cmd.verbose)
dh.set_exaconf(exaconf)
dh.start_cluster(cmd = cmd.command)
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
# }}}
# {{{ Stop cluster
def stop_cluster(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
try:
exaconf = EXAConf.EXAConf(root, True)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
try:
dh = docker_handler.docker_handler(verbose=cmd.verbose)
dh.set_exaconf(exaconf)
# merge EXAConf right before stopping the system,
# just to make sure that we get the internal changes,
# even if the shutdown fails
dh.merge_exaconf(allow_self = False, force = True)
if dh.cluster_online():
drh = docker_rpc_handler.docker_rpc_handler(exaconf, quiet=True)
print("Stopping database(s)...", end=' ')
drh.stop_database()
print("successful")
dh.stop_cluster(cmd.timeout)
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
# }}}
# {{{ Update cluster
def update_cluster(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
# check if has been started (i. e. containers exist)
if cluster_started(root):
print("Cluster '%s' has existing containers. It has to be stopped before it can be updated!" % cmd.cluster)
sys.exit(1)
# extract new versions
new_db_version, new_os_version, new_re_version, new_img_version = extract_versions(cmd=cmd,
image=cmd.image)
# update image and versions
try:
exaconf = EXAConf.EXAConf(root, True)
old_docker_image = exaconf.get_docker_image()
old_db_version = exaconf.get_db_version()
old_os_version = exaconf.get_os_version()
old_re_version = exaconf.get_re_version()
old_img_version = exaconf.get_img_version()
old_file_version = exaconf.get_file_version()
exaconf.update_self()
exaconf.update_docker_image(cmd.image)
exaconf.update_db_version(new_db_version)
exaconf.update_os_version(new_os_version)
exaconf.update_re_version(new_re_version)
exaconf.update_img_version(new_img_version)
nodes_conf = exaconf.get_nodes()
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
# create missing directories
for node_conf in nodes_conf.values():
create_node_dirs(node_conf.docker_volume, exaconf)
# print update info
col_width = len(old_docker_image)
print("Cluster '%s' has been successfully updated!" % cmd.cluster)
print("- Image name : %-*s --> %s" % (col_width, old_docker_image, exaconf.get_docker_image()))
print("- Image ver. : %-*s --> %s" % (col_width, old_img_version, exaconf.get_img_version()))
print("- DB ver. : %-*s --> %s" % (col_width, old_db_version, exaconf.get_db_version()))
print("- OS ver. : %-*s --> %s" % (col_width, old_os_version, exaconf.get_os_version()))
print("- RE ver. : %-*s --> %s" % (col_width, old_re_version, exaconf.get_re_version()))
print("- EXAConf : %-*s --> %s" % (col_width, old_file_version, exaconf.get_file_version()))
print("Restart the cluster in order to apply the changes.")
# }}}
# {{{ Update sc
def update_sc(cmd):
"""
Update a 'standalone-container' (called from within the running container)
NOTE : the image name is not modified because it is ignored anyway for standalone-containers.
"""
new_db_version, new_os_version, new_re_version, new_img_version = extract_versions(cmd=cmd,
env=os.environ)
# update image and versions
try:
exaconf = EXAConf.EXAConf(sc_etc, True)
old_db_version = exaconf.get_db_version()
old_os_version = exaconf.get_os_version()
old_re_version = exaconf.get_re_version()
old_img_version = exaconf.get_img_version()
old_file_version = exaconf.get_file_version()
exaconf.update_self()
exaconf.update_db_version(new_db_version)
exaconf.update_os_version(new_os_version)
exaconf.update_re_version(new_re_version)
exaconf.update_img_version(new_img_version)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
col_width = len(old_img_version)
print("Container has been successfully updated!")
print("- Image ver. : %-*s --> %s" % (col_width, old_img_version, exaconf.get_img_version()))
print("- DB ver. : %-*s --> %s" % (col_width, old_db_version, exaconf.get_db_version()))
print("- OS ver. : %-*s --> %s" % (col_width, old_os_version, exaconf.get_os_version()))
print("- RE ver. : %-*s --> %s" % (col_width, old_re_version, exaconf.get_re_version()))
print("- EXAConf : %-*s --> %s" % (col_width, old_file_version, exaconf.get_file_version()))
# }}}
# {{{ ps
def ps(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
try:
exaconf = EXAConf.EXAConf(root, True)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
try:
dh = docker_handler.docker_handler()
dh.set_exaconf(exaconf)
containers = dh.get_containers()
except docker_handler.DockerError as e:
print(e)
sys.exit(1)
# not running?
if len(containers) == 0:
print("No containers found for cluster '%s'." % cmd.cluster)
sys.exit(0)
# verbose output
if cmd.verbose:
pprint.pprint(containers)
# create list containing the column widths (use max. len of each column entry)
width = [0] * 7
width[0] = max(max(len(c['Labels']['NodeID']) for c in containers), len("NODE_ID"))
width[1] = max(max(len(c['Status']) for c in containers), len("STATUS"))
width[2] = max(max(len(c['Labels']['version']) for c in containers), len("IMAGE VERSION"))
width[3] = max(max(len(c['Labels']['Name']) for c in containers), len("NAME"))
width[4] = 12
width[5] = max(max(len(c['Names'][0].lstrip("/")) for c in containers), len("CONTAINER NAME"))
width[6] = 0
for c in containers:
ports = [ (d["PublicPort"], d["PrivatePort"]) for d in c['Ports'] if 'PublicPort' in d]
ports_str = ",".join([str(p[0]) + "->" + str(p[1]) for p in ports])
if len(ports_str) > width[6]:
width[6] = len(ports_str)
width[6] = max(width[6], len("EXPOSED PORTS"))
print(" %- *s %- *s %- *s %- *s %- *s %- *s %- *s" % (width[0], "NODE ID",
width[1], "STATUS",
width[2], "IMAGE VERSION",
width[3], "NAME",
width[4], "CONTAINER ID",
width[5], "CONTAINER NAME",
width[6], "EXPOSED PORTS"))
for c in containers:
ports = [ (d["PublicPort"], d["PrivatePort"]) for d in c['Ports'] if 'PublicPort' in d]
ports_str = ",".join([str(p[0]) + "->" + str(p[1]) for p in ports])
print(" %- *s %- *s %- *s %- *s %- .*s %- *s %- *s" % (width[0], c['Labels']['NodeID'],
width[1], c['Status'],
width[2], c['Labels']['version'],
width[3], c['Labels']['Name'],
width[4], c['Id'],
width[5], c['Names'][0].lstrip("/"),
width[6], ports_str))
# }}}
# {{{ Create file devices
def create_file_devices(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
try:
exaconf = EXAConf.EXAConf(root, True)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
try:
byte_size = util.units2bytes(cmd.size)
except RuntimeError as e:
print("ERROR:: %s" % e)
sys.exit(1)
# check if path does exist (do NOT create it)
if cmd.path and not os.path.exists(cmd.path):
print("ERROR:: Given path '%s' does not exist!" % cmd.path)
sys.exit(1)
# ask user for confirmation if existing devices should be replaced
if cmd.replace and not confirm("Do you really want to replace all file-devices of cluster '%s'?" % cmd.cluster):
print("Aborted")
sys.exit(0)
try:
devh = device_handler.device_handler(exaconf)
devices = devh.create_file_devices(cmd.disk, cmd.num, byte_size, cmd.path, cmd.replace)
except device_handler.DeviceError as e:
print(e)
sys.exit(1)
if devices:
if len(devices[1]) > 0:
print("The following file devices have been removed:")
for nid in list(devices[1].keys()):
print("Node %s : %s" % (nid, devices[1][nid]))
if len(devices[0]) > 0:
print("Successfully created the following file devices:")
for nid in list(devices[0].keys()):
print("Node %s : %s" % (nid, devices[0][nid]))
# }}}
# {{{ execute
def execute(cmd):
try:
conf = exadt_conf.exadt_conf()
root = conf.get_root(cmd.cluster)
except exadt_conf.ConfError as e:
print(e)
sys.exit(1)
try:
exaconf = EXAConf.EXAConf(root, True)
except EXAConf.EXAConfError as e:
print(e)
sys.exit(1)
try:
dh = docker_handler.docker_handler()