~smoser/curtin/yakkety.lp1666986

« back to all changes in this revision

Viewing changes to curtin/commands/block_meta.py

  • Committer: Ryan Harper
  • Date: 2016-09-29 18:31:02 UTC
  • mto: (58.1.1 pkg)
  • mto: This revision was merged to the branch mainline in revision 59.
  • Revision ID: ryan.harper@canonical.com-20160929183102-qwcn73t5h6o0ag3k
Tags: upstream-0.1.0~bzr425
ImportĀ upstreamĀ versionĀ 0.1.0~bzr425

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
from collections import OrderedDict
19
19
from curtin import (block, config, util)
20
 
from curtin.block import mdadm
 
20
from curtin.block import (mdadm, mkfs, clear_holders, lvm)
21
21
from curtin.log import LOG
22
 
from curtin.block import mkfs
23
22
from curtin.reporter import events
24
23
 
25
24
from . import populate_one_subcmd
129
128
    return "mbr"
130
129
 
131
130
 
132
 
def get_holders(devname):
133
 
    # Look up any block device holders.
134
 
    # Handle devices and partitions as devnames (vdb, md0, vdb7)
135
 
    devname_sysfs = block.sys_block_path(devname)
136
 
    if devname_sysfs:
137
 
        holders = os.listdir(os.path.join(devname_sysfs, 'holders'))
138
 
        LOG.debug("devname '%s' had holders: %s", devname, ','.join(holders))
139
 
        return holders
140
 
 
141
 
    LOG.debug('get_holders: did not find sysfs path for %s', devname)
142
 
    return []
143
 
 
144
 
 
145
 
def clear_holders(sys_block_path):
146
 
    holders = os.listdir(os.path.join(sys_block_path, "holders"))
147
 
    LOG.info("clear_holders running on '%s', with holders '%s'" %
148
 
             (sys_block_path, holders))
149
 
    for holder in holders:
150
 
        # get path to holder in /sys/block, then clear it
151
 
        try:
152
 
            holder_realpath = os.path.realpath(
153
 
                os.path.join(sys_block_path, "holders", holder))
154
 
            clear_holders(holder_realpath)
155
 
        except IOError as e:
156
 
            # something might have already caused the holder to go away
157
 
            if util.is_file_not_found_exc(e):
158
 
                pass
159
 
            pass
160
 
 
161
 
    # detect what type of holder is using this volume and shut it down, need to
162
 
    # find more robust name of doing detection
163
 
    if "bcache" in sys_block_path:
164
 
        # bcache device
165
 
        part_devs = []
166
 
        for part_dev in glob.glob(os.path.join(sys_block_path,
167
 
                                               "slaves", "*", "dev")):
168
 
            with open(part_dev, "r") as fp:
169
 
                part_dev_id = fp.read().rstrip()
170
 
                part_devs.append(
171
 
                    os.path.split(os.path.realpath(os.path.join("/dev/block",
172
 
                                  part_dev_id)))[-1])
173
 
        for cache_dev in glob.glob("/sys/fs/bcache/*/bdev*"):
174
 
            for part_dev in part_devs:
175
 
                if part_dev in os.path.realpath(cache_dev):
176
 
                    # This is our bcache device, stop it, wait for udev to
177
 
                    # settle
178
 
                    with open(os.path.join(os.path.split(cache_dev)[0],
179
 
                              "stop"), "w") as fp:
180
 
                        LOG.info("stopping: %s" % fp)
181
 
                        fp.write("1")
182
 
                        udevadm_settle()
183
 
                    break
184
 
        for part_dev in part_devs:
185
 
            block.wipe_volume(os.path.join("/dev", part_dev),
186
 
                              mode="superblock")
187
 
 
188
 
    if os.path.exists(os.path.join(sys_block_path, "bcache")):
189
 
        # bcache device that isn't running, if it were, we would have found it
190
 
        # when we looked for holders
191
 
        try:
192
 
            with open(os.path.join(sys_block_path, "bcache", "set", "stop"),
193
 
                      "w") as fp:
194
 
                LOG.info("stopping: %s" % fp)
195
 
                fp.write("1")
196
 
        except IOError as e:
197
 
            if not util.is_file_not_found_exc(e):
198
 
                raise e
199
 
            with open(os.path.join(sys_block_path, "bcache", "stop"),
200
 
                      "w") as fp:
201
 
                LOG.info("stopping: %s" % fp)
202
 
                fp.write("1")
203
 
        udevadm_settle()
204
 
 
205
 
    if os.path.exists(os.path.join(sys_block_path, "md")):
206
 
        # md device
207
 
        block_dev_kname = block.path_to_kname(sys_block_path)
208
 
        block_dev = block.kname_to_path(block_dev_kname)
