~ubuntu-branches/ubuntu/saucy/cloud-init/saucy

« back to all changes in this revision

Viewing changes to cloudinit/config/cc_disk_setup.py

  • Committer: Scott Moser
  • Date: 2013-09-21 00:45:57 UTC
  • mfrom: (1.4.6)
  • Revision ID: smoser@ubuntu.com-20130921004557-o8zfowviyrqz02sd
* New upstream snapshot.
  * add 'disk_setup' config module for partitioning disks and
    creating filesystems. (LP: #1218506)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vi: ts=4 expandtab
 
2
#
 
3
#    Copyright (C) 2009-2010 Canonical Ltd.
 
4
#    Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
 
5
#
 
6
#    Author: Ben Howard <ben.howard@canonical.com>
 
7
#
 
8
#    This program is free software: you can redistribute it and/or modify
 
9
#    it under the terms of the GNU General Public License version 3, as
 
10
#    published by the Free Software Foundation.
 
11
#
 
12
#    This program is distributed in the hope that it will be useful,
 
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
#    GNU General Public License for more details.
 
16
#
 
17
#    You should have received a copy of the GNU General Public License
 
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
from cloudinit import util
 
20
from cloudinit.settings import PER_INSTANCE
 
21
import logging
 
22
import shlex
 
23
 
 
24
frequency = PER_INSTANCE
 
25
 
 
26
# Define the commands to use
 
27
UDEVADM_CMD = util.which('udevadm')
 
28
SFDISK_CMD = util.which("sfdisk")
 
29
LSBLK_CMD = util.which("lsblk")
 
30
BLKID_CMD = util.which("blkid")
 
31
BLKDEV_CMD = util.which("blockdev")
 
32
 
 
33
LOG = logging.getLogger(__name__)
 
34
 
 
35
 
 
36
def handle(_name, cfg, cloud, log, _args):
 
37
    """
 
38
    Call util.prep_disk for disk_setup cloud-config.
 
39
    See doc/examples/cloud-config_disk-setup.txt for documentation on the
 
40
    format.
 
41
    """
 
42
    disk_setup = cfg.get("disk_setup")
 
43
    if isinstance(disk_setup, dict):
 
44
        log.info("Partitioning disks.")
 
45
        for disk, definition in disk_setup.items():
 
46
            if not isinstance(definition, dict):
 
47
                log.warn("Invalid disk definition for %s" % disk)
 
48
                continue
 
49
 
 
50
            try:
 
51
                log.debug("Creating new partition table/disk")
 
52
                util.log_time(logfunc=LOG.debug,
 
53
                              msg="Creating partition on %s" % disk,
 
54
                              func=mkpart, args=(disk, cloud, definition))
 
55
            except Exception as e:
 
56
                util.logexc(LOG, "Failed partitioning operation\n%s" % e)
 
57
 
 
58
    fs_setup = cfg.get("fs_setup")
 
59
    if isinstance(fs_setup, list):
 
60
        log.info("Creating file systems.")
 
61
        for definition in fs_setup:
 
62
            if not isinstance(definition, dict):
 
63
                log.warn("Invalid file system definition: %s" % definition)
 
64
                continue
 
65
 
 
66
            try:
 
67
                log.debug("Creating new filesystem.")
 
68
                device = definition.get('device')
 
69
                util.log_time(logfunc=LOG.debug,
 
70
                              msg="Creating fs for %s" % device,
 
71
                              func=mkfs, args=(cloud, definition))
 
72
            except Exception as e:
 
73
                util.logexc(LOG, "Failed during filesystem operation\n%s" % e)
 
74
 
 
75
 
 
76
def is_default_device(name, cloud, fallback=None):
 
77
    """
 
78
    Ask the cloud datasource if the 'name' maps to a default
 
79
    device. If so, return that value, otherwise return 'name', or
 
80
    fallback if so defined.
 
81
    """
 
82
 
 
83
    _dev = None
 
84
    try:
 
85
        _dev = cloud.device_name_to_device(name)
 
86
    except Exception as e:
 
87
        util.logexc(LOG, "Failed to find mapping for %s" % e)
 
88
 
 
89
    if _dev:
 
90
        return _dev
 
91
 
 
92
    if fallback:
 
93
        return fallback
 
94
 
 
95
    return name
 
96
 
 
97
 
 
98
def value_splitter(values, start=None):
 
99
    """
 
100
    Returns the key/value pairs of output sent as string
 
101
    like:  FOO='BAR' HOME='127.0.0.1'
 
102
    """
 
103
    _values = shlex.split(values)
 
104
    if start:
 
105
        _values = _values[start:]
 
106
 
 
107
    for key, value in [x.split('=') for x in _values]:
 
108
        yield key, value
 
109
 
 
110
 
 
111
def device_type(device):
 
112
    """
 
113
    Return the device type of the device by calling lsblk.
 
114
    """
 
115
 
 
116
    lsblk_cmd = [LSBLK_CMD, '--pairs', '--nodeps', '--out', 'NAME,TYPE',
 
117
                 device]
 
118
    info = None
 
119
    try:
 
120
        info, _err = util.subp(lsblk_cmd)
 
121
    except Exception as e:
 
122
        raise Exception("Failed during disk check for %s\n%s" % (device, e))
 
123
 
 
124
    for key, value in value_splitter(info):
 
125
        if key.lower() == "type":
 
126
            return value.lower()
 
127
 
 
128
    return None
 
129
 
 
130
 
 
131
def is_device_valid(name, partition=False):
 
132
    """
 
133
    Check if the device is a valid device.
 
134
    """
 
135
    d_type = ""
 
136
    try:
 
137
        d_type = device_type(name)
 
138
    except:
 
139
        LOG.warn("Query against device %s failed" % name)
 
140
        return False
 
141
 
 
142
    if partition and d_type == 'part':
 
143
        return True
 
144
    elif not partition and d_type == 'disk':
 
145
        return True
 
146
    return False
 
147
 
 
148
 
 
149
def check_fs(device):
 
150
    """
 
151
    Check if the device has a filesystem on it
 
152
 
 
153
    Output of blkid is generally something like:
 
154
    /dev/sda: LABEL="Backup500G" UUID="..." TYPE="ext4"
 
155
 
 
156
    Return values are device, label, type, uuid
 
157
    """
 
158
    out, label, fs_type, uuid = None, None, None, None
 
159
 
 
160
    blkid_cmd = [BLKID_CMD, '-c', '/dev/null', device]
 
161
    try:
 
162
        out, _err = util.subp(blkid_cmd, rcs=[0, 2])
 
163
    except Exception as e:
 
164
        raise Exception("Failed during disk check for %s\n%s" % (device, e))
 
165
 
 
166
    if out:
 
167
        if len(out.splitlines()) == 1:
 
168
            for key, value in value_splitter(out, start=1):
 
169
                if key.lower() == 'label':
 
170
                    label = value
 
171
                elif key.lower() == 'type':
 
172
                    fs_type = value
 
173
                elif key.lower() == 'uuid':
 
174
                    uuid = value
 
175
 
 
176
    return label, fs_type, uuid
 
177
 
 
178
 
 
179
def is_filesystem(device):
 
180
    """
 
181
    Returns true if the device has a file system.
 
182
    """
 
183
    _, fs_type, _ = check_fs(device)
 
184
    return fs_type
 
185
 
 
186
 
 
187
def find_device_node(device, fs_type=None, label=None, valid_targets=None,
 
188
                     label_match=True):
 
189
    """
 
190
    Find a device that is either matches the spec, or the first
 
191
 
 
192
    The return is value is (<device>, <bool>) where the device is the
 
193
    device to use and the bool is whether the device matches the
 
194
    fs_type and label.
 
195
 
 
196
    Note: This works with GPT partition tables!
 
197
    """
 
198
    if not valid_targets:
 
199
        valid_targets = ['disk', 'part']
 
200
 
 
201
    lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE,FSTYPE,LABEL',
 
202
                 device]
 
