summaryrefslogtreecommitdiffstats
path: root/src/cephadm/box/osd.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/cephadm/box/osd.py')
-rw-r--r--src/cephadm/box/osd.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/cephadm/box/osd.py b/src/cephadm/box/osd.py
new file mode 100644
index 000000000..827a4de36
--- /dev/null
+++ b/src/cephadm/box/osd.py
@@ -0,0 +1,157 @@
+import json
+import os
+import time
+import re
+from typing import Dict
+
+from util import (
+ BoxType,
+ Config,
+ Target,
+ ensure_inside_container,
+ ensure_outside_container,
+ get_orch_hosts,
+ run_cephadm_shell_command,
+ run_dc_shell_command,
+ get_container_engine,
+ run_shell_command,
+)
+
+DEVICES_FILE="./devices.json"
+
+def remove_loop_img() -> None:
+ loop_image = Config.get('loop_img')
+ if os.path.exists(loop_image):
+ os.remove(loop_image)
+
+def create_loopback_devices(osds: int) -> Dict[int, Dict[str, str]]:
+ assert osds
+ cleanup_osds()
+ osd_devs = dict()
+
+ for i in range(osds):
+ img_name = f'osd{i}'
+ loop_dev = create_loopback_device(img_name)
+ osd_devs[i] = dict(img_name=img_name, device=loop_dev)
+ with open(DEVICES_FILE, 'w') as dev_file:
+ dev_file.write(json.dumps(osd_devs))
+ return osd_devs
+
+def create_loopback_device(img_name, size_gb=5):
+ loop_img_dir = Config.get('loop_img_dir')
+ run_shell_command(f'mkdir -p {loop_img_dir}')
+ loop_img = os.path.join(loop_img_dir, img_name)
+ run_shell_command(f'rm -f {loop_img}')
+ run_shell_command(f'dd if=/dev/zero of={loop_img} bs=1 count=0 seek={size_gb}G')
+ loop_dev = run_shell_command(f'sudo losetup -f')
+ if not os.path.exists(loop_dev):
+ dev_minor = re.match(r'\/dev\/[^\d]+(\d+)', loop_dev).groups()[0]
+ run_shell_command(f'sudo mknod -m777 {loop_dev} b 7 {dev_minor}')
+ run_shell_command(f'sudo chown {os.getuid()}:{os.getgid()} {loop_dev}')
+ if os.path.ismount(loop_dev):
+ os.umount(loop_dev)
+ run_shell_command(f'sudo losetup {loop_dev} {loop_img}')
+ run_shell_command(f'sudo chown {os.getuid()}:{os.getgid()} {loop_dev}')
+ return loop_dev
+
+
+def get_lvm_osd_data(data: str) -> Dict[str, str]:
+ osd_lvm_info = run_cephadm_shell_command(f'ceph-volume lvm list {data}')
+ osd_data = {}
+ for line in osd_lvm_info.split('\n'):
+ line = line.strip()
+ if not line:
+ continue
+ line = line.split()
+ if line[0].startswith('===') or line[0].startswith('[block]'):
+ continue
+ # "block device" key -> "block_device"
+ key = '_'.join(line[:-1])
+ osd_data[key] = line[-1]
+ return osd_data
+
+def load_osd_devices():
+ if not os.path.exists(DEVICES_FILE):
+ return dict()
+ with open(DEVICES_FILE) as dev_file:
+ devs = json.loads(dev_file.read())
+ return devs
+
+
+@ensure_inside_container
+def deploy_osd(data: str, hostname: str) -> bool:
+ out = run_cephadm_shell_command(f'ceph orch daemon add osd {hostname}:{data} raw')
+ return 'Created osd(s)' in out
+
+
+def cleanup_osds() -> None:
+ loop_img_dir = Config.get('loop_img_dir')
+ osd_devs = load_osd_devices()
+ for osd in osd_devs.values():
+ device = osd['device']
+ if 'loop' in device:
+ loop_img = os.path.join(loop_img_dir, osd['img_name'])
+ run_shell_command(f'sudo losetup -d {device}', expect_error=True)
+ if os.path.exists(loop_img):
+ os.remove(loop_img)
+ run_shell_command(f'rm -rf {loop_img_dir}')
+
+
+def deploy_osds(count: int):
+ osd_devs = load_osd_devices()
+ hosts = get_orch_hosts()
+ host_index = 0
+ seed = get_container_engine().get_seed()
+ v = '-v' if Config.get('verbose') else ''
+ for osd in osd_devs.values():
+ deployed = False
+ while not deployed:
+ print(hosts)
+ hostname = hosts[host_index]['hostname']
+ deployed = run_dc_shell_command(
+ f'/cephadm/box/box.py {v} osd deploy --data {osd["device"]} --hostname {hostname}',
+ seed
+ )
+ deployed = 'created osd' in deployed.lower() or 'already created?' in deployed.lower()
+ print('Waiting 5 seconds to re-run deploy osd...')
+ time.sleep(5)
+ host_index = (host_index + 1) % len(hosts)
+
+
+class Osd(Target):
+ _help = """
+ Deploy osds and create needed block devices with loopback devices:
+ Actions:
+ - deploy: Deploy an osd given a block device
+ - create_loop: Create needed loopback devices and block devices in logical volumes
+ for a number of osds.
+ - destroy: Remove all osds and the underlying loopback devices.
+ """
+ actions = ['deploy', 'create_loop', 'destroy']
+
+ def set_args(self):
+ self.parser.add_argument('action', choices=Osd.actions)
+ self.parser.add_argument('--data', type=str, help='path to a block device')
+ self.parser.add_argument('--hostname', type=str, help='host to deploy osd')
+ self.parser.add_argument('--osds', type=int, default=0, help='number of osds')
+
+ def deploy(self):
+ data = Config.get('data')
+ hostname = Config.get('hostname')
+ if not hostname:
+ # assume this host
+ hostname = run_shell_command('hostname')
+ if not data:
+ deploy_osds(Config.get('osds'))
+ else:
+ deploy_osd(data, hostname)
+
+ @ensure_outside_container
+ def create_loop(self):
+ osds = Config.get('osds')
+ create_loopback_devices(int(osds))
+ print('Successfully created loopback devices')
+
+ @ensure_outside_container
+ def destroy(self):
+ cleanup_osds()