209
 
        mdadm.mdadm_stop(block_dev)
210
 
        mdadm.mdadm_remove(block_dev)
211
 
 
212
 
    elif os.path.exists(os.path.join(sys_block_path, "dm")):
213
 
        # Shut down any volgroups
214
 
        with open(os.path.join(sys_block_path, "dm", "name"), "r") as fp:
215
 
            name = fp.read().split('-')
216
 
        util.subp(["lvremove", "--force", name[0].rstrip(), name[1].rstrip()],
217
 
                  rcs=[0, 5])
218
 
        util.subp(["vgremove", name[0].rstrip()], rcs=[0, 5, 6])
219
 
 
220
 
 
221
131
def devsync(devpath):
222
132
    LOG.debug('devsync for %s', devpath)
223
133
    util.subp(['partprobe', devpath], rcs=[0, 1])
428
338
 
429
339
 
430
340
def disk_handler(info, storage_config):
 
341
    _dos_names = ['dos', 'msdos']
431
342
    ptable = info.get('ptable')
432
 
 
433
343
    disk = get_path_to_storage_volume(info.get('id'), storage_config)
434
344
 
435
 
    # Handle preserve flag
436
 
    if info.get('preserve'):
437
 
        if not ptable:
438
 
            # Don't need to check state, return
439
 
            return
440
 
 
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)
445
 
        try:
446
 
            (out, _err) = util.subp(["blkid", "-o", "export", disk],
447
 
                                    capture=True)
448
 
            current_ptable = next(l.split('=')[1] for l in out.splitlines()
449
 
                                  if 'TYPE' in l)
450
 
        except _possible_errors:
451
 
            try:
452
 
                (out, _err) = util.subp(["parted", disk, "--script", "print"],
453
 
                                        capture=True)
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."
465
 
                             % info.get('id'))
 
345
    if config.value_as_boolean(info.get('preserve')):
 
346
        # Handle preserve flag, verifying if ptable specified in config
 
347
        if config.value_as_boolean(ptable):
 
348
            current_ptable = block.get_part_table_type(disk)
 
349
            if not ((ptable in _dos_names and current_ptable in _dos_names) or
 
350
                    (ptable == 'gpt' and current_ptable == 'gpt')):
 
351
                raise ValueError(
 
352
                    "disk '%s' does not have correct partition table or "
 
353
                    "cannot be read, but preserve is set to true. "
 
354
                    "cannot continue installation." % info.get('id'))
466
355
        LOG.info("disk '%s' marked to be preserved, so keeping partition "
467
356
                 "table" % disk)
468
 
        return
469
 
 
470
 
    # Wipe the disk
471
 
    if info.get('wipe') and info.get('wipe') != "none":
472
 
        # The disk has a lable, clear all partitions
473
 
        mdadm.mdadm_assemble(scan=True)
474
 
        disk_sysfs_path = block.sys_block_path(disk)
475
 
        sysfs_partitions = list(
476
 
            os.path.split(prt)[0] for prt in
477
 
            glob.glob(os.path.join(disk_sysfs_path, '*', 'partition')))
478
 
        for partition in sysfs_partitions:
479
 
            clear_holders(partition)
480
 
            with open(os.path.join(partition, "dev"), "r") as fp:
481
 
                block_no = fp.read().rstrip()
482
 
            partition_path = os.path.realpath(
483
 
                os.path.join("/dev/block", block_no))
484
 
            block.wipe_volume(partition_path, mode=info.get('wipe'))
485
 
 
486
 
        clear_holders(disk_sysfs_path)
487
 
        block.wipe_volume(disk, mode=info.get('wipe'))
488
 
 
489
 
    # Create partition table on disk
490
 
    if info.get('ptable'):
491
 
        LOG.info("labeling device: '%s' with '%s' partition table", disk,
492
 
                 ptable)
493
 
        if ptable == "gpt":
494
 
            util.subp(["sgdisk", "--clear", disk])
495
 
        elif ptable == "msdos":
496
 
            util.subp(["parted", disk, "--script", "mklabel", "msdos"])
 
357
    else:
 
358
        # wipe the disk and create the partition table if instructed to do so
 
359
        if config.value_as_boolean(info.get('wipe')):
 
360
            block.wipe_volume(disk, mode=info.get('wipe'))
 
361
        if config.value_as_boolean(ptable):
 
362
            LOG.info("labeling device: '%s' with '%s' partition table", disk,
 
363
                     ptable)
 
364
            if ptable == "gpt":
 
365
                util.subp(["sgdisk", "--clear", disk])
 
366
            elif ptable in _dos_names:
 
367
                util.subp(["parted", disk, "--script", "mklabel", "msdos"])
 
368
            else:
 