203
    info = None
 
204
    try:
 
205
        info, _err = util.subp(lsblk_cmd)
 
206
    except Exception as e:
 
207
        raise Exception("Failed during disk check for %s\n%s" % (device, e))
 
208
 
 
209
    raw_device_used = False
 
210
    parts = [x for x in (info.strip()).splitlines() if len(x.split()) > 0]
 
211
 
 
212
    for part in parts:
 
213
        d = {'name': None,
 
214
             'type': None,
 
215
             'fstype': None,
 
216
             'label': None,
 
217
            }
 
218
 
 
219
        for key, value in value_splitter(part):
 
220
            d[key.lower()] = value
 
221
 
 
222
        if d['fstype'] == fs_type and \
 
223
            ((label_match and d['label'] == label) or not label_match):
 
224
            # If we find a matching device, we return that
 
225
            return ('/dev/%s' % d['name'], True)
 
226
 
 
227
        if d['type'] in valid_targets:
 
228
 
 
229
            if d['type'] != 'disk' or d['fstype']:
 
230
                raw_device_used = True
 
231
 
 
232
            if d['type'] == 'disk':
 
233
                # Skip the raw disk, its the default
 
234
                pass
 
235
 
 
236
            elif not d['fstype']:
 
237
                return ('/dev/%s' % d['name'], False)
 
