]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | import json |
2 | import os | |
3 | from typing import Dict | |
4 | ||
33c7a0ef TL |
5 | from util import ( |
6 | Config, | |
7 | Target, | |
8 | ensure_inside_container, | |
9 | ensure_outside_container, | |
10 | get_orch_hosts, | |
11 | inside_container, | |
12 | run_cephadm_shell_command, | |
13 | run_dc_shell_command, | |
14 | run_shell_command, | |
15 | ) | |
20effc67 TL |
16 | |
17 | ||
18 | def remove_loop_img() -> None: | |
19 | loop_image = Config.get('loop_img') | |
20 | if os.path.exists(loop_image): | |
21 | os.remove(loop_image) | |
22 | ||
33c7a0ef | 23 | |
20effc67 TL |
24 | @ensure_outside_container |
25 | def create_loopback_devices(osds: int) -> None: | |
26 | assert osds | |
27 | size = (5 * osds) + 1 | |
28 | print(f'Using {size}GB of data to store osds') | |
29 | avail_loop = run_shell_command('sudo losetup -f') | |
20effc67 TL |
30 | |
31 | # create loop if we cannot find it | |
32 | if not os.path.exists(avail_loop): | |
33c7a0ef | 33 | num_loops = int(run_shell_command("lsmod | grep loop | awk '{print $3}'")) |
20effc67 TL |
34 | num_loops += 1 |
35 | run_shell_command(f'mknod {avail_loop} b 7 {num_loops}') | |
36 | ||
37 | if os.path.ismount(avail_loop): | |
38 | os.umount(avail_loop) | |
39 | ||
33c7a0ef | 40 | loop_devices = json.loads(run_shell_command('losetup -l -J', expect_error=True)) |
20effc67 TL |
41 | for dev in loop_devices['loopdevices']: |
42 | if dev['name'] == avail_loop: | |
43 | run_shell_command(f'sudo losetup -d {avail_loop}') | |
44 | ||
45 | if not os.path.exists('./loop-images'): | |
46 | os.mkdir('loop-images') | |
47 | ||
48 | remove_loop_img() | |
49 | ||
50 | loop_image = Config.get('loop_img') | |
51 | run_shell_command(f'sudo dd if=/dev/zero of={loop_image} bs=1 count=0 seek={size}G') | |
52 | run_shell_command(f'sudo losetup {avail_loop} {loop_image}') | |
53 | ||
54 | # cleanup last call | |
55 | cleanup() | |
56 | ||
57 | run_shell_command(f'sudo pvcreate {avail_loop} ') | |
58 | run_shell_command(f'sudo vgcreate vg1 {avail_loop}') | |
59 | ||
60 | p = int(100 / osds) | |
61 | for i in range(osds): | |
62 | run_shell_command('sudo vgchange --refresh') | |
63 | run_shell_command(f'sudo lvcreate -l {p}%VG --name lv{i} vg1') | |
64 | ||
33c7a0ef | 65 | |
20effc67 TL |
66 | def get_lvm_osd_data(data: str) -> Dict[str, str]: |
67 | osd_lvm_info = run_cephadm_shell_command(f'ceph-volume lvm list {data}') | |
68 | osd_data = {} | |
69 | for line in osd_lvm_info.split('\n'): | |
70 | line = line.strip() | |
71 | if not line: | |
72 | continue | |
73 | line = line.split() | |
74 | if line[0].startswith('===') or line[0].startswith('[block]'): | |
75 | continue | |
76 | # "block device" key -> "block_device" | |
77 | key = '_'.join(line[:-1]) | |
78 | osd_data[key] = line[-1] | |
79 | return osd_data | |
80 | ||
33c7a0ef | 81 | |
20effc67 | 82 | @ensure_inside_container |
33c7a0ef TL |
83 | def deploy_osd(data: str, hostname: str) -> bool: |
84 | out = run_cephadm_shell_command(f'ceph orch daemon add osd "{hostname}:{data}"') | |
85 | return 'Created osd(s)' in out | |
86 | ||
20effc67 TL |
87 | |
88 | def cleanup() -> None: | |
89 | vg = 'vg1' | |
90 | pvs = json.loads(run_shell_command('sudo pvs --reportformat json')) | |
91 | for pv in pvs['report'][0]['pv']: | |
92 | if pv['vg_name'] == vg: | |
93 | device = pv['pv_name'] | |
94 | run_shell_command(f'sudo vgremove -f --yes {vg}') | |
95 | run_shell_command(f'sudo losetup -d {device}') | |
96 | run_shell_command(f'sudo wipefs -af {device}') | |
97 | # FIX: this can fail with excluded filter | |
98 | run_shell_command(f'sudo pvremove -f --yes {device}', expect_error=True) | |
99 | break | |
100 | ||
101 | remove_loop_img() | |
102 | ||
33c7a0ef TL |
103 | |
104 | def deploy_osds_in_vg(vg: str): | |
105 | """ | |
106 | rotate host will deploy each osd in a different host | |
107 | ||
108 | deploying osds will not succeed with starting services so this | |
109 | makes another process to run on the background | |
110 | """ | |
111 | if inside_container(): | |
112 | lvs = json.loads(run_shell_command('lvs --reportformat json')) | |
113 | # distribute osds per host | |
114 | hosts = get_orch_hosts() | |
115 | host_index = 0 | |
116 | for lv in lvs['report'][0]['lv']: | |
117 | if lv['vg_name'] == vg: | |
118 | deployed = False | |
119 | while not deployed: | |
120 | deployed = deploy_osd( | |
121 | f'{vg}/{lv["lv_name"]}', hosts[host_index]['hostname'] | |
122 | ) | |
123 | host_index = (host_index + 1) % len(hosts) | |
124 | else: | |
125 | verbose = '-v' if Config.get('verbose') else '' | |
126 | print('Redirecting deploy osd in vg to inside container') | |
127 | run_dc_shell_command( | |
128 | f'/cephadm/box/box.py {verbose} osd deploy --vg {vg}', 1, 'seed' | |
129 | ) | |
130 | ||
131 | ||
20effc67 | 132 | class Osd(Target): |
33c7a0ef | 133 | _help = """ |
20effc67 TL |
134 | Deploy osds and create needed block devices with loopback devices: |
135 | Actions: | |
136 | - deploy: Deploy an osd given a block device | |
137 | - create_loop: Create needed loopback devices and block devices in logical volumes | |
138 | for a number of osds. | |
33c7a0ef | 139 | """ |
20effc67 TL |
140 | actions = ['deploy', 'create_loop'] |
141 | ||
142 | def set_args(self): | |
143 | self.parser.add_argument('action', choices=Osd.actions) | |
144 | self.parser.add_argument('--data', type=str, help='path to a block device') | |
145 | self.parser.add_argument('--hostname', type=str, help='host to deploy osd') | |
146 | self.parser.add_argument('--osds', type=int, default=0, help='number of osds') | |
33c7a0ef TL |
147 | self.parser.add_argument( |
148 | '--vg', type=str, help='Deploy with all lv from virtual group' | |
149 | ) | |
20effc67 | 150 | |
20effc67 TL |
151 | def deploy(self): |
152 | data = Config.get('data') | |
153 | hostname = Config.get('hostname') | |
154 | vg = Config.get('vg') | |
155 | if not hostname: | |
156 | # assume this host | |
157 | hostname = run_shell_command('hostname') | |
158 | if vg: | |
33c7a0ef | 159 | deploy_osds_in_vg(vg) |
20effc67 TL |
160 | else: |
161 | deploy_osd(data, hostname) | |
162 | ||
163 | @ensure_outside_container | |
164 | def create_loop(self): | |
165 | osds = Config.get('osds') | |
166 | create_loopback_devices(osds) | |
167 | print('Successfully added logical volumes in loopback devices') |