369
                raise ValueError('invalid partition table type: %s', ptable)
497
370
 
498
371
    # Make the name if needed
499
372
    if info.get('name'):
621
494
        length_sectors = length_sectors + (logdisks * alignment_offset)
622
495
 
623
496
    # Handle preserve flag
624
 
    if info.get('preserve'):
 
497
    if config.value_as_boolean(info.get('preserve')):
625
498
        return
626
 
    elif storage_config.get(device).get('preserve'):
 
499
    elif config.value_as_boolean(storage_config.get(device).get('preserve')):
627
500
        raise NotImplementedError("Partition '%s' is not marked to be \
628
501
            preserved, but device '%s' is. At this time, preserving devices \
629
502
            but not also the partitions on the devices is not supported, \
666
539
    else:
667
540
        raise ValueError("parent partition has invalid partition table")
668
541
 
669
 
    # Wipe the partition if told to do so
670
 
    if info.get('wipe') and info.get('wipe') != "none":
671
 
        block.wipe_volume(
672
 
            get_path_to_storage_volume(info.get('id'), storage_config),
673
 
            mode=info.get('wipe'))
 
542
    # Wipe the partition if told to do so, do not wipe dos extended partitions
 
543
    # as this may damage the extended partition table
 
544
    if config.value_as_boolean(info.get('wipe')):
 
545
        if info.get('flag') == "extended":
 
546
            LOG.warn("extended partitions do not need wiping, so skipping: "
 
547
                     "'%s'" % info.get('id'))
 
548
        else:
 
549
            block.wipe_volume(
 
550
                get_path_to_storage_volume(info.get('id'), storage_config),
 
551
                mode=info.get('wipe'))
674
552
    # Make the name if needed
675
553
    if storage_config.get(device).get('name') and partition_type != 'extended':
676
554
        make_dname(info.get('id'), storage_config)
686
564
    volume_path = get_path_to_storage_volume(volume, storage_config)
687
565
 
688
566
    # Handle preserve flag
689
 
    if info.get('preserve'):
 
567
    if config.value_as_boolean(info.get('preserve')):
690
568
        # Volume marked to be preserved, not formatting
691
569
        return
692
570
 
768
646
                            storage_config))
769
647
 
770
648
    # Handle preserve flag
771
 
    if info.get('preserve'):
 
649
    if config.value_as_boolean(info.get('preserve')):
772
650
        # LVM will probably be offline, so start it
773
651
        util.subp(["vgchange", "-a", "y"])
774
652
        # Verify that volgroup exists and contains all specified devices
775
 
        current_paths = []
776
 
        (out, _err) = util.subp(["pvdisplay", "-C", "--separator", "=", "-o",
777
 
                                "vg_name,pv_name", "--noheadings"],
778
 
                                capture=True)
779
 
        for line in out.splitlines():
780
 
            if name in line:
781
 
                current_paths.append(line.split("=")[-1])
782
 
        if set(current_paths) != set(device_paths):