238
 
 
239
    if not raw_device_used:
 
240
        return (device, False)
 
241
 
 
242
    LOG.warn("Failed to find device during available device search.")
 
243
    return (None, False)
 
244
 
 
245
 
 
246
def is_disk_used(device):
 
247
    """
 
248
    Check if the device is currently used. Returns false if there
 
249
    is no filesystem found on the disk.
 
250
    """
 
251
    lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE',
 
252
                 device]
 
253
    info = None
 
254
    try:
 
255
        info, _err = util.subp(lsblk_cmd)
 
256
    except Exception as e:
 
257
        # if we error out, we can't use the device
 
258
        util.logexc(LOG,
 
259
                    "Error checking for filesystem on %s\n%s" % (device, e))
 
260
        return True
 
261
 
 
262
    # If there is any output, then the device has something
 
263
    if len(info.splitlines()) > 1:
 
264
        return True
 
265
 
 
266
    return False
 
267
 
 
268
 
 
269
def get_hdd_size(device):
 
270
    """
 
271
    Returns the hard disk size.
 
272
    This works with any disk type, including GPT.
 
273
    """
 
274
 
 
275
    size_cmd = [SFDISK_CMD, '--show-size', device]
 
276
    size = None
 
277
    try:
 
278
        size, _err = util.subp(size_cmd)
 
279
    except Exception as e:
 
280
        raise Exception("Failed to get %s size\n%s" % (device, e))
 
281
 
 
282
    return int(size.strip())
 
283
 
 
284
 
 
285
def get_dyn_func(*args):
 
286
    """
 
287
    Call the appropriate function.
 
288
 
 
289
    The first value is the template for function name
 
290
    The second value is the template replacement
 
291
    The remain values are passed to the function
 
292
 
 
293
    For example: get_dyn_func("foo_%s", 'bar', 1, 2, 3,)
 
294
        would call "foo_bar" with args of 1, 2, 3
 
295
    """
 
296
    if len(args) < 2:
 
297
        raise Exception("Unable to determine dynamic funcation name")
 
298
 
 
299
    func_name = (args[0] % args[1])
 
300
    func_args = args[2:]
 
301
 
 
302
    try:
 
303
        if func_args:
 
304
            return globals()[func_name](*func_args)
 
305
        else:
 
306
            return globals()[func_name]
 
