1 from __future__
import print_function
2 from ceph_volume
.util
import disk
, prepare
3 from ceph_volume
.api
import lvm
4 from . import validators
5 from .strategies
import Strategy
6 from .strategies
import MixedStrategy
7 from ceph_volume
.devices
.lvm
.create
import Create
8 from ceph_volume
.devices
.lvm
.prepare
import Prepare
9 from ceph_volume
.util
import templates
, system
10 from ceph_volume
.exceptions
import SizeAllocationError
13 def get_journal_size(args
):
15 Helper for Filestore strategies, to prefer the --journal-size value from
16 the CLI over anything that might be in a ceph configuration file (if any).
19 return disk
.Size(mb
=args
.journal_size
)
21 return prepare
.get_journal_size(lv_format
=False)
24 class SingleType(Strategy
):
26 Support for all SSDs, or all HDDs, data and journal LVs will be colocated
31 def __init__(self
, args
, data_devs
):
32 super(SingleType
, self
).__init
__(args
, data_devs
)
33 self
.journal_size
= get_journal_size(args
)
34 self
.validate_compute()
37 def with_auto_devices(cls
, args
, devices
):
38 return cls(args
, devices
)
42 return "filestore.SingleType"
44 def report_pretty(self
, filtered_devices
):
47 string
+= templates
.filtered_devices(filtered_devices
)
48 string
+= templates
.total_osds
.format(
49 total_osds
=self
.total_osds
51 string
+= templates
.osd_component_titles
53 for osd
in self
.computed
['osds']:
54 string
+= templates
.osd_header
56 string
+= templates
.osd_reused_id
.format(
58 string
+= templates
.osd_component
.format(
60 path
=osd
['data']['path'],
61 size
=osd
['data']['human_readable_size'],
62 percent
=osd
['data']['percentage'],
64 string
+= templates
.osd_component
.format(
66 path
=osd
['journal']['path'],
67 size
=osd
['journal']['human_readable_size'],
68 percent
=osd
['journal']['percentage'],
75 Ensure that the minimum requirements for this type of scenario is
76 met, raise an error if the provided devices would not work
78 # validate minimum size for all devices
79 validators
.minimum_device_size(self
.data_devs
, osds_per_device
=self
.osds_per_device
)
81 # validate collocation
82 validators
.minimum_device_collocated_size(
83 self
.data_devs
, self
.journal_size
, osds_per_device
=self
.osds_per_device
86 # make sure that data devices do not have any LVs
87 validators
.no_lvm_membership(self
.data_devs
)
90 self
._validate
_osd
_ids
()
94 Go through the rules needed to properly size the lvs, return
95 a dictionary with the result
97 # chose whichever is the one group we have to compute against
98 osds
= self
.computed
['osds']
99 for device
in self
.data_devs
:
100 for osd
in range(self
.osds_per_device
):
101 device_size
= disk
.Size(b
=device
.lvm_size
.b
)
102 osd_size
= device_size
/ self
.osds_per_device
103 journal_size
= self
.journal_size
104 data_size
= osd_size
- journal_size
105 data_percentage
= data_size
* 100 / device_size
106 osd
= {'data': {}, 'journal': {}}
107 osd
['data']['path'] = device
.abspath
108 osd
['data']['size'] = data_size
.b
.as_int()
109 osd
['data']['parts'] = self
.osds_per_device
110 osd
['data']['percentage'] = int(data_percentage
)
111 osd
['data']['human_readable_size'] = str(data_size
)
112 osd
['journal']['path'] = device
.abspath
113 osd
['journal']['size'] = journal_size
.b
.as_int()
114 osd
['journal']['percentage'] = int(100 - data_percentage
)
115 osd
['journal']['human_readable_size'] = str(journal_size
)
118 osd
['osd_id'] = self
.osd_ids
.pop()
122 self
.computed
['changed'] = len(osds
) > 0
126 Create vgs/lvs from the incoming set of devices, assign their roles
127 (data, journal) and offload the OSD creation to ``lvm create``
129 device_vgs
= dict([(osd
['data']['path'], None) for osd
in self
.computed
['osds']])
131 # create 1 vg per data device first, mapping them to the device path,
132 # when the lvs get created later, it can create as many as needed,
133 # including the journals since it is going to be collocated
134 for osd
in self
.computed
['osds']:
135 vg
= device_vgs
.get(osd
['data']['path'])
137 vg
= lvm
.create_vg(osd
['data']['path'], name_prefix
='ceph-filestore')
138 device_vgs
[osd
['data']['path']] = vg
140 # create the lvs from the per-device vg created in the beginning
141 for osd
in self
.computed
['osds']:
142 data_path
= osd
['data']['path']
143 data_lv_size
= disk
.Size(b
=osd
['data']['size']).gb
.as_int()
144 device_vg
= device_vgs
[data_path
]
145 data_lv_extents
= device_vg
.sizing(size
=data_lv_size
)['extents']
146 journal_lv_extents
= device_vg
.sizing(size
=self
.journal_size
.gb
.as_int())['extents']
147 data_uuid
= system
.generate_uuid()
148 data_lv
= lvm
.create_lv(
149 'osd-data', data_uuid
, vg
=device_vg
, extents
=data_lv_extents
)
150 journal_uuid
= system
.generate_uuid()
151 journal_lv
= lvm
.create_lv(
152 'osd-journal', journal_uuid
, vg
=device_vg
, extents
=journal_lv_extents
)
154 command
= ['--filestore', '--data']
155 command
.append('%s/%s' % (device_vg
.name
, data_lv
.name
))
156 command
.extend(['--journal', '%s/%s' % (device_vg
.name
, journal_lv
.name
)])
157 if self
.args
.dmcrypt
:
158 command
.append('--dmcrypt')
159 if self
.args
.no_systemd
:
160 command
.append('--no-systemd')
161 if self
.args
.crush_device_class
:
162 command
.extend(['--crush-device-class', self
.args
.crush_device_class
])
164 command
.extend(['--osd-id', osd
['osd_id']])
166 if self
.args
.prepare
:
167 Prepare(command
).main()
169 Create(command
).main()
172 class MixedType(MixedStrategy
):
174 Supports HDDs with SSDs, journals will be placed on SSDs, while HDDs will
175 be used fully for data.
177 If an existing common VG is detected on SSDs, it will be extended if blank
178 SSDs are used, otherwise it will be used directly.
182 def __init__(self
, args
, data_devs
, journal_devs
):
183 super(MixedType
, self
).__init
__(args
, data_devs
, journal_devs
)
184 self
.blank_journal_devs
= []
185 self
.journals_needed
= len(self
.data_devs
) * self
.osds_per_device
186 self
.journal_size
= get_journal_size(args
)
187 self
.validate_compute()
190 def with_auto_devices(cls
, args
, devices
):
191 data_devs
, journal_devs
= cls
.split_devices_rotational(devices
)
192 return cls(args
, data_devs
, journal_devs
)
196 return "filestore.MixedType"
198 def report_pretty(self
, filtered_devices
):
201 string
+= templates
.filtered_devices(filtered_devices
)
202 string
+= templates
.total_osds
.format(
203 total_osds
=self
.total_osds
206 string
+= templates
.ssd_volume_group
.format(
208 total_lv_size
=str(self
.total_available_journal_space
),
209 total_lvs
=self
.journals_needed
,
210 block_db_devices
=', '.join([d
.path
for d
in self
.db_or_journal_devs
]),
211 lv_size
=str(self
.journal_size
),
212 total_osds
=self
.journals_needed
215 string
+= templates
.osd_component_titles
217 for osd
in self
.computed
['osds']:
218 string
+= templates
.osd_header
220 string
+= templates
.osd_reused_id
.format(
222 string
+= templates
.osd_component
.format(
224 path
=osd
['data']['path'],
225 size
=osd
['data']['human_readable_size'],
226 percent
=osd
['data']['percentage'],
228 string
+= templates
.osd_component
.format(
230 path
=osd
['journal']['path'],
231 size
=osd
['journal']['human_readable_size'],
232 percent
=osd
['journal']['percentage'],
239 Ensure that the minimum requirements for this type of scenario is
240 met, raise an error if the provided devices would not work
242 # validate minimum size for all devices
243 validators
.minimum_device_size(self
.devices
, osds_per_device
=self
.osds_per_device
)
245 # make sure that data devices do not have any LVs
246 validators
.no_lvm_membership(self
.data_devs
)
248 # do not allow non-common VG to continue
249 validators
.has_common_vg(self
.db_or_journal_devs
)
251 # find the common VG to calculate how much is available
252 self
.common_vg
= self
.get_common_vg(self
.db_or_journal_devs
)
254 # find how many journals are possible from the common VG
256 common_vg_size
= disk
.Size(b
=self
.common_vg
.free
)
258 common_vg_size
= disk
.Size(gb
=0)
261 vg_ssds
= set([d
for d
in self
.db_or_journal_devs
if d
.is_lvm_member
])
262 self
.blank_journal_devs
= set(self
.db_or_journal_devs
).difference(vg_ssds
)
263 self
.total_blank_journal_dev_size
= disk
.Size(b
=0)
264 for blank_journal_dev
in self
.blank_journal_devs
:
265 self
.total_blank_journal_dev_size
+= disk
.Size(b
=blank_journal_dev
.lvm_size
.b
)
267 self
.total_available_journal_space
= self
.total_blank_journal_dev_size
+ common_vg_size
270 self
.vg_extents
= lvm
.sizing(
271 self
.total_available_journal_space
.b
, size
=self
.journal_size
.b
* self
.osds_per_device
273 except SizeAllocationError
:
274 msg
= "Not enough space in fast devices (%s) to create %s x %s journal LV"
276 msg
% (self
.total_available_journal_space
, self
.osds_per_device
, self
.journal_size
)
279 # validate that number of journals possible are enough for number of
281 total_journals_possible
= self
.total_available_journal_space
/ self
.journal_size
282 if self
.osds_per_device
> total_journals_possible
:
283 msg
= "Not enough space (%s) to create %s x %s journal LVs" % (
284 self
.total_available_journal_space
, self
.journals_needed
, self
.journal_size
286 raise RuntimeError(msg
)
289 self
._validate
_osd
_ids
()
293 Go through the rules needed to properly size the lvs, return
294 a dictionary with the result
296 osds
= self
.computed
['osds']
298 vg_free
= int(self
.total_available_journal_space
.gb
)
299 if not self
.common_vg
:
300 # there isn't a common vg, so a new one must be created with all
302 self
.computed
['vg'] = {
303 'devices': ", ".join([ssd
.abspath
for ssd
in self
.blank_journal_devs
]),
304 'parts': self
.journals_needed
,
305 'percentages': self
.vg_extents
['percentages'],
306 'sizes': self
.journal_size
.b
.as_int(),
307 'size': self
.total_blank_journal_dev_size
.b
.as_int(),
308 'human_readable_sizes': str(self
.journal_size
),
309 'human_readable_size': str(self
.total_available_journal_space
),
313 vg_name
= self
.common_vg
.name
315 for device
in self
.data_devs
:
316 for osd
in range(self
.osds_per_device
):
317 device_size
= disk
.Size(b
=device
.lvm_size
.b
)
318 data_size
= device_size
/ self
.osds_per_device
319 osd
= {'data': {}, 'journal': {}}
320 osd
['data']['path'] = device
.path
321 osd
['data']['size'] = data_size
.b
.as_int()
322 osd
['data']['percentage'] = 100 / self
.osds_per_device
323 osd
['data']['human_readable_size'] = str(data_size
)
324 osd
['journal']['path'] = 'vg: %s' % vg_name
325 osd
['journal']['size'] = self
.journal_size
.b
.as_int()
326 osd
['journal']['percentage'] = int(self
.journal_size
.gb
* 100 / vg_free
)
327 osd
['journal']['human_readable_size'] = str(self
.journal_size
)
330 osd
['osd_id'] = self
.osd_ids
.pop(0)
334 self
.computed
['changed'] = len(osds
) > 0
338 Create vgs/lvs from the incoming set of devices, assign their roles
339 (data, journal) and offload the OSD creation to ``lvm create``
341 blank_journal_dev_paths
= [d
.abspath
for d
in self
.blank_journal_devs
]
342 data_vgs
= dict([(osd
['data']['path'], None) for osd
in self
.computed
['osds']])
344 # no common vg is found, create one with all the blank SSDs
345 if not self
.common_vg
:
346 journal_vg
= lvm
.create_vg(blank_journal_dev_paths
, name_prefix
='ceph-journals')
347 # a vg exists that can be extended
348 elif self
.common_vg
and blank_journal_dev_paths
:
349 journal_vg
= lvm
.extend_vg(self
.common_vg
, blank_journal_dev_paths
)
350 # one common vg with nothing else to extend can be used directly
352 journal_vg
= self
.common_vg
354 journal_size
= prepare
.get_journal_size(lv_format
=False)
356 # create 1 vg per data device first, mapping them to the device path,
357 # when the lv gets created later, it can create as many as needed (or
359 for osd
in self
.computed
['osds']:
360 vg
= data_vgs
.get(osd
['data']['path'])
362 vg
= lvm
.create_vg(osd
['data']['path'], name_prefix
='ceph-data')
363 data_vgs
[osd
['data']['path']] = vg
365 for osd
in self
.computed
['osds']:
366 data_path
= osd
['data']['path']
367 data_vg
= data_vgs
[data_path
]
368 data_lv_extents
= data_vg
.sizing(parts
=1)['extents']
369 data_uuid
= system
.generate_uuid()
370 data_lv
= lvm
.create_lv(
371 'osd-data', data_uuid
, vg
=data_vg
, extents
=data_lv_extents
)
372 journal_uuid
= system
.generate_uuid()
373 journal_lv
= lvm
.create_lv(
374 'osd-journal', journal_uuid
, vg
=journal_vg
, size
=journal_size
)
376 command
= ['--filestore', '--data']
377 command
.append('%s/%s' % (data_vg
.name
, data_lv
.name
))
378 command
.extend(['--journal', '%s/%s' % (journal_vg
.name
, journal_lv
.name
)])
379 if self
.args
.dmcrypt
:
380 command
.append('--dmcrypt')
381 if self
.args
.no_systemd
:
382 command
.append('--no-systemd')
383 if self
.args
.crush_device_class
:
384 command
.extend(['--crush-device-class', self
.args
.crush_device_class
])
386 command
.extend(['--osd-id', osd
['osd_id']])
388 if self
.args
.prepare
:
389 Prepare(command
).main()
391 Create(command
).main()