1 from __future__ import print_function
5 from textwrap import dedent
6 from ceph_volume import process, conf, decorators
7 from ceph_volume.util import system, disk
8 from ceph_volume.util import prepare as prepare_utils
9 from ceph_volume.systemd import systemctl
10 from ceph_volume.api import lvm as api
13 logger = logging.getLogger(__name__)
16 def activate_filestore(lvs):
18 osd_lv = lvs.get(lv_tags={'ceph.type': 'data'})
20 raise RuntimeError('Unable to find a data LV for filestore activation')
21 osd_id = osd_lv.tags['ceph.osd_id']
22 conf.cluster = osd_lv.tags['ceph.cluster_name']
23 # it may have a volume with a journal
24 osd_journal_lv = lvs.get(lv_tags={'ceph.type': 'journal'})
25 # TODO: add sensible error reporting if this is ever the case
26 # blow up with a KeyError if this doesn't exist
27 osd_fsid = osd_lv.tags['ceph.osd_fsid']
28 if not osd_journal_lv:
29 # must be a disk partition, by quering blkid by the uuid we are ensuring that the
30 # device path is always correct
31 osd_journal = disk.get_device_from_partuuid(osd_lv.tags['ceph.journal_uuid'])
33 osd_journal = osd_lv.tags['ceph.journal_device']
36 raise RuntimeError('unable to detect an lv or device journal for OSD %s' % osd_id)
39 source = osd_lv.lv_path
40 destination = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
41 if not system.device_is_mounted(source, destination=destination):
42 process.run(['mount', '-v', source, destination])
44 # always re-do the symlink regardless if it exists, so that the journal
45 # device path that may have changed can be mapped correctly every time
46 destination = '/var/lib/ceph/osd/%s-%s/journal' % (conf.cluster, osd_id)
47 process.run(['ln', '-snf', osd_journal, destination])
49 # make sure that the journal has proper permissions
50 system.chown(osd_journal)
52 # enable the ceph-volume unit for this OSD
53 systemctl.enable_volume(osd_id, osd_fsid, 'lvm')
56 systemctl.start_osd(osd_id)
59 def get_osd_device_path(osd_lv, lvs, device_type):
61 ``device_type`` can be one of ``db``, ``wal`` or ``block`` so that
62 we can query ``lvs`` (a ``Volumes`` object) and fallback to querying the uuid
63 if that is not present.
65 Return a path if possible, failing to do that a ``None``, since some of these devices
68 osd_lv = lvs.get(lv_tags={'ceph.type': 'block'})
69 uuid_tag = 'ceph.%s_uuid' % device_type
70 device_uuid = osd_lv.tags.get(uuid_tag)
74 device_lv = lvs.get(lv_uuid=device_uuid)
76 return device_lv.lv_path
78 # this could be a regular device, so query it with blkid
79 physical_device = disk.get_device_from_partuuid(device_uuid)
80 return physical_device or None
84 def activate_bluestore(lvs):
86 osd_lv = lvs.get(lv_tags={'ceph.type': 'block'})
87 osd_id = osd_lv.tags['ceph.osd_id']
88 conf.cluster = osd_lv.tags['ceph.cluster_name']
89 osd_fsid = osd_lv.tags['ceph.osd_fsid']
90 db_device_path = get_osd_device_path(osd_lv, lvs, 'db')
91 wal_device_path = get_osd_device_path(osd_lv, lvs, 'wal')
93 # mount on tmpfs the osd directory
94 osd_path = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
95 if not system.path_is_mounted(osd_path):
96 # mkdir -p and mount as tmpfs
97 prepare_utils.create_osd_path(osd_id, tmpfs=True)
98 # XXX This needs to be removed once ceph-bluestore-tool can deal with
99 # symlinks that exist in the osd dir
100 for link_name in ['block', 'block.db', 'block.wal']:
101 link_path = os.path.join(osd_path, link_name)
102 if os.path.exists(link_path):
103 os.unlink(os.path.join(osd_path, link_name))
104 # Once symlinks are removed, the osd dir can be 'primed again.
106 'ceph-bluestore-tool', '--cluster=%s' % conf.cluster,
107 'prime-osd-dir', '--dev', osd_lv.lv_path,
109 # always re-do the symlink regardless if it exists, so that the block,
110 # block.wal, and block.db devices that may have changed can be mapped
111 # correctly every time
112 process.run(['ln', '-snf', osd_lv.lv_path, os.path.join(osd_path, 'block')])
113 system.chown(os.path.join(osd_path, 'block'))
114 system.chown(osd_path)
116 destination = os.path.join(osd_path, 'block.db')
117 process.run(['ln', '-snf', db_device_path, destination])
118 system.chown(db_device_path)
120 destination = os.path.join(osd_path, 'block.wal')
121 process.run(['ln', '-snf', wal_device_path, destination])
122 system.chown(wal_device_path)
124 # enable the ceph-volume unit for this OSD
125 systemctl.enable_volume(osd_id, osd_fsid, 'lvm')
128 systemctl.start_osd(osd_id)
131 class Activate(object):
133 help = 'Discover and mount the LVM device associated with an OSD ID and start the Ceph OSD'
135 def __init__(self, argv):
138 @decorators.needs_root
139 def activate(self, args):
141 # filter them down for the OSD ID and FSID we need to activate
142 if args.osd_id and args.osd_fsid:
143 lvs.filter(lv_tags={'ceph.osd_id': args.osd_id, 'ceph.osd_fsid': args.osd_fsid})
144 elif args.osd_fsid and not args.osd_id:
145 lvs.filter(lv_tags={'ceph.osd_fsid': args.osd_fsid})
147 raise RuntimeError('could not find osd.%s with fsid %s' % (args.osd_id, args.osd_fsid))
148 # This argument is only available when passed in directly or via
149 # systemd, not when ``create`` is being used
150 if getattr(args, 'auto_detect_objectstore', False):
151 logger.info('auto detecting objectstore')
152 # may get multiple lvs, so can't do lvs.get() calls here
154 has_journal = lv.tags.get('ceph.journal_uuid')
156 logger.info('found a journal associated with the OSD, assuming filestore')
157 return activate_filestore(lvs)
158 logger.info('unable to find a journal associated with the OSD, assuming bluestore')
159 return activate_bluestore(lvs)
161 activate_bluestore(lvs)
163 activate_filestore(lvs)
166 sub_command_help = dedent("""
167 Activate OSDs by discovering them with LVM and mounting them in their
168 appropriate destination:
170 ceph-volume lvm activate {ID} {FSID}
172 The lvs associated with the OSD need to have been prepared previously,
173 so that all needed tags and metadata exist.
176 parser = argparse.ArgumentParser(
177 prog='ceph-volume lvm activate',
178 formatter_class=argparse.RawDescriptionHelpFormatter,
179 description=sub_command_help,
186 help='The ID of the OSD, usually an integer, like 0'
192 help='The FSID of the OSD, similar to a SHA1'
195 '--auto-detect-objectstore',
197 help='Autodetect the objectstore by inspecting the OSD',
202 help='filestore objectstore (not yet implemented)',
207 help='filestore objectstore (current default)',
209 if len(self.argv) == 0:
210 print(sub_command_help)
212 args = parser.parse_args(self.argv)
213 # Default to bluestore here since defaulting it in add_argument may
214 # cause both to be True
215 if not args.bluestore and not args.filestore:
216 args.bluestore = True