307
 
 
308
    except KeyError:
 
309
        raise Exception("No such function %s to call!" % func_name)
 
310
 
 
311
 
 
312
def check_partition_mbr_layout(device, layout):
 
313
    """
 
314
    Returns true if the partition layout matches the one on the disk
 
315
 
 
316
    Layout should be a list of values. At this time, this only
 
317
    verifies that the number of partitions and their labels is correct.
 
318
    """
 
319
 
 
320
    read_parttbl(device)
 
321
    prt_cmd = [SFDISK_CMD, "-l", device]
 
322
    try:
 
323
        out, _err = util.subp(prt_cmd, data="%s\n" % layout)
 
324
    except Exception as e:
 
325
        raise Exception("Error running partition command on %s\n%s" % (
 
326
                        device, e))
 
327
 
 
328
    found_layout = []
 
329
    for line in out.splitlines():
 
330
        _line = line.split()
 
331
        if len(_line) == 0:
 
332
            continue
 
333
 
 
334
        if device in _line[0]:
 
335
            # We don't understand extended partitions yet
 
336
            if _line[-1].lower() in ['extended', 'empty']:
 
337
                continue
 
338
 
 
339
            # Find the partition types
 
340
            type_label = None
 
341
            for x in sorted(range(1, len(_line)), reverse=True):
 
342
                if _line[x].isdigit() and _line[x] != '/':
 
343
                    type_label = _line[x]
 
344
                    break
 
345
 
 
346
            found_layout.append(type_label)
 
347
 
 
348
    if isinstance(layout, bool):
 
349
        # if we are using auto partitioning, or "True" be happy
 
350
        # if a single partition exists.
 
351
        if layout and len(found_layout) >= 1:
 
352
            return True
 
353
        return False
 
354
 
 
355
    else:
 
356
        if len(found_layout) != len(layout):
 
357
            return False
 
358
        else:
 
359
            # This just makes sure that the number of requested
 
360
            # partitions and the type labels are right
 
361
            for x in range(1, len(layout) + 1):
 
362
                if isinstance(layout[x - 1], tuple):
 
363
                    _, part_type = layout[x]
 
364
                    if int(found_layout[x]) != int(part_type):
 
365
                        return False
 
366
            return True
 
367
 
 
368
    return False
 
369
 
 
370
 
 
371
def check_partition_layout(table_type, device, layout):
 
372
    """
 
373
    See if the partition lay out matches.
 
374
 
 
375
    This is future a future proofing function. In order
 
376
    to add support for other disk layout schemes, add a
 
377
    function called check_partition_%s_layout
 
378
    """
 
379
    return get_dyn_func("check_partition_%s_layout", table_type, device,
 
380
                        layout)
 
381
 
 
382
 
 
383
def get_partition_mbr_layout(size, layout):
 
384
    """
 
385
    Calculate the layout of the partition table. Partition sizes
 
386
    are defined as percentage values or a tuple of percentage and
 
387
    partition type.
 
388
 
 
389
    For example:
 
390
        [ 33, [66: 82] ]
 
391
 
 
392
    Defines the first partition to be a size of 1/3 the disk,
 
393
    while the remaining 2/3's will be of type Linux Swap.
 
394
    """
 
395
 
 
396
    if not isinstance(layout, list) and isinstance(layout, bool):
 
397
        # Create a single partition
 
398
        return "0,"
 
399
 
 
400
    if (len(layout) == 0 and isinstance(layout, list)) or \
 
401
        not isinstance(layout, list):
 
402
        raise Exception("Partition layout is invalid")
 
403
 
 
404
    last_part_num = len(layout)
 
405
    if last_part_num > 4:
 
406
        raise Exception("Only simply partitioning is allowed.")
 
407
 
 
408
    part_definition = []
 
409
    part_num = 0
 
410
    for part in layout:
 
411
        part_type = 83  # Default to Linux
 
412
        percent = part
 
