Merge "Configuration engine for image generation CLI"
This commit is contained in:
commit
9e25355d94
@ -104,10 +104,22 @@ class ImageRemote(remote.TerminalOnlyRemote):
|
||||
return self.guest.inspect_get_distro()
|
||||
|
||||
|
||||
def pack_image(plugin_name, plugin_version, image_path, root_drive=None,
|
||||
test_only=False, **kwargs):
|
||||
def setup_plugins():
|
||||
plugins_base.setup_plugins()
|
||||
|
||||
|
||||
def get_plugin_arguments(plugin_name):
|
||||
"""Gets plugin arguments, as a dict of version to argument list."""
|
||||
plugin = plugins_base.PLUGINS.get_plugin(plugin_name)
|
||||
versions = plugin.get_versions()
|
||||
return {version: plugin.get_image_arguments(version)
|
||||
for version in versions}
|
||||
|
||||
|
||||
def pack_image(image_path, plugin_name, plugin_version, image_arguments,
|
||||
root_drive=None, test_only=False):
|
||||
with ImageRemote(image_path, root_drive) as image_remote:
|
||||
plugin = plugins_base.PLUGINS.get_plugin(plugin_name)
|
||||
reconcile = not test_only
|
||||
plugin.pack_image(image_remote, reconcile=reconcile, env_map=kwargs)
|
||||
plugin = plugins_base.PLUGINS.get_plugin(plugin_name)
|
||||
plugin.pack_image(plugin_version, image_remote, reconcile=reconcile,
|
||||
image_arguments=image_arguments)
|
||||
|
@ -16,50 +16,36 @@ import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
import six
|
||||
|
||||
from sahara.cli.image_pack import api
|
||||
from sahara.i18n import _
|
||||
from sahara.i18n import _LI
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
CONF.register_cli_opts([
|
||||
cfg.StrOpt(
|
||||
'plugin',
|
||||
required=True,
|
||||
help="The name of the Sahara plugin for which you would like to "
|
||||
"generate an image. Use sahara-image-create -p PLUGIN -h to "
|
||||
"see a set of versions for a specific plugin."),
|
||||
cfg.StrOpt(
|
||||
'plugin-version',
|
||||
dest='plugin_version',
|
||||
required=True,
|
||||
help="The version of the Sahara plugin for which you would like to "
|
||||
"generate an image. Use sahara-image-create -p PLUGIN -v "
|
||||
"VERSION -h to see a full set of arguments for a specific plugin "
|
||||
"and version."),
|
||||
cfg.StrOpt(
|
||||
'image',
|
||||
required=True,
|
||||
help="The path to an image to modify. This image will be modified "
|
||||
"in-place: be sure to target a copy if you wish to maintain a "
|
||||
"clean master image."),
|
||||
help=_("The path to an image to modify. This image will be modified "
|
||||
"in-place: be sure to target a copy if you wish to maintain a "
|
||||
"clean master image.")),
|
||||
cfg.StrOpt(
|
||||
'root-filesystem',
|
||||
dest='root_fs',
|
||||
required=False,
|
||||
help="The filesystem to mount as the root volume on the image. No"
|
||||
"value is required if only one filesystem is detected."),
|
||||
help=_("The filesystem to mount as the root volume on the image. No"
|
||||
"value is required if only one filesystem is detected.")),
|
||||
cfg.BoolOpt(
|
||||
'test-only',
|
||||
dest='test_only',
|
||||
default=False,
|
||||
help="If this flag is set, no changes will be made to the image; "
|
||||
"instead, the script will fail if discrepancies are found "
|
||||
"between the image and the intended state."),
|
||||
])
|
||||
help=_("If this flag is set, no changes will be made to the image; "
|
||||
"instead, the script will fail if discrepancies are found "
|
||||
"between the image and the intended state."))])
|
||||
|
||||
|
||||
def unregister_extra_cli_opt(name):
|
||||
@ -75,6 +61,47 @@ for extra_opt in ["log-exchange", "host", "port"]:
|
||||
unregister_extra_cli_opt(extra_opt)
|
||||
|
||||
|
||||
def add_plugin_parsers(subparsers):
|
||||
api.setup_plugins()
|
||||
for plugin in CONF.plugins:
|
||||
args_by_version = api.get_plugin_arguments(plugin)
|
||||
if all(args is NotImplemented for version, args
|
||||
in six.iteritems(args_by_version)):
|
||||
continue
|
||||
plugin_parser = subparsers.add_parser(
|
||||
plugin, help=_('Image generation for the {plugin} plugin').format(
|
||||
plugin=plugin))
|
||||
version_parsers = plugin_parser.add_subparsers(
|
||||
title=_("Plugin version"),
|
||||
dest="version",
|
||||
help=_("Available versions"))
|
||||
for version, args in six.iteritems(args_by_version):
|
||||
if not args:
|
||||
continue
|
||||
version_parser = version_parsers.add_parser(
|
||||
version, help=_('{plugin} version {version}').format(
|
||||
plugin=plugin, version=version))
|
||||
for arg in args:
|
||||
arg_token = ("--%s" % arg.name if len(arg.name) > 1 else
|
||||
"-%s" % arg.name)
|
||||
version_parser.add_argument(arg_token,
|
||||
dest=arg.target_variable,
|
||||
help=arg.description,
|
||||
default=arg.default,
|
||||
required=arg.required,
|
||||
choices=arg.choices)
|
||||
version_parser.set_defaults(args={arg.target_variable
|
||||
for arg in args})
|
||||
|
||||
|
||||
command_opt = cfg.SubCommandOpt('plugin',
|
||||
title=_('Plugin'),
|
||||
help=_('Available plugins'),
|
||||
handler=add_plugin_parsers)
|
||||
|
||||
CONF.register_cli_opt(command_opt)
|
||||
|
||||
|
||||
def main():
|
||||
CONF(project='sahara')
|
||||
|
||||
@ -86,8 +113,13 @@ def main():
|
||||
api.set_logger(LOG)
|
||||
api.set_conf(CONF)
|
||||
|
||||
api.pack_image(CONF.plugin, CONF.plugin_version, CONF.image,
|
||||
plugin = CONF.plugin.name
|
||||
version = CONF.plugin.version
|
||||
args = CONF.plugin.args
|
||||
image_arguments = {arg: getattr(CONF.plugin, arg) for arg in args}
|
||||
|
||||
api.pack_image(CONF.image, plugin, version, image_arguments,
|
||||
CONF.root_fs, CONF.test_only)
|
||||
|
||||
LOG.info(_LI("Finished packing image for {plugin} at version {version}"
|
||||
).format(plugin=CONF.plugin, version=CONF.plugin_version))
|
||||
LOG.info(_LI("Finished packing image for {plugin} at version {version}")
|
||||
.format(plugin=plugin, version=version))
|
||||
|
@ -70,6 +70,19 @@ def validate_instance(instance, validators, reconcile=True, **kwargs):
|
||||
validator.validate(remote, reconcile=reconcile, **kwargs)
|
||||
|
||||
|
||||
class ImageArgument(object):
|
||||
"""An argument used by an image manifest."""
|
||||
|
||||
def __init__(self, name, target_variable, description=None,
|
||||
default=None, required=False, choices=None):
|
||||
self.name = name
|
||||
self.target_variable = target_variable
|
||||
self.description = description
|
||||
self.default = default
|
||||
self.required = required
|
||||
self.choices = choices
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ImageValidator(object):
|
||||
"""Validates the image spawned to an instance via a set of rules."""
|
||||
|
@ -93,6 +93,16 @@ class ProvisioningPluginBase(plugins_base.PluginInterface):
|
||||
def decommission_nodes(self, cluster, instances):
|
||||
pass
|
||||
|
||||
@plugins_base.optional
|
||||
def get_image_arguments(self, hadoop_version):
|
||||
"""Gets the argument set taken by the plugin's image generator"""
|
||||
return NotImplemented
|
||||
|
||||
@plugins_base.optional
|
||||
def pack_image(self, hadoop_version, remote,
|
||||
reconcile=True, image_arguments=None):
|
||||
pass
|
||||
|
||||
@plugins_base.optional
|
||||
def validate_images(self, cluster, reconcile=True):
|
||||
pass
|
||||
|
@ -43,8 +43,8 @@ class TestSaharaImagePackAPI(base.SaharaTestCase):
|
||||
get_plugin=mock.Mock(return_value=plugin))
|
||||
|
||||
api.pack_image(
|
||||
"plugin_name", "plugin_version", "image_path",
|
||||
root_drive=None, test_only=False)
|
||||
"image_path", "plugin_name", "plugin_version",
|
||||
{"anarg": "avalue"}, root_drive=None, test_only=False)
|
||||
|
||||
guest.add_drive_opts.assert_called_with("image_path", format="qcow2")
|
||||
guest.set_network.assert_called_with(True)
|
||||
@ -54,3 +54,19 @@ class TestSaharaImagePackAPI(base.SaharaTestCase):
|
||||
guest.sync.assert_called_once_with()
|
||||
guest.umount_all.assert_called_once_with()
|
||||
guest.close.assert_called_once_with()
|
||||
|
||||
@mock.patch('sahara.cli.image_pack.api.plugins_base')
|
||||
def test_get_plugin_arguments(self, mock_plugins_base):
|
||||
api.setup_plugins()
|
||||
mock_plugins_base.setup_plugins.assert_called_once_with()
|
||||
mock_PLUGINS = mock.Mock()
|
||||
mock_plugins_base.PLUGINS = mock_PLUGINS
|
||||
mock_plugin = mock.Mock()
|
||||
mock_plugin.get_versions = mock.Mock(return_value=['1'])
|
||||
mock_plugin.get_image_arguments = mock.Mock(
|
||||
return_value=["Argument!"])
|
||||
mock_PLUGINS.get_plugin = mock.Mock(return_value=mock_plugin)
|
||||
result = api.get_plugin_arguments('Plugin!')
|
||||
mock_plugin.get_versions.assert_called_once_with()
|
||||
mock_plugin.get_image_arguments.assert_called_once_with('1')
|
||||
self.assertEqual(result, {'1': ['Argument!']})
|
||||
|
Loading…
Reference in New Issue
Block a user