Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / ceph-volume / ceph_volume / devices / simple / scan.py
1 from __future__ import print_function
2 import argparse
3 import json
4 import logging
5 import os
6 from textwrap import dedent
7 from ceph_volume import decorators, terminal, conf
8 from ceph_volume.api import lvm
9 from ceph_volume.util import arg_validators, system, disk
10
11
12 logger = logging.getLogger(__name__)
13
14
15 class Scan(object):
16
17     help = 'Capture metadata from an OSD data partition or directory'
18
19     def __init__(self, argv):
20         self.argv = argv
21         self._etc_path = '/etc/ceph/osd/'
22
23     @property
24     def etc_path(self):
25         if os.path.isdir(self._etc_path):
26             return self._etc_path
27
28         if not os.path.exists(self._etc_path):
29             os.mkdir(self._etc_path)
30             return self._etc_path
31
32         error = "OSD Configuration path (%s) needs to be a directory" % self._etc_path
33         raise RuntimeError(error)
34
35     def get_contents(self, path):
36         with open(path, 'r') as fp:
37             contents = fp.readlines()
38         if len(contents) > 1:
39             return ''.join(contents)
40         return ''.join(contents).strip().strip('\n')
41
42     def scan_device(self, path):
43         device_metadata = {'path': None, 'uuid': None}
44         if not path:
45             return device_metadata
46         # cannot read the symlink if this is tmpfs
47         if os.path.islink(path):
48             device = os.readlink(path)
49         else:
50             device = path
51         lvm_device = lvm.get_lv_from_argument(device)
52         if lvm_device:
53             device_uuid = lvm_device.lv_uuid
54         else:
55             device_uuid = disk.get_partuuid(device)
56
57         device_metadata['uuid'] = device_uuid
58         device_metadata['path'] = device
59
60         return device_metadata
61
62     def scan_directory(self, path):
63         osd_metadata = {'cluster_name': conf.cluster}
64         path_mounts = system.get_mounts(paths=True)
65         for _file in os.listdir(path):
66             file_path = os.path.join(path, _file)
67             if os.path.islink(file_path):
68                 osd_metadata[_file] = self.scan_device(file_path)
69             if os.path.isdir(file_path):
70                 continue
71             # the check for binary needs to go before the file, to avoid
72             # capturing data from binary files but still be able to capture
73             # contents from actual files later
74             if system.is_binary(file_path):
75                 continue
76             if os.path.isfile(file_path):
77                 osd_metadata[_file] = self.get_contents(file_path)
78
79         device = path_mounts.get(path)
80         # it is possible to have more than one device, pick the first one, and
81         # warn that it is possible that more than one device is 'data'
82         if not device:
83             terminal.error('Unable to detect device mounted for path: %s' % path)
84             raise RuntimeError('Cannot activate OSD')
85         osd_metadata['data'] = self.scan_device(device[0] if len(device) else None)
86
87         return osd_metadata
88
89     @decorators.needs_root
90     def scan(self, args):
91         osd_metadata = {'cluster_name': conf.cluster}
92         device_mounts = system.get_mounts(devices=True)
93         osd_path = None
94         logger.info('detecting if argument is a device or a directory: %s', args.osd_path)
95         if os.path.isdir(args.osd_path):
96             logger.info('will scan directly, path is a directory')
97             osd_path = args.osd_path
98         else:
99             # assume this is a device, check if it is mounted and use that path
100             logger.info('path is not a directory, will check if mounted')
101             if system.device_is_mounted(args.osd_path):
102                 logger.info('argument is a device, which is mounted')
103                 mounted_osd_paths = device_mounts.get(args.osd_path)
104                 osd_path = mounted_osd_paths[0] if len(mounted_osd_paths) else None
105
106         # argument is not a directory, and it is not a device that is mounted
107         # somewhere so temporarily mount it to poke inside, otherwise, scan
108         # directly
109         if not osd_path:
110             logger.info('device is not mounted, will mount it temporarily to scan')
111             with system.tmp_mount(args.osd_path) as osd_path:
112                 osd_metadata = self.scan_directory(osd_path)
113         else:
114             logger.info('will scan OSD directory at path: %s', osd_path)
115             osd_metadata = self.scan_directory(osd_path)
116
117         osd_id = osd_metadata['whoami']
118         osd_fsid = osd_metadata['fsid']
119         filename = '%s-%s.json' % (osd_id, osd_fsid)
120         json_path = os.path.join(self.etc_path, filename)
121         if os.path.exists(json_path) and not args.stdout:
122             if not args.force:
123                 raise RuntimeError(
124                     '--force was not used and OSD metadata file exists: %s' % json_path
125                 )
126
127         if args.stdout:
128             print(json.dumps(osd_metadata, indent=4, sort_keys=True, ensure_ascii=False))
129         else:
130             with open(json_path, 'w') as fp:
131                 json.dump(osd_metadata, fp, indent=4, sort_keys=True, ensure_ascii=False)
132             terminal.success(
133                 'OSD %s got scanned and metadata persisted to file: %s' % (
134                     osd_id,
135                     json_path
136                 )
137             )
138             terminal.success(
139                 'To take over managment of this scanned OSD, and disable ceph-disk and udev, run:'
140             )
141             terminal.success('    ceph-volume simple activate %s %s' % (osd_id, osd_fsid))
142
143         if not osd_metadata.get('data'):
144             msg = 'Unable to determine device mounted on %s' % args.osd_path
145             logger.warning(msg)
146             terminal.warning(msg)
147             terminal.warning('OSD will not be able to start without this information:')
148             terminal.warning('    "data": "/path/to/device",')
149             logger.warning('Unable to determine device mounted on %s' % args.osd_path)
150
151     def main(self):
152         sub_command_help = dedent("""
153         Scan an OSD directory for files and configurations that will allow to
154         take over the management of the OSD.
155
156         Scanned OSDs will get their configurations stored in
157         /etc/ceph/osd/<id>-<fsid>.json
158
159         For an OSD ID of 0 with fsid of ``a9d50838-e823-43d6-b01f-2f8d0a77afc2``
160         that could mean a scan command that looks like::
161
162             ceph-volume lvm scan /var/lib/ceph/osd/ceph-0
163
164         Which would store the metadata in a JSON file at::
165
166             /etc/ceph/osd/0-a9d50838-e823-43d6-b01f-2f8d0a77afc2.json
167
168         To a scan an existing, running, OSD:
169
170             ceph-volume simple scan /var/lib/ceph/osd/{cluster}-{osd id}
171
172         And to scan a device (mounted or unmounted) that has OSD data in it, for example /dev/sda1
173
174             ceph-volume simple scan /dev/sda1
175         """)
176         parser = argparse.ArgumentParser(
177             prog='ceph-volume simple scan',
178             formatter_class=argparse.RawDescriptionHelpFormatter,
179             description=sub_command_help,
180         )
181
182         parser.add_argument(
183             '-f', '--force',
184             action='store_true',
185             help='If OSD has already been scanned, the JSON file will be overwritten'
186         )
187
188         parser.add_argument(
189             '--stdout',
190             action='store_true',
191             help='Do not save to a file, output metadata to stdout'
192         )
193
194         parser.add_argument(
195             'osd_path',
196             metavar='OSD_PATH',
197             type=arg_validators.OSDPath(),
198             nargs='?',
199             help='Path to an existing OSD directory or OSD data partition'
200         )
201
202         if len(self.argv) == 0:
203             print(sub_command_help)
204             return
205         args = parser.parse_args(self.argv)
206         self.scan(args)