413
        part_num += 1
 
414
 
 
415
        if isinstance(part, list):
 
416
            if len(part) != 2:
 
417
                raise Exception("Partition was incorrectly defined: %s" % \
 
418
                                part)
 
419
            percent, part_type = part
 
420
 
 
421
        part_size = int((float(size) * (float(percent) / 100)) / 1024)
 
422
 
 
423
        if part_num == last_part_num:
 
424
            part_definition.append(",,%s" % part_type)
 
425
        else:
 
426
            part_definition.append(",%s,%s" % (part_size, part_type))
 
427
 
 
428
    sfdisk_definition = "\n".join(part_definition)
 
429
    if len(part_definition) > 4:
 
430
        raise Exception("Calculated partition definition is too big\n%s" %
 
431
                        sfdisk_definition)
 
432
 
 
433
    return sfdisk_definition
 
434
 
 
435
 
 
436
def get_partition_layout(table_type, size, layout):
 
437
    """
 
438
    Call the appropriate function for creating the table
 
439
    definition. Returns the table definition
 
440
 
 
441
    This is a future proofing function. To add support for
 
442
    other layouts, simply add a "get_partition_%s_layout"
 
443
    function.
 
444
    """
 
445
    return get_dyn_func("get_partition_%s_layout", table_type, size, layout)
 
446
 
 
447
 
 
448
def read_parttbl(device):
 
449
    """
 
450
    Use partprobe instead of 'udevadm'. Partprobe is the only
 
451
    reliable way to probe the partition table.
 
452
    """
 
453
    blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device]
 
454
    udev_cmd = [UDEVADM_CMD, 'settle']
 
455
    try:
 
456
        util.subp(udev_cmd)
 
457
        util.subp(blkdev_cmd)
 
458
        util.subp(udev_cmd)
 
459
    except Exception as e:
 
460
        util.logexc(LOG, "Failed reading the partition table %s" % e)
 
461
 
 
462
 
 
463
def exec_mkpart_mbr(device, layout):
 
464
    """
 
465
    Break out of mbr partition to allow for future partition
 
466
    types, i.e. gpt
 
467
    """
 
468
    # Create the partitions
 
469
    prt_cmd = [SFDISK_CMD, "--Linux", "-uM", device]
 
470
    try:
 
471
        util.subp(prt_cmd, data="%s\n" % layout)
 
472
    except Exception as e:
 
473
        raise Exception("Failed to partition device %s\n%s" % (device, e))
 
474
 
 
475
    read_parttbl(device)
 
476
 
 
477
 
 
478
def exec_mkpart(table_type, device, layout):
 
479
    """
 
480
    Fetches the function for creating the table type.
 
481
    This allows to dynamically find which function to call.
 
482
 
 
483
    Paramaters:
 
484
        table_type: type of partition table to use
 
485
        device: the device to work on
 
486
        layout: layout definition specific to partition table
 
487
    """
 
488
    return get_dyn_func("exec_mkpart_%s", table_type, device, layout)
 
489
 
 
490
 
 
491
def mkpart(device, cloud, definition):
 
492
    """
 
493
    Creates the partition table.
 
494
 
 
495
    Parameters:
 
496
        cloud: the cloud object
 
497
        definition: dictionary describing how to create the partition.
 
498
 
 
499
            The following are supported values in the dict:
 
500
                overwrite: Should the partition table be created regardless
 
501
                            of any pre-exisiting data?
 
502
                layout: the layout of the partition table
 
503
                table_type: Which partition table to use, defaults to MBR
 
504
                device: the device to work on.
 
505
    """
 
506
 
 
507
    LOG.debug("Checking values for %s definition" % device)
 
508
    overwrite = definition.get('overwrite', False)
 
509
    layout = definition.get('layout', False)
 
510
    table_type = definition.get('table_type', 'mbr')
 
511
    _device = is_default_device(device, cloud)
 