783
 
            raise ValueError("volgroup '%s' marked to be preserved, but does \
784
 
                             not exist or does not contain the right physical \
785
 
                             volumes" % info.get('id'))
 
653
        if set(lvm.get_pvols_in_volgroup(name)) != set(device_paths):
 
654
            raise ValueError("volgroup '%s' marked to be preserved, but does "
 
655
                             "not exist or does not contain the right "
 
656
                             "physical volumes" % info.get('id'))
786
657
    else:
787
658
        # Create vgrcreate command and run
788
 
        cmd = ["vgcreate", name]
789
 
        cmd.extend(device_paths)
790
 
        util.subp(cmd)
 
659
        # capture output to avoid printing it to log
 
660
        util.subp(['vgcreate', name] + device_paths, capture=True)
 
661
 
 
662
    # refresh lvmetad
 
663
    lvm.lvm_scan()
791
664
 
792
665
 
793
666
def lvm_partition_handler(info, storage_config):
797
670
        raise ValueError("lvm volgroup for lvm partition must be specified")
798
671
    if not name:
799
672
        raise ValueError("lvm partition name must be specified")
 
673
    if info.get('ptable'):
 
674
        raise ValueError("Partition tables on top of lvm logical volumes is "
 
675
                         "not supported")
800
676
 
801
677
    # Handle preserve flag
802
 
    if info.get('preserve'):
803
 
        (out, _err) = util.subp(["lvdisplay", "-C", "--separator", "=", "-o",
804
 
                                "lv_name,vg_name", "--noheadings"],
805
 
                                capture=True)
806
 
        found = False
807
 
        for line in out.splitlines():
808
 
            if name in line:
809
 
                if volgroup == line.split("=")[-1]:
810
 
                    found = True
811
 
                    break
812
 
        if not found:
813
 
            raise ValueError("lvm partition '%s' marked to be preserved, but \
814
 
                             does not exist or does not mach storage \
815
 
                             configuration" % info.get('id'))
 
678
    if config.value_as_boolean(info.get('preserve')):
 
679
        if name not in lvm.get_lvols_in_volgroup(volgroup):
 
680
            raise ValueError("lvm partition '%s' marked to be preserved, but "
 
681
                             "does not exist or does not mach storage "
 
682
                             "configuration" % info.get('id'))
816
683
    elif storage_config.get(info.get('volgroup')).get('preserve'):
817
 
        raise NotImplementedError("Lvm Partition '%s' is not marked to be \
818
 
            preserved, but volgroup '%s' is. At this time, preserving \
819
 
            volgroups but not also the lvm partitions on the volgroup is \
820
 
            not supported, because of the possibility of damaging lvm \
821
 
            partitions intended to be preserved." % (info.get('id'), volgroup))
 
684
        raise NotImplementedError(
 
685
            "Lvm Partition '%s' is not marked to be preserved, but volgroup "
 
686
            "'%s' is. At this time, preserving volgroups but not also the lvm "
 
687
            "partitions on the volgroup is not supported, because of the "
 
688
            "possibility of damaging lvm  partitions intended to be "
 
689
            "preserved." % (info.get('id'), volgroup))
822
690
    else:
823
691
        cmd = ["lvcreate", volgroup, "-n", name]
824
692
        if info.get('size'):
828
696
 
829
697
        util.subp(cmd)
830
698
 
831
 
    if info.get('ptable'):
832
 
        raise ValueError("Partition tables on top of lvm logical volumes is \
833
 
                         not supported")
 
699
    # refresh lvmetad
 
700
    lvm.lvm_scan()
834
701
 
835
702
    make_dname(info.get('id'), storage_config)
836
703
 
917
784
                  zip(spare_devices, spare_device_paths)))
918
785
 
919
786
    # Handle preserve flag
920
 
    if info.get('preserve'):
 
787
    if config.value_as_boolean(info.get('preserve')):
921
788
        # check if the array is already up, if not try to assemble
922
789
        if not mdadm.md_check(md_devname, raidlevel,
923
790
                              device_paths, spare_device_paths):
973
840
        raise ValueError("backing device and cache device for bcache"
974
841
                         " must be specified")
975
842
 
976
 
    # The bcache module is not loaded when bcache is installed by apt-get, so
977
 
    # we will load it now
978
 
    util.subp(["modprobe", "bcache"])
979
843
    bcache_sysfs = "/sys/fs/bcache"
980
844
    udevadm_settle(exists=bcache_sysfs)
981
845
 
995
859
                          bcache_device, expected)
996
860
                return
997
861
            LOG.debug('bcache device path not found: %s', expected)
998
 
            local_holders = get_holders(bcache_device)
 
862
            local_holders = clear_holders.get_holders(bcache_device)
999
863
            LOG.debug('got initial holders being "%s"', local_holders)
1000
864
            if len(local_holders) == 0:
1001
865
                raise ValueError("holders == 0 , expected non-zero")
1058
922
 
1059
923
        # via the holders we can identify which bcache device we just created
1060
924
        # for a given backing device
1061
 
        holders = get_holders(backing_device)
 
925
        holders = clear_holders.get_holders(backing_device)
1062
926
        if len(holders) != 1:
1063
927
            err = ('Invalid number {} of holding devices:'
1064
928
                   ' "{}"'.format(len(holders), holders))
1150
1014
    # set up reportstack
1151
1015
    stack_prefix = state.get('report_stack_prefix', '')
1152
1016
 
 
1017
    # shut down any already existing storage layers above any disks used in
 
1018
    # config that have 'wipe' set
 
1019
    with events.ReportEventStack(
 
1020
            name=stack_prefix, reporting_enabled=True, level='INFO',
 
1021
            description="removing previous storage devices"):
 
1022
        clear_holders.start_clear_holders_deps()
 
1023
        disk_paths = [get_path_to_storage_volume(k, storage_config_dict)
 
1024
                      for (k, v) in storage_config_dict.items()
 
1025
                      if v.get('type') == 'disk' and
 
1026
                      config.value_as_boolean(v.get('wipe')) and
 
1027
                      not config.value_as_boolean(v.get('preserve'))]
 
1028
        clear_holders.clear_holders(disk_paths)
 
1029
        # if anything was not properly shut down, stop installation
 
1030
        clear_holders.assert_clear(disk_paths)
 
1031
 
1153
1032
    for item_id, command in storage_config_dict.items():
1154
1033
        handler = command_handlers.get(command['type'])
1155
1034
        if not handler: