132
def block_find_sysfs_path(devname):
133
# return the path in sys for device named devname
134
# support either short name ('sda') or full path /dev/sda
135
# sda -> /sys/class/block/sda
136
# sda1 -> /sys/class/block/sda/sda1
138
raise ValueError("empty devname provided to find_sysfs_path")
140
sys_class_block = '/sys/class/block/'
141
basename = os.path.basename(devname)
142
# try without parent blockdevice, then prepend parent
144
os.path.join(sys_class_block, basename),
145
os.path.join(sys_class_block,
146
re.split('[\d+]', basename)[0], basename),
149
# find path to devname directory in sysfs
152
if os.path.exists(path):
155
if devname_sysfs is None:
156
err = ('No sysfs path to device:'
157
' {}'.format(devname_sysfs))
159
raise ValueError(err)
164
132
def get_holders(devname):
165
133
# Look up any block device holders.
166
134
# Handle devices and partitions as devnames (vdb, md0, vdb7)
167
devname_sysfs = block_find_sysfs_path(devname)
135
devname_sysfs = block.sys_block_path(devname)
168
136
if devname_sysfs:
169
137
holders = os.listdir(os.path.join(devname_sysfs, 'holders'))
170
138
LOG.debug("devname '%s' had holders: %s", devname, ','.join(holders))
237
205
if os.path.exists(os.path.join(sys_block_path, "md")):
239
block_dev = os.path.join("/dev/", os.path.split(sys_block_path)[-1])
240
# if these fail its okay, the array might not be assembled and thats
207
block_dev_kname = block.path_to_kname(sys_block_path)
208
block_dev = block.kname_to_path(block_dev_kname)
242
209
mdadm.mdadm_stop(block_dev)
243
210
mdadm.mdadm_remove(block_dev)
265
232
raise OSError('Failed to find device at path: %s', devpath)
268
def determine_partition_kname(disk_kname, partition_number):
269
for dev_type in ["nvme", "mmcblk"]:
270
if disk_kname.startswith(dev_type):
271
partition_number = "p%s" % partition_number
273
return "%s%s" % (disk_kname, partition_number)
276
235
def determine_partition_number(partition_id, storage_config):
277
236
vol = storage_config.get(partition_id)
278
237
partnumber = vol.get('number')
304
263
return partnumber
266
def sanitize_dname(dname):
268
dnames should be sanitized before writing rule files, in case maas has
269
emitted a dname with a special character
271
only letters, numbers and '-' and '_' are permitted, as this will be
272
used for a device path. spaces are also not permitted
274
valid = string.digits + string.ascii_letters + '-_'
275
return ''.join(c if c in valid else '-' for c in dname)
307
278
def make_dname(volume, storage_config):
308
279
state = util.load_command_environment()
309
280
rules_dir = os.path.join(state['scratch'], "rules.d")
346
317
volgroup_name = storage_config.get(vol.get('volgroup')).get('name')
347
318
dname = "%s-%s" % (volgroup_name, dname)
348
319
rule.append(compose_udev_equality("ENV{DM_NAME}", dname))
349
rule.append("SYMLINK+=\"disk/by-dname/%s\"" % dname)
321
raise ValueError('cannot make dname for device with type: {}'
322
.format(vol.get('type')))
324
# note: this sanitization is done here instead of for all name attributes
325
# at the beginning of storage configuration, as some devices, such as
326
# lvm devices may use the name attribute and may permit special chars
327
sanitized = sanitize_dname(dname)
328
if sanitized != dname:
330
"dname modified to remove invalid chars. old: '{}' new: '{}'"
331
.format(dname, sanitized))
333
rule.append("SYMLINK+=\"disk/by-dname/%s\"" % sanitized)
350
334
LOG.debug("Writing dname udev rule '{}'".format(str(rule)))
351
335
util.ensure_dir(rules_dir)
352
with open(os.path.join(rules_dir, volume), "w") as fp:
353
fp.write(', '.join(rule))
336
rule_file = os.path.join(rules_dir, '{}.rules'.format(sanitized))
337
util.write_file(rule_file, ', '.join(rule))
356
340
def get_path_to_storage_volume(volume, storage_config):
368
352
partnumber = determine_partition_number(vol.get('id'), storage_config)
369
353
disk_block_path = get_path_to_storage_volume(vol.get('device'),
371
(base_path, disk_kname) = os.path.split(disk_block_path)
372
partition_kname = determine_partition_kname(disk_kname, partnumber)
373
volume_path = os.path.join(base_path, partition_kname)
355
disk_kname = block.path_to_kname(disk_block_path)
356
partition_kname = block.partition_kname(disk_kname, partnumber)
357
volume_path = block.kname_to_path(partition_kname)
374
358
devsync_vol = os.path.join(disk_block_path)
376
360
elif vol.get('type') == "disk":
419
403
# block devs are in the slaves dir there. Then, those blockdevs can be
420
404
# checked against the kname of the devs in the config for the desired
421
405
# bcache device. This is not very elegant though
422
backing_device_kname = os.path.split(get_path_to_storage_volume(
423
vol.get('backing_device'), storage_config))[-1]
406
backing_device_path = get_path_to_storage_volume(
407
vol.get('backing_device'), storage_config)
408
backing_device_kname = block.path_to_kname(backing_device_path)
424
409
sys_path = list(filter(lambda x: backing_device_kname in x,
425
410
glob.glob("/sys/block/bcache*/slaves/*")))[0]
426
411
while "bcache" not in os.path.split(sys_path)[-1]:
427
412
sys_path = os.path.split(sys_path)[0]
428
volume_path = os.path.join("/dev", os.path.split(sys_path)[-1])
413
bcache_kname = block.path_to_kname(sys_path)
414
volume_path = block.kname_to_path(bcache_kname)
429
415
LOG.debug('got bcache volume path {}'.format(volume_path))
452
438
# Don't need to check state, return
455
# Check state of current ptable
441
# Check state of current ptable, try to do this using blkid, but if
442
# blkid fails then try to fall back to using parted.
443
_possible_errors = (util.ProcessExecutionError, StopIteration,
444
IndexError, AttributeError)
457
446
(out, _err) = util.subp(["blkid", "-o", "export", disk],
459
except util.ProcessExecutionError:
460
raise ValueError("disk '%s' has no readable partition table or \
461
cannot be accessed, but preserve is set to true, so cannot \
463
current_ptable = list(filter(lambda x: "PTTYPE" in x,
464
out.splitlines()))[0].split("=")[-1]
465
if current_ptable == "dos" and ptable != "msdos" or \
466
current_ptable == "gpt" and ptable != "gpt":
467
raise ValueError("disk '%s' does not have correct \
468
partition table, but preserve is set to true, so not \
469
creating table, so not creating table." % info.get('id'))
470
LOG.info("disk '%s' marked to be preserved, so keeping partition \
448
current_ptable = next(l.split('=')[1] for l in out.splitlines()
450
except _possible_errors:
452
(out, _err) = util.subp(["parted", disk, "--script", "print"],
454
current_ptable = next(l.split()[-1] for l in out.splitlines()
455
if "Partition Table" in l)
456
except _possible_errors:
457
raise ValueError("disk '%s' has no readable partition table "
458
"or cannot be accessed, but preserve is set "
459
"to true, so cannot continue" % disk)
460
if not (current_ptable == ptable or
461
(current_ptable == "dos" and ptable == "msdos")):
462
raise ValueError("disk '%s' does not have correct "
463
"partition table, but preserve is "
464
"set to true, so not creating table."
466
LOG.info("disk '%s' marked to be preserved, so keeping partition "
475
471
if info.get('wipe') and info.get('wipe') != "none":
476
472
# The disk has a lable, clear all partitions
477
473
mdadm.mdadm_assemble(scan=True)
478
disk_kname = os.path.split(disk)[-1]
479
syspath_partitions = list(
474
disk_sysfs_path = block.sys_block_path(disk)
475
sysfs_partitions = list(
480
476
os.path.split(prt)[0] for prt in
481
glob.glob("/sys/block/%s/*/partition" % disk_kname))
482
for partition in syspath_partitions:
477
glob.glob(os.path.join(disk_sysfs_path, '*', 'partition')))
478
for partition in sysfs_partitions:
483
479
clear_holders(partition)
484
480
with open(os.path.join(partition, "dev"), "r") as fp:
485
481
block_no = fp.read().rstrip()
543
539
disk = get_path_to_storage_volume(device, storage_config)
544
540
partnumber = determine_partition_number(info.get('id'), storage_config)
546
disk_kname = os.path.split(
547
get_path_to_storage_volume(device, storage_config))[-1]
541
disk_kname = block.path_to_kname(disk)
542
disk_sysfs_path = block.sys_block_path(disk)
548
543
# consider the disks logical sector size when calculating sectors
550
prefix = "/sys/block/%s/queue/" % disk_kname
551
with open(prefix + "logical_block_size", "r") as f:
545
lbs_path = os.path.join(disk_sysfs_path, 'queue', 'logical_block_size')
546
with open(lbs_path, 'r') as f:
553
548
logical_block_size_bytes = int(l)
566
561
extended_part_no = determine_partition_number(
567
562
key, storage_config)
569
partition_kname = determine_partition_kname(
570
disk_kname, extended_part_no)
571
previous_partition = "/sys/block/%s/%s/" % \
572
(disk_kname, partition_kname)
564
pnum = extended_part_no
574
566
pnum = find_previous_partition(device, info['id'], storage_config)
575
LOG.debug("previous partition number for '%s' found to be '%s'",
576
info.get('id'), pnum)
577
partition_kname = determine_partition_kname(disk_kname, pnum)
578
previous_partition = "/sys/block/%s/%s/" % \
579
(disk_kname, partition_kname)
568
LOG.debug("previous partition number for '%s' found to be '%s'",
569
info.get('id'), pnum)
570
partition_kname = block.partition_kname(disk_kname, pnum)
571
previous_partition = os.path.join(disk_sysfs_path, partition_kname)
580
572
LOG.debug("previous partition: {}".format(previous_partition))
581
573
# XXX: sys/block/X/{size,start} is *ALWAYS* in 512b value
582
574
previous_size = util.load_file(os.path.join(previous_partition,
1058
1050
ensure_bcache_is_registered(cache_device, target_sysfs_path)
1060
1052
if backing_device:
1061
backing_device_sysfs = block_find_sysfs_path(backing_device)
1053
backing_device_sysfs = block.sys_block_path(backing_device)
1062
1054
target_sysfs_path = os.path.join(backing_device_sysfs, "bcache")
1063
1055
if not os.path.exists(os.path.join(backing_device_sysfs, "bcache")):
1064
1056
util.subp(["make-bcache", "-B", backing_device])