512
 
 
513
    # Check if the default device is a partition or not
 
514
    LOG.debug("Checking against default devices")
 
515
    if _device and (_device != device):
 
516
        if not is_device_valid(_device):
 
517
            _device = _device[:-1]
 
518
 
 
519
        if not is_device_valid(_device):
 
520
            raise Exception("Unable to find backing block device for %s" % \
 
521
                            device)
 
522
        else:
 
523
            LOG.debug("Mapped %s to physical device %s" % (device, _device))
 
524
            device = _device
 
525
 
 
526
    if (isinstance(layout, bool) and not layout) or not layout:
 
527
        LOG.debug("Device is not to be partitioned, skipping")
 
528
        return  # Device is not to be partitioned
 
529
 
 
530
    # This prevents you from overwriting the device
 
531
    LOG.debug("Checking if device %s is a valid device" % device)
 
532
    if not is_device_valid(device):
 
533
        raise Exception("Device %s is not a disk device!" % device)
 
534
 
 
535
    LOG.debug("Checking if device layout matches")
 
536
    if check_partition_layout(table_type, device, layout):
 
537
        LOG.debug("Device partitioning layout matches")
 
538
        return True
 
539
 
 
540
    LOG.debug("Checking if device is safe to partition")
 
541
    if not overwrite and (is_disk_used(device) or is_filesystem(device)):
 
542
        LOG.debug("Skipping partitioning on configured device %s" % device)
 
543
        return
 
544
 
 
545
    LOG.debug("Checking for device size")
 
546
    device_size = get_hdd_size(device)
 
547
 
 
548
    LOG.debug("Calculating partition layout")
 
549
    part_definition = get_partition_layout(table_type, device_size, layout)
 
550
    LOG.debug("   Layout is: %s" % part_definition)
 
551
 
 
552
    LOG.debug("Creating partition table on %s" % device)
 
553
    exec_mkpart(table_type, device, part_definition)
 
554
 
 
555
    LOG.debug("Partition table created for %s" % device)
 
556
 
 
557
 
 
558
def mkfs(cloud, fs_cfg):
 
559
    """
 
560
    Create a file system on the device.
 
561
 
 
562
        label: defines the label to use on the device
 
563
        fs_cfg: defines how the filesystem is to look
 
564
            The following values are required generally:
 
565
                device: which device or cloud defined default_device
 
566
                filesystem: which file system type
 
567
                overwrite: indiscriminately create the file system
 
568
                partition: when device does not define a partition,
 
569
                            setting this to a number will mean
 
570
                            device + partition. When set to 'auto', the
 
571
                            first free device or the first device which
 
572
                            matches both label and type will be used.
 
573
 
 
574
                            'any' means the first filesystem that matches
 
575
                            on the device.
 
576
 
 
577
            When 'cmd' is provided then no other parameter is required.
 
578
    """
 
579
    fs_cfg['partition'] = 'any'
 
580
    label = fs_cfg.get('label')
 
581
    device = fs_cfg.get('device')
 
582
    partition = str(fs_cfg.get('partition'))
 
583
    fs_type = fs_cfg.get('filesystem')
 
584
    fs_cmd = fs_cfg.get('cmd', [])
 
585
    fs_opts = fs_cfg.get('extra_opts', [])
 
586
    overwrite = fs_cfg.get('overwrite', False)
 
587
 
 
588
    # This allows you to define the default ephemeral or swap
 
589
    LOG.debug("Checking %s against default devices" % device)
 
590
    _device = is_default_device(label, cloud, fallback=device)
 
591
    if _device and (_device != device):
 
592
        if not is_device_valid(_device):
 
593
            raise Exception("Unable to find backing block device for %s" % \
 
594
                            device)
 
595
        else:
 
596
            LOG.debug("Mapped %s to physical device %s" % (device, _device))
 
597
            device = _device
 
598
 
 
599
    if not partition or partition.isdigit():
 
600
        # Handle manual definition of partition
 
