From 57c9e0bb414d7d0255ae99828632dbfe6daad41f Mon Sep 17 00:00:00 2001 From: Andreas Florath Date: Fri, 17 Mar 2017 18:10:04 +0000 Subject: [PATCH] Use stevedore for plugin config of block device This patch introduces stevedore plugin mechanism for use with the block device layer. This makes it possible that other projects pass in their own block device plugins. Change-Id: Id3ea56aaf75f5a20a4e1b6ac2a68adb12c56b574 Signed-off-by: Andreas Florath --- diskimage_builder/block_device/blockdevice.py | 17 +++---- .../block_device/level0/localloop.py | 8 +-- .../block_device/level1/partitioning.py | 5 +- diskimage_builder/block_device/plugin_base.py | 51 +++++++++++++++++++ requirements.txt | 1 + setup.cfg | 4 ++ 6 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 diskimage_builder/block_device/plugin_base.py diff --git a/diskimage_builder/block_device/blockdevice.py b/diskimage_builder/block_device/blockdevice.py index 897c0d4b..18b5e55e 100644 --- a/diskimage_builder/block_device/blockdevice.py +++ b/diskimage_builder/block_device/blockdevice.py @@ -15,13 +15,12 @@ import codecs from diskimage_builder.block_device.blockdevicesetupexception \ import BlockDeviceSetupException -from diskimage_builder.block_device.level0 import LocalLoop -from diskimage_builder.block_device.level1 import Partitioning from diskimage_builder.graph.digraph import Digraph import json import logging import os import shutil +from stevedore import extension import sys import yaml @@ -85,13 +84,6 @@ class BlockDevice(object): # type: ext4 # mount_point: / - # A dictionary to map sensible names to internal implementation. - cfg_type_map = { - 'local_loop': LocalLoop, - 'partitioning': Partitioning, - 'mkfs': 'not yet implemented', - } - def __init__(self, block_device_config, build_dir, default_image_size, default_image_dir): logger.debug("Creating BlockDevice object") @@ -109,6 +101,9 @@ class BlockDevice(object): "states/block-device") self.state_json_file_name \ = os.path.join(self.state_dir, "state.json") + self.plugin_manager = extension.ExtensionManager( + namespace='diskimage_builder.block_device.plugin', + invoke_on_load=False) def write_state(self, result): logger.debug("Write state [%s]" % self.state_json_file_name) @@ -138,11 +133,11 @@ class BlockDevice(object): # As the first step the configured objects are created # (if it exists) - if cfg_obj_name not in BlockDevice.cfg_type_map: + if cfg_obj_name not in self.plugin_manager: logger.error("Configured top level element [%s] " "does not exists." % cfg_obj_name) return 1 - cfg_obj = BlockDevice.cfg_type_map[cfg_obj_name]( + cfg_obj = self.plugin_manager[cfg_obj_name].plugin( cfg_obj_val, default_config) # At this point it is only possible to add the nodes: # adding the edges needs all nodes first. diff --git a/diskimage_builder/block_device/level0/localloop.py b/diskimage_builder/block_device/level0/localloop.py index 7fd21b3d..dfa543d8 100644 --- a/diskimage_builder/block_device/level0/localloop.py +++ b/diskimage_builder/block_device/level0/localloop.py @@ -14,6 +14,7 @@ from diskimage_builder.block_device.blockdevicesetupexception \ import BlockDeviceSetupException +from diskimage_builder.block_device.plugin_base import NodePluginBase from diskimage_builder.block_device.utils import parse_abs_size_spec from diskimage_builder.graph.digraph import Digraph import logging @@ -24,15 +25,13 @@ import subprocess logger = logging.getLogger(__name__) -class LocalLoop(Digraph.Node): +class LocalLoop(NodePluginBase): """Level0: Local loop image device handling. This class handles local loop devices that can be used for VM image installation. """ - type_string = "local_loop" - def __init__(self, config, default_config): logger.debug("Creating LocalLoop object; config [%s] " "default_config [%s]" % (config, default_config)) @@ -50,9 +49,6 @@ class LocalLoop(Digraph.Node): Digraph.Node.__init__(self, self.name) self.filename = os.path.join(self.image_dir, self.name + ".raw") - def insert_nodes(self, dg): - dg.add_node(self) - def insert_edges(self, dg): """Because this is created without base, there are no edges.""" pass diff --git a/diskimage_builder/block_device/level1/partitioning.py b/diskimage_builder/block_device/level1/partitioning.py index 28f2a688..2d79ddb5 100644 --- a/diskimage_builder/block_device/level1/partitioning.py +++ b/diskimage_builder/block_device/level1/partitioning.py @@ -16,6 +16,7 @@ import collections from diskimage_builder.block_device.blockdevicesetupexception \ import BlockDeviceSetupException from diskimage_builder.block_device.level1.mbr import MBR +from diskimage_builder.block_device.plugin_base import PluginBase from diskimage_builder.block_device.utils import parse_abs_size_spec from diskimage_builder.block_device.utils import parse_rel_size_spec from diskimage_builder.graph.digraph import Digraph @@ -67,9 +68,7 @@ class Partition(Digraph.Node): pass -class Partitioning(object): - - type_string = "partitioning" +class Partitioning(PluginBase): flag_boot = 1 flag_primary = 2 diff --git a/diskimage_builder/block_device/plugin_base.py b/diskimage_builder/block_device/plugin_base.py new file mode 100644 index 00000000..c92ff77c --- /dev/null +++ b/diskimage_builder/block_device/plugin_base.py @@ -0,0 +1,51 @@ +# 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 abc +from diskimage_builder.graph.digraph import Digraph +import six + + +@six.add_metaclass(abc.ABCMeta) +class PluginBase(object): + """Abstract base class for block device plugins""" + + def __init__(self, name): + """All plugins must have a name""" + self.name = name + + @abc.abstractmethod + def create(self, state, rollback): + """Create the block device plugin + + :param state: a dictionary to store results for this plugin. + These are used in two scenarios: other plugins + can use this to get information about the + result of the plugin and it can be used in + later runs of dib-block-device for cleaning up. + :type state: dict(str:?) + + :param rollback: a list of python functions that will be + called in reversed order to cleanup if there + occurs an error later within the same + dib-block-device run. + :type rollback: list(function) + """ + + +class NodePluginBase(PluginBase, Digraph.Node): + + def insert_nodes(self, dg): + """Adds self as a node to the given digraph""" + dg.add_node(self) diff --git a/requirements.txt b/requirements.txt index 2f2d1093..6077f0c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ pbr>=2.0.0 # Apache-2.0 PyYAML>=3.10.0 # MIT flake8<2.6.0,>=2.5.4 # MIT six>=1.9.0 # MIT +stevedore>=1.20.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 5eac8745..6a42eb0c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,3 +56,7 @@ console_scripts = disk-image-create = diskimage_builder.disk_image_create:main ramdisk-image-create = diskimage_builder.disk_image_create:main dib-run-parts = diskimage_builder.dib_run_parts:main + +diskimage_builder.block_device.plugin = + local_loop = diskimage_builder.block_device.level0.localloop:LocalLoop + partitioning = diskimage_builder.block_device.level1.partitioning:Partitioning