# Copyright 2017 Andreas Florath (andreas@florath.net) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import uuid from diskimage_builder.block_device.exception \ import BlockDeviceSetupException from diskimage_builder.block_device.plugin import NodeBase from diskimage_builder.block_device.plugin import PluginBase from diskimage_builder.block_device.utils import exec_sudo logger = logging.getLogger(__name__) # There is the need that filesystem labels are unique: # if not the boot and / or mount (with LABEL=) might fail. file_system_labels = set() # There is the need to check the length of the label of # the filesystem. The maximum length depends on the used filesystem. # This map provides information about the maximum label length. file_system_max_label_length = { "ext2": 16, "ext3": 16, "ext4": 16, "xfs": 12, "vfat": 11 } class FilesystemNode(NodeBase): def __init__(self, config): logger.debug("Create filesystem object; config [%s]" % config) super(FilesystemNode, self).__init__(config['name']) # Parameter check (mandatory) for pname in ['base', 'type']: if pname not in config: raise BlockDeviceSetupException( "Mkfs config needs [%s]" % pname) setattr(self, pname, config[pname]) # Parameter check (optional) for pname in ['label', 'opts', 'uuid']: setattr(self, pname, config[pname] if pname in config else None) if self.label is None: self.label = self.name # Historic reasons - this will hopefully vanish in one of # the next major releases if self.label == "cloudimg-rootfs" and self.type == "xfs": logger.warning("Default label [cloudimg-rootfs] too long for xfs " "file system - using [img-rootfs] instead") self.label = "img-rootfs" if self.label in file_system_labels: raise BlockDeviceSetupException( "File system label [%s] used more than once" % self.label) file_system_labels.add(self.label) if self.type in file_system_max_label_length: if file_system_max_label_length[self.type] < len(self.label): raise BlockDeviceSetupException( "Label [{label}] too long for filesystem [{type}]: " " [{len}] > [{max_len}]".format({ 'label': self.label, 'type': self.type, 'len': len(self.label), 'max': file_system_max_label_length[self.type]})) else: logger.warning("Length of label [%s] cannot be checked for " "filesystem [%s]: unknown max length" % (self.label, self.type)) logger.warning("Continue - but this might lead to an error") if self.opts is not None: self.opts = self.opts.strip().split(' ') if self.uuid is None: self.uuid = str(uuid.uuid4()) logger.debug("Filesystem created [%s]" % self) def get_edges(self): edge_from = [self.base] edge_to = [] return (edge_from, edge_to) def create(self, result, rollback): logger.info("create called; result [%s]" % result) cmd = ["mkfs"] cmd.extend(['-t', self.type]) if self.opts: cmd.extend(self.opts) cmd.extend(["-L", self.label]) if self.type in ('ext2', 'ext3', 'ext4'): cmd.extend(['-U', self.uuid]) elif self.type == 'xfs': cmd.extend(['-m', "uuid=%s" % self.uuid]) else: logger.warning("UUID will not be written for fs type [%s]" % self.type) if self.type in ('ext2', 'ext3', 'ext4', 'xfs'): cmd.append('-q') if 'blockdev' not in result: result['blockdev'] = {} device = result['blockdev'][self.base]['device'] cmd.append(device) logger.debug("Creating fs command [%s]" % (cmd)) exec_sudo(cmd) if 'filesys' not in result: result['filesys'] = {} result['filesys'][self.name] \ = {'uuid': self.uuid, 'label': self.label, 'fstype': self.type, 'opts': self.opts, 'device': device} class Mkfs(PluginBase): """Create a file system This block device module handles creating different file systems. """ def __init__(self, config, defaults): super(Mkfs, self).__init__() self.filesystems = {} fs = FilesystemNode(config) self.filesystems[fs.get_name()] = fs def get_nodes(self): nodes = [] for _, fs in self.filesystems.items(): nodes.append(fs) return nodes