601
        if partition.isdigit():
 
602
            device = "%s%s" % (device, partition)
 
603
            LOG.debug("Manual request of partition %s for %s" % (
 
604
                      partition, device))
 
605
 
 
606
        # Check to see if the fs already exists
 
607
        LOG.debug("Checking device %s" % device)
 
608
        check_label, check_fstype, _ = check_fs(device)
 
609
        LOG.debug("Device %s has %s %s" % (device, check_label, check_fstype))
 
610
 
 
611
        if check_label == label and check_fstype == fs_type:
 
612
            LOG.debug("Existing file system found at %s" % device)
 
613
 
 
614
            if not overwrite:
 
615
                LOG.warn("Device %s has required file system" % device)
 
616
                return
 
617
            else:
 
618
                LOG.warn("Destroying filesystem on %s" % device)
 
619
 
 
620
        else:
 
621
            LOG.debug("Device %s is cleared for formating" % device)
 
622
 
 
623
    elif partition and str(partition).lower() in ('auto', 'any'):
 
624
        # For auto devices, we match if the filesystem does exist
 
625
        odevice = device
 
626
        LOG.debug("Identifying device to create %s filesytem on" % label)
 
627
 
 
628
        # any mean pick the first match on the device with matching fs_type
 
629
        label_match = True
 
630
        if partition.lower() == 'any':
 
631
            label_match = False
 
632
 
 
633
        device, reuse = find_device_node(device, fs_type=fs_type, label=label,
 
634
                                         label_match=label_match)
 
635
        LOG.debug("Automatic device for %s identified as %s" % (
 
636
                  odevice, device))
 
637
 
 
638
        if reuse:
 
639
            LOG.debug("Found filesystem match, skipping formating.")
 
640
            return
 
641
 
 
642
        if not device:
 
643
            LOG.debug("No device aviable that matches request.")
 
644
            LOG.debug("Skipping fs creation for %s" % fs_cfg)
 
645
            return
 
646
 
 
647
    else:
 
648
        LOG.debug("Error in device identification handling.")
 
649
        return
 
650
 
 
651
    LOG.debug("File system %s will be created on %s" % (label, device))
 
652
 
 
653
    # Make sure the device is defined
 
654
    if not device:
 
655
        LOG.critical("Device is not known: %s" % fs_cfg)
 
656
        return
 
657
 
 
658
    # Check that we can create the FS
 
659
    if not label or not fs_type:
 
660
        LOG.debug("Command to create filesystem %s is bad. Skipping." % \
 
661
                  label)
 
662
 
 
663
    # Create the commands
 
664
    if fs_cmd:
 
665
        fs_cmd = fs_cfg['cmd'] % {'label': label,
 
666
                                  'filesystem': fs_type,
 
667
                                  'device': device,
 
668
                                 }
 
669
    else:
 
670
        # Find the mkfs command
 
671
        mkfs_cmd = util.which("mkfs.%s" % fs_type)
 
672
        if not mkfs_cmd:
 
673
            mkfs_cmd = util.which("mk%s" % fs_type)
 
674
 
 
675
        if not mkfs_cmd:
 
676
            LOG.critical("Unable to locate command to create filesystem.")
 
677
            return
 
678
 
 
679
        fs_cmd = [mkfs_cmd, device]
 
680
 
 
681
        if label:
 
682
            fs_cmd.extend(["-L", label])
 
683
 
 
684
    # Add the extends FS options
 
685
    if fs_opts:
 
686
        fs_cmd.extend(fs_opts)
 
687
 
 
688
    LOG.debug("Creating file system %s on %s" % (label, device))
 
689
    LOG.debug("     Using cmd: %s" % "".join(fs_cmd))
 
690
    try:
 
691
        util.subp(fs_cmd)
 
692
    except Exception as e:
 
693
        raise Exception("Failed to exec of '%s':\n%s" % (fs_cmd, e))