forked from red-hat-storage/ocs-ci
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
2139 lines (1859 loc) · 73 KB
/
Copy pathutils.py
File metadata and controls
2139 lines (1859 loc) · 73 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
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
import datetime
import json
import logging
import os
import pickle
import re
import threading
import tarfile
import time
import traceback
import subprocess
import shlex
import shutil
from subprocess import TimeoutExpired
from concurrent.futures import ThreadPoolExecutor, as_completed
import yaml
from gevent import sleep
from pathlib import Path
from libcloud.common.exceptions import BaseHTTPError
from libcloud.common.types import LibcloudError
from libcloud.compute.providers import get_driver
from libcloud.compute.types import Provider
from paramiko.ssh_exception import SSHException
from ocs_ci.framework import config as ocsci_config, config
from ocs_ci.ocs import constants, defaults
from ocs_ci.ocs.external_ceph import RolesContainer, Ceph, CephNode
from ocs_ci.ocs.clients import WinNode
from ocs_ci.ocs.exceptions import (
CommandFailed,
ExternalClusterDetailsException,
ResourceNotFoundError,
UnexpectedBehaviour,
)
from ocs_ci.ocs.ocp import OCP, get_images
from ocs_ci.ocs.openstack import CephVMNode
from ocs_ci.ocs.parallel import parallel
from ocs_ci.ocs.resources.ocs import OCS
from ocs_ci.utility import templating, version
from ocs_ci.utility.prometheus import PrometheusAPI
from ocs_ci.utility.retry import retry
from ocs_ci.utility.utils import (
create_directory_path,
exec_nb_db_query,
mirror_image,
run_cmd,
get_oadp_version,
get_acm_version,
)
from ocs_ci.utility.version import (
get_dr_hub_operator_version,
get_dr_cluster_operator_version,
get_odf_multicluster_orchestrator_version,
get_ocp_gitops_operator_version,
get_submariner_operator_version,
get_volsync_operator_version,
)
log = logging.getLogger(__name__)
mg_fail_count = 0
mg_skip_count = 0
mg_last_fail = None
mg_collected_logs = 0
mg_collected_types = set()
mg_lock = threading.Lock()
subctl_lock = threading.Lock()
def create_ceph_nodes(cluster_conf, inventory, osp_cred, run_id, instances_name=None):
osp_glbs = osp_cred.get("globals")
os_cred = osp_glbs.get("openstack-credentials")
params = dict()
ceph_cluster = cluster_conf.get("ceph-cluster")
if ceph_cluster.get("inventory"):
inventory_path = os.path.abspath(ceph_cluster.get("inventory"))
with open(inventory_path, "r") as inventory_stream:
inventory = yaml.safe_load(inventory_stream)
params["cloud-data"] = inventory.get("instance").get("setup")
params["username"] = os_cred["username"]
params["password"] = os_cred["password"]
params["auth-url"] = os_cred["auth-url"]
params["auth-version"] = os_cred["auth-version"]
params["tenant-name"] = os_cred["tenant-name"]
params["service-region"] = os_cred["service-region"]
params["keypair"] = os_cred.get("keypair", None)
ceph_nodes = dict()
if inventory.get("instance").get("create"):
if ceph_cluster.get("image-name"):
params["image-name"] = ceph_cluster.get("image-name")
else:
params["image-name"] = (
inventory.get("instance").get("create").get("image-name")
)
params["cluster-name"] = ceph_cluster.get("name")
params["vm-size"] = inventory.get("instance").get("create").get("vm-size")
if params.get("root-login") is False:
params["root-login"] = False
else:
params["root-login"] = True
with parallel() as p:
for node in range(1, 100):
node = "node" + str(node)
if not ceph_cluster.get(node):
break
node_dict = ceph_cluster.get(node)
node_params = params.copy()
node_params["role"] = RolesContainer(node_dict.get("role"))
role = node_params["role"]
user = os.getlogin()
if instances_name:
node_params["node-name"] = "{}-{}-{}-{}-{}".format(
node_params.get("cluster-name", "ceph"),
instances_name,
run_id,
node,
"+".join(role),
)
else:
node_params["node-name"] = "{}-{}-{}-{}-{}".format(
node_params.get("cluster-name", "ceph"),
user,
run_id,
node,
"+".join(role),
)
if node_dict.get("no-of-volumes"):
node_params["no-of-volumes"] = node_dict.get("no-of-volumes")
node_params["size-of-disks"] = node_dict.get("disk-size")
if node_dict.get("image-name"):
node_params["image-name"] = node_dict.get("image-name")
if node_dict.get("cloud-data"):
node_params["cloud-data"] = node_dict.get("cloud-data")
p.spawn(setup_vm_node, node, ceph_nodes, **node_params)
log.info("Done creating nodes")
return ceph_nodes
def setup_vm_node(node, ceph_nodes, **params):
ceph_nodes[node] = CephVMNode(**params)
def get_openstack_driver(yaml):
OpenStack = get_driver(Provider.OPENSTACK)
glbs = yaml.get("globals")
os_cred = glbs.get("openstack-credentials")
username = os_cred["username"]
password = os_cred["password"]
auth_url = os_cred["auth-url"]
auth_version = os_cred["auth-version"]
tenant_name = os_cred["tenant-name"]
service_region = os_cred["service-region"]
driver = OpenStack(
username,
password,
ex_force_auth_url=auth_url,
ex_force_auth_version=auth_version,
ex_tenant_name=tenant_name,
ex_force_service_region=service_region,
ex_domain_name="redhat.com",
)
return driver
def cleanup_ceph_nodes(osp_cred, pattern=None, timeout=300):
user = os.getlogin()
name = pattern if pattern else "-{user}-".format(user=user)
driver = get_openstack_driver(osp_cred)
timeout = datetime.timedelta(seconds=timeout)
with parallel() as p:
for node in driver.list_nodes():
if name in node.name:
for ip in node.public_ips:
log.info("removing ip %s from node %s", ip, node.name)
driver.ex_detach_floating_ip_from_node(node, ip)
starttime = datetime.datetime.now()
log.info(
"Destroying node {node_name} with {timeout} timeout".format(
node_name=node.name, timeout=timeout
)
)
while True:
try:
p.spawn(node.destroy)
break
except AttributeError:
if datetime.datetime.now() - starttime > timeout:
raise RuntimeError(
"Failed to destroy node {node_name} with {timeout} timeout:\n{stack_trace}".format(
node_name=node.name,
timeout=timeout,
stack_trace=traceback.format_exc(),
)
)
else:
sleep(1)
sleep(5)
with parallel() as p:
for fips in driver.ex_list_floating_ips():
if fips.node_id is None:
log.info("Releasing ip %s", fips.ip_address)
driver.ex_delete_floating_ip(fips)
with parallel() as p:
errors = {}
for volume in driver.list_volumes():
if volume.name is None:
log.info("Volume has no name, skipping")
elif name in volume.name:
log.info("Removing volume %s", volume.name)
sleep(10)
try:
volume.destroy()
except BaseHTTPError as e:
log.error(e, exc_info=True)
errors.update({volume.name: e.message})
if errors:
for vol, err in errors.items():
log.error("Error destroying {vol}: {err}".format(vol=vol, err=err))
raise RuntimeError(
"Encountered errors during volume deletion. Volume names and messages have been logged."
)
def keep_alive(ceph_nodes):
for node in ceph_nodes:
node.exec_command(cmd="uptime", check_ec=False)
def setup_repos(ceph, base_url, installer_url=None):
repos = ["MON", "OSD", "Tools", "Calamari", "Installer"]
base_repo = generate_repo_file(base_url, repos)
base_file = ceph.write_file(
sudo=True, file_name="/etc/yum.repos.d/rh_ceph.repo", file_mode="w"
)
base_file.write(base_repo)
base_file.flush()
if installer_url is not None:
installer_repos = ["Agent", "Main", "Installer"]
inst_repo = generate_repo_file(installer_url, installer_repos)
log.info("Setting up repo on %s", ceph.hostname)
inst_file = ceph.write_file(
sudo=True, file_name="/etc/yum.repos.d/rh_ceph_inst.repo", file_mode="w"
)
inst_file.write(inst_repo)
inst_file.flush()
def check_ceph_healthly(ceph_mon, num_osds, num_mons, mon_container=None, timeout=300):
"""
Function to check ceph is in healthy state
Args:
ceph_mon (CephNode): monitor node
num_osds (int): number of osds in cluster
num_mons (int): number of mons in cluster
mon_container (str): monitor container name if monitor is placed in
the container
timeout: 300 seconds(default) max time to check if cluster is not
healthy within timeout period return 1
Returns:
int: returns 0 when ceph is in healthy state otherwise returns 1
"""
timeout = datetime.timedelta(seconds=timeout)
starttime = datetime.datetime.now()
lines = None
pending_states = ["peering", "activating", "creating"]
valid_states = ["active+clean"]
while datetime.datetime.now() - starttime <= timeout:
if mon_container:
out, err = ceph_mon.exec_command(
cmd="sudo docker exec {container} ceph -s".format(
container=mon_container
)
)
else:
out, err = ceph_mon.exec_command(cmd="sudo ceph -s")
lines = out.read().decode()
if not any(state in lines for state in pending_states):
if all(state in lines for state in valid_states):
break
sleep(5)
log.info(lines)
if not all(state in lines for state in valid_states):
log.error("Valid States are not found in the health check")
return 1
match = re.search(r"(\d+)\s+osds:\s+(\d+)\s+up,\s+(\d+)\s+in", lines)
all_osds = int(match.group(1))
up_osds = int(match.group(2))
in_osds = int(match.group(3))
if num_osds != all_osds:
log.error("Not all osd's are up. %s / %s" % (num_osds, all_osds))
return 1
if up_osds != in_osds:
log.error("Not all osd's are in. %s / %s" % (up_osds, all_osds))
return 1
# attempt luminous pattern first, if it returns none attempt jewel pattern
match = re.search(r"(\d+) daemons, quorum", lines)
if not match:
match = re.search(r"(\d+) mons at", lines)
all_mons = int(match.group(1))
if all_mons != num_mons:
log.error("Not all monitors are in cluster")
return 1
if "HEALTH_ERR" in lines:
log.error("HEALTH in ERROR STATE")
return 1
return 0
def generate_repo_file(base_url, repos):
return Ceph.generate_repository_file(base_url, repos)
def get_iso_file_url(base_url):
return Ceph.get_iso_file_url(base_url)
def create_ceph_conf(
fsid,
mon_hosts,
pg_num="128",
pgp_num="128",
size="2",
auth="cephx",
pnetwork="172.16.0.0/12",
jsize="1024",
):
fsid = "fsid = " + fsid + "\n"
mon_init_memb = "mon initial members = "
mon_host = "mon host = "
public_network = "public network = " + pnetwork + "\n"
auth = "auth cluster required = cephx\nauth service \
required = cephx\nauth client required = cephx\n"
jsize = "osd journal size = " + jsize + "\n"
size = "osd pool default size = " + size + "\n"
pgnum = "osd pool default pg num = " + pg_num + "\n"
pgpnum = "osd pool default pgp num = " + pgp_num + "\n"
for mhost in mon_hosts:
mon_init_memb = mon_init_memb + mhost.shortname + ","
mon_host = mon_host + mhost.internal_ip + ","
mon_init_memb = mon_init_memb[:-1] + "\n"
mon_host = mon_host[:-1] + "\n"
conf = "[global]\n"
conf = (
conf
+ fsid
+ mon_init_memb
+ mon_host
+ public_network
+ auth
+ size
+ jsize
+ pgnum
+ pgpnum
)
return conf
def setup_deb_repos(node, ubuntu_repo):
node.exec_command(cmd="sudo rm -f /etc/apt/sources.list.d/*")
repos = ["MON", "OSD", "Tools"]
for repo in repos:
cmd = (
"sudo echo deb "
+ ubuntu_repo
+ "/{0}".format(repo)
+ " $(lsb_release -sc) main"
)
node.exec_command(cmd=cmd + " > " + "/tmp/{0}.list".format(repo))
node.exec_command(
cmd="sudo cp /tmp/{0}.list /etc/apt/sources.list.d/".format(repo)
)
ds_keys = [
"https://www.redhat.com/security/897da07a.txt",
"https://www.redhat.com/security/f21541eb.txt",
# 'https://prodsec.redhat.com/keys/00da75f2.txt',
# TODO: replace file file.rdu.redhat.com/~kdreyer with prodsec.redhat.com when it's back
"http://file.rdu.redhat.com/~kdreyer/keys/00da75f2.txt",
"https://www.redhat.com/security/data/fd431d51.txt",
]
for key in ds_keys:
wget_cmd = "sudo wget -O - " + key + " | sudo apt-key add -"
node.exec_command(cmd=wget_cmd)
node.exec_command(cmd="sudo apt-get update")
def setup_deb_cdn_repo(node, build=None):
user = "redhat"
passwd = "OgYZNpkj6jZAIF20XFZW0gnnwYBjYcmt7PeY76bLHec9"
num = build.split(".")[0]
cmd = (
"umask 0077; echo deb https://{user}:{passwd}@rhcs.download.redhat.com/{num}-updates/Tools "
"$(lsb_release -sc) main | tee /etc/apt/sources.list.d/Tools.list".format(
user=user, passwd=passwd, num=num
)
)
node.exec_command(sudo=True, cmd=cmd)
node.exec_command(
sudo=True,
cmd="wget -O - https://www.redhat.com/security/fd431d51.txt | apt-key add -",
)
node.exec_command(sudo=True, cmd="apt-get update")
def setup_cdn_repos(ceph_nodes, build=None):
repos_13x = [
"rhel-7-server-rhceph-1.3-mon-rpms",
"rhel-7-server-rhceph-1.3-osd-rpms",
"rhel-7-server-rhceph-1.3-calamari-rpms",
"rhel-7-server-rhceph-1.3-installer-rpms",
"rhel-7-server-rhceph-1.3-tools-rpms",
]
repos_20 = [
"rhel-7-server-rhceph-2-mon-rpms",
"rhel-7-server-rhceph-2-osd-rpms",
"rhel-7-server-rhceph-2-tools-rpms",
"rhel-7-server-rhscon-2-agent-rpms",
"rhel-7-server-rhscon-2-installer-rpms",
"rhel-7-server-rhscon-2-main-rpms",
]
repos_30 = [
"rhel-7-server-rhceph-3-mon-rpms",
"rhel-7-server-rhceph-3-osd-rpms",
"rhel-7-server-rhceph-3-tools-rpms",
"rhel-7-server-extras-rpms",
]
repos = None
if build.startswith("1"):
repos = repos_13x
elif build.startswith("2"):
repos = repos_20
elif build.startswith("3"):
repos = repos_30
with parallel() as p:
for node in ceph_nodes:
p.spawn(set_cdn_repo, node, repos)
def set_cdn_repo(node, repos):
for repo in repos:
node.exec_command(
sudo=True, cmd="subscription-manager repos --enable={r}".format(r=repo)
)
# node.exec_command(sudo=True, cmd='subscription-manager refresh')
def update_ca_cert(node, cert_url, timeout=120):
if node.pkg_type == "deb":
cmd = "cd /usr/local/share/ca-certificates/ && {{ sudo curl -OL {url} ; cd -; }}".format(
url=cert_url
)
node.exec_command(cmd=cmd, timeout=timeout)
node.exec_command(cmd="sudo update-ca-certificates", timeout=timeout)
else:
cmd = "cd /etc/pki/ca-trust/source/anchors && {{ sudo curl -OL {url} ; cd -; }}".format(
url=cert_url
)
node.exec_command(cmd=cmd, timeout=timeout)
node.exec_command(cmd="sudo update-ca-trust extract", timeout=timeout)
def write_docker_daemon_json(json_text, node):
"""
Write given string to /etc/docker/daemon/daemon
Args:
json_text: json string
node (ceph.ceph.CephNode): Ceph node object
"""
node.write_docker_daemon_json(json_text)
def search_ethernet_interface(ceph_node, ceph_node_list):
"""
Search interface on the given node node which allows every node in the cluster accesible by it's shortname.
Args:
ceph_node (ceph.ceph.CephNode): node where check is performed
ceph_node_list(list): node list to check
"""
return ceph_node.search_ethernet_interface(ceph_node_list)
def open_firewall_port(ceph_node, port, protocol):
"""
Opens firewall ports for given node
Args:
ceph_node (ceph.ceph.CephNode): ceph node
port (str): port
protocol (str): protocol
"""
ceph_node.open_firewall_port(port, protocol)
def config_ntp(ceph_node):
ceph_node.exec_command(
cmd="sudo sed -i '/server*/d' /etc/ntp.conf", long_running=True
)
ceph_node.exec_command(
cmd="echo 'server clock.corp.redhat.com iburst' | sudo tee -a /etc/ntp.conf",
long_running=True,
)
ceph_node.exec_command(cmd="sudo ntpd -gq", long_running=True)
ceph_node.exec_command(cmd="sudo systemctl enable ntpd", long_running=True)
ceph_node.exec_command(cmd="sudo systemctl start ntpd", long_running=True)
def get_ceph_versions(ceph_nodes, containerized=False):
"""
Log and return the ceph or ceph-ansible versions for each node in the cluster.
Args:
ceph_nodes: nodes in the cluster
containerized: is the cluster containerized or not
Returns:
A dict of the name / version pair for each node or container in the cluster
"""
versions_dict = {}
for node in ceph_nodes:
try:
if node.role == "installer":
if node.pkg_type == "rpm":
out, rc = node.exec_command(cmd="rpm -qa | grep ceph-ansible")
else:
out, rc = node.exec_command(cmd="dpkg -s ceph-ansible")
output = out.read().decode().rstrip()
log.info(output)
versions_dict.update({node.shortname: output})
else:
if containerized:
containers = []
if node.role == "client":
pass
else:
out, rc = node.exec_command(
sudo=True, cmd='docker ps --format "{{.Names}}"'
)
output = out.read().decode()
containers = [
container
for container in output.split("\n")
if container != ""
]
log.info("Containers: {}".format(containers))
for container_name in containers:
out, rc = node.exec_command(
sudo=True,
cmd="sudo docker exec {container} ceph --version".format(
container=container_name
),
)
output = out.read().decode().rstrip()
log.info(output)
versions_dict.update({container_name: output})
else:
out, rc = node.exec_command(cmd="ceph --version")
output = out.read().decode().rstrip()
log.info(output)
versions_dict.update({node.shortname: output})
except CommandFailed:
log.info("No ceph versions on {}".format(node.shortname))
return versions_dict
def hard_reboot(gyaml, name=None):
user = os.getlogin()
if name is None:
name = "ceph-" + user
driver = get_openstack_driver(gyaml)
for node in driver.list_nodes():
if node.name.startswith(name):
log.info("Hard-rebooting %s" % node.name)
driver.ex_hard_reboot_node(node)
return 0
def node_power_failure(gyaml, sleep_time=300, name=None):
user = os.getlogin()
if name is None:
name = "ceph-" + user
driver = get_openstack_driver(gyaml)
for node in driver.list_nodes():
if node.name.startswith(name):
log.info("Doing power-off on %s" % node.name)
driver.ex_stop_node(node)
time.sleep(20)
op = driver.ex_get_node_details(node)
if op.state == "stopped":
log.info("Node stopped successfully")
time.sleep(sleep_time)
log.info("Doing power-on on %s" % node.name)
driver.ex_start_node(node)
time.sleep(20)
op = driver.ex_get_node_details(node)
if op.state == "running":
log.info("Node restarted successfully")
time.sleep(20)
return 0
def get_root_permissions(node, path):
"""
Transfer ownership of root to current user for the path given. Recursive.
Args:
node(ceph.ceph.CephNode):
path: file path
"""
node.obtain_root_permissions(path)
def get_public_network():
"""
Get the configured public network subnet for nodes in the cluster.
Returns:
(str) public network subnet
"""
return "10.0.144.0/22" # TODO: pull from configuration file
@retry(LibcloudError, tries=5, delay=15)
def create_nodes(conf, inventory, osp_cred, run_id, instances_name=None):
log.info("Destroying existing osp instances")
cleanup_ceph_nodes(osp_cred, instances_name)
ceph_cluster_dict = {}
log.info("Creating osp instances")
for cluster in conf.get("globals"):
ceph_vmnodes = create_ceph_nodes(
cluster, inventory, osp_cred, run_id, instances_name
)
ceph_nodes = []
clients = []
for node in ceph_vmnodes.values():
if node.role == "win-iscsi-clients":
clients.append(
WinNode(
ip_address=node.ip_address, private_ip=node.get_private_ip()
)
)
else:
ceph = CephNode(
username="cephuser",
password="cephuser",
root_password="passwd",
root_login=node.root_login,
role=node.role,
no_of_volumes=node.no_of_volumes,
ip_address=node.ip_address,
private_ip=node.get_private_ip(),
hostname=node.hostname,
ceph_vmnode=node,
)
ceph_nodes.append(ceph)
cluster_name = cluster.get("ceph-cluster").get("name", "ceph")
ceph_cluster_dict[cluster_name] = Ceph(cluster_name, ceph_nodes)
# TODO: refactor cluster dict to cluster list
log.info("Done creating osp instances")
log.info("Waiting for Floating IPs to be available")
log.info("Sleeping 15 Seconds")
time.sleep(15)
for cluster_name, cluster in ceph_cluster_dict.items():
for instance in cluster:
instance.connect()
return ceph_cluster_dict, clients
def store_cluster_state(ceph_cluster_object, ceph_clusters_file_name):
cn = open(ceph_clusters_file_name, "w+b")
pickle.dump(ceph_cluster_object, cn)
cn.close()
log.info("ceph_clusters_file %s", ceph_clusters_file_name)
def create_oc_resource(
template_name,
cluster_path,
_templating,
template_data=None,
template_dir="ocs-deployment",
):
"""
Create an oc resource after rendering the specified template with
the rook data from cluster_conf.
Args:
template_name (str): Name of the ocs-deployment config template
cluster_path (str): Path to cluster directory, where files will be
written
_templating (Templating): Object of Templating class used for
templating
template_data (dict): Data for render template (default: {})
template_dir (str): Directory under templates dir where template
exists (default: ocs-deployment)
"""
if template_data is None:
template_data = {}
template_path = os.path.join(template_dir, template_name)
template = _templating.render_template(template_path, template_data)
cfg_file = os.path.join(cluster_path, template_name)
with open(cfg_file, "w") as f:
f.write(template)
log.info(f"Creating rook resource from {template_name}")
occli = OCP()
occli.create(cfg_file)
def get_pod_name_by_pattern(
pattern="client", namespace=None, filter=None, cluster_kubeconfig=""
):
"""
In a given namespace find names of the pods that match
the given pattern
Args:
pattern (str): name of the pod with given pattern
namespace (str): Namespace value
filter (str): pod name to filter from the list
cluster_kubeconfig (str): Path to kubeconfig file
Returns:
pod_list (list): List of pod names matching the pattern
"""
namespace = namespace if namespace else ocsci_config.ENV_DATA["cluster_namespace"]
ocp_obj = OCP(
kind="pod", namespace=namespace, cluster_kubeconfig=cluster_kubeconfig
)
pod_names = ocp_obj.exec_oc_cmd("get pods -o name", out_yaml_format=False)
pod_names = pod_names.split("\n")
pod_list = []
for name in pod_names:
if filter is not None and re.search(filter, name):
log.info(f"Pod name filtered {name}")
elif re.search(pattern, name):
(_, name) = name.split("/")
log.info(f"pod name match found appending {name}")
pod_list.append(name)
return pod_list
def get_namespce_name_by_pattern(
pattern="client",
filter=None,
):
"""
Find namespace names that match the given pattern
Args:
pattern (str): name of the namespace with given pattern
filter (str): namespace name to filter from the list
Returns:
list: Namespace names matching the pattern
"""
ocp_obj = OCP(kind="namespace")
namespace_names = ocp_obj.exec_oc_cmd(
"get namespace -o name", out_yaml_format=False
)
namespace_names = namespace_names.split("\n")
namespace_list = []
for namespace_name in namespace_names:
if filter is not None and filter == namespace_name:
log.info(f"Namespace name filtered {namespace_name}")
elif re.search(pattern, namespace_name):
(_, name) = namespace_name.split("/")
log.info(f"namespace name match found appending {namespace_name}")
namespace_list.append(name)
return namespace_list
def get_rook_version():
"""
Get the rook image information from rook-ceph-operator pod
Returns:
str: rook version
"""
namespace = ocsci_config.ENV_DATA["cluster_namespace"]
rook_operator = get_pod_name_by_pattern("rook-ceph-operator", namespace)
out = run_cmd(
f"oc -n {namespace} get pods {rook_operator[0]} -o yaml",
)
version = yaml.safe_load(out)
rook_version = version["spec"]["containers"][0]["image"]
return rook_version
def setup_ceph_toolbox(force_setup=False, storage_cluster=None):
"""
Setup ceph-toolbox - also checks if toolbox exists, if it exists it
behaves as noop.
Args:
force_setup (bool): force setup toolbox pod
"""
ocs_version = version.get_semantic_ocs_version_from_config()
storage_cluster = (
storage_cluster if storage_cluster else constants.DEFAULT_STORAGE_CLUSTER
)
if ocsci_config.ENV_DATA["mcg_only_deployment"]:
log.info("Skipping Ceph toolbox setup due to running in MCG only mode")
return
namespace = ocsci_config.ENV_DATA["cluster_namespace"]
ceph_toolbox = get_pod_name_by_pattern("rook-ceph-tools", namespace)
# setup toolbox for external mode
# Refer bz: 1856982 - invalid admin secret
if len(ceph_toolbox) == 1:
log.info("Ceph toolbox already exists, skipping")
if force_setup:
log.info("Running force setup for Ceph toolbox!")
else:
return
external_mode = ocsci_config.DEPLOYMENT.get("external_mode")
if ocs_version == version.VERSION_4_2:
tool_box_data = templating.load_yaml(constants.TOOL_POD_YAML)
tool_box_data["spec"]["template"]["spec"]["containers"][0][
"image"
] = get_rook_version()
rook_toolbox = OCS(**tool_box_data)
rook_toolbox.create()
else:
if external_mode:
toolbox = templating.load_yaml(constants.TOOL_POD_YAML)
toolbox["spec"]["template"]["spec"]["containers"][0][
"image"
] = get_rook_version()
toolbox["metadata"]["name"] += "-external"
keyring_dict = ocsci_config.EXTERNAL_MODE.get("admin_keyring")
if ocs_version >= version.VERSION_4_10:
toolbox["spec"]["template"]["spec"]["containers"][0]["command"] = [
"/bin/bash"
]
toolbox["spec"]["template"]["spec"]["containers"][0]["args"][0] = "-m"
toolbox["spec"]["template"]["spec"]["containers"][0]["args"][1] = "-c"
toolbox["spec"]["template"]["spec"]["containers"][0]["tty"] = True
env = toolbox["spec"]["template"]["spec"]["containers"][0]["env"]
# replace secret
env = [item for item in env if not (item["name"] == "ROOK_CEPH_SECRET")]
env.append({"name": "ROOK_CEPH_SECRET", "value": keyring_dict["key"]})
toolbox["spec"]["template"]["spec"]["containers"][0]["env"] = env
# add ceph volumeMounts
ceph_volume_mount_path = {"mountPath": "/etc/ceph", "name": "ceph-config"}
ceph_volume = {"name": "ceph-config", "emptyDir": {}}
toolbox["spec"]["template"]["spec"]["containers"][0]["volumeMounts"].append(
ceph_volume_mount_path
)
toolbox["spec"]["template"]["spec"]["volumes"].append(ceph_volume)
if ocs_version >= version.VERSION_4_16:
toolbox["spec"]["template"]["spec"][
"serviceAccount"
] = "rook-ceph-default"
toolbox["spec"]["template"]["spec"][
"serviceAccountName"
] = "rook-ceph-default"
rook_toolbox = OCS(**toolbox)
rook_toolbox.create()
return
if (
ocsci_config.ENV_DATA.get("platform").lower()
== constants.FUSIONAAS_PLATFORM
and ocsci_config.ENV_DATA["cluster_type"].lower()
== constants.MS_CONSUMER_TYPE
):
log.warning(
f"Skipping toolbox creation on {constants.MS_CONSUMER_TYPE} cluster on "
f"{constants.FUSIONAAS_PLATFORM} platform."
)
return
# for OCS >= 4.3 there is new toolbox pod deployment done here:
# https://github.com/openshift/ocs-operator/pull/207/
log.info("starting ceph toolbox pod")
cmd = (
f"oc patch storagecluster {storage_cluster} -n {config.ENV_DATA['cluster_namespace']} --type "
'json --patch \'[{ "op": "replace", "path": '
'"/spec/enableCephTools", "value": true }]\''
)
run_cmd(cmd)
toolbox_pod = OCP(
kind=constants.POD, namespace=config.ENV_DATA["cluster_namespace"]
)
toolbox_pod.wait_for_resource(
condition="Running",
selector="app=rook-ceph-tools",
resource_count=1,
timeout=300,
)
def apply_oc_resource(
template_name,
cluster_path,
_templating,
template_data=None,
template_dir="ocs-deployment",
):
"""
Apply an oc resource after rendering the specified template with
the rook data from cluster_conf.
Args:
template_name (str): Name of the ocs-deployment config template
cluster_path (str): Path to cluster directory, where files will be
written
_templating (Templating): Object of Templating class used for
templating
template_data (dict): Data for render template (default: {})
template_dir (str): Directory under templates dir where template
exists (default: ocs-deployment)
"""
if template_data is None:
template_data = {}
template_path = os.path.join(template_dir, template_name)
template = _templating.render_template(template_path, template_data)
cfg_file = os.path.join(cluster_path, template_name)
with open(cfg_file, "w") as f:
f.write(template)
log.info(f"Applying rook resource from {template_name}")
occli = OCP()
occli.apply(cfg_file)
def run_must_gather(
log_dir_path,
image,
command=None,
cluster_config=None,
silent=False,
output_file=None,
skip_after_max_fail=False,
timeout=defaults.MUST_GATHER_TIMEOUT,
):
"""
Runs the must-gather tool against the cluster
Args:
log_dir_path (str): directory for dumped must-gather logs (if REPORTING["tarball_mg_logs"] is set, this
directory will be packed to the parent directory with extension .tar.gz)
image (str): must-gather image registry path
command (str): optional command to execute within the must-gather image
cluster_config (MultiClusterConfig): Holds specifc cluster config object in case of multicluster
silent (bool): True if silent mode
output_file (bool): True if direct whole output to file instead of printing it out to log (apply
only if silent is True).
skip_after_max_fail (bool): When max number failed attempts to collect MG reached, will skip
MG collection.
timeout (int): Max timeout to wait for MG to complete before aborting the MG execution.
Returns:
mg_output (str): must-gather cli output
"""
global mg_fail_count, mg_last_fail, mg_collected_logs, mg_skip_count
max_mg_fail_attempts = config.REPORTING.get("max_mg_fail_attempts")
if skip_after_max_fail:
with mg_lock:
if mg_fail_count > max_mg_fail_attempts:
mg_skip_count += 1
log.warning(
f"MG collection is skipped because MG already failed {mg_fail_count} times!"
f" Last error occurred at: {mg_last_fail}"
)
return
if not cluster_config:
cluster_config = ocsci_config
mg_output = ""
timestamp = time.time()
log.info(f"Must gather image: {image} will be used.")
create_directory_path(log_dir_path)
cmd = f"adm must-gather --image={image} --dest-dir={log_dir_path}"
if command:
cmd += f" -- {command}"
log.info(f"OCS logs will be placed in location {log_dir_path}")
if output_file: