diff options
Diffstat (limited to 'src/ceph-volume/ceph_volume/main.py')
-rw-r--r-- | src/ceph-volume/ceph_volume/main.py | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/ceph-volume/ceph_volume/main.py b/src/ceph-volume/ceph_volume/main.py new file mode 100644 index 000000000..7868665ce --- /dev/null +++ b/src/ceph-volume/ceph_volume/main.py @@ -0,0 +1,183 @@ +from __future__ import print_function +import argparse +import os +import pkg_resources +import sys +import logging + +from ceph_volume.decorators import catches +from ceph_volume import log, devices, configuration, conf, exceptions, terminal, inventory, drive_group, activate + + +class Volume(object): + _help = """ +ceph-volume: Deploy Ceph OSDs using different device technologies like lvm or +physical disks. + +Log Path: {log_path} +Ceph Conf: {ceph_path} + +{sub_help} +{plugins} +{environ_vars} +{warning} + """ + + def __init__(self, argv=None, parse=True): + self.mapper = { + 'lvm': devices.lvm.LVM, + 'simple': devices.simple.Simple, + 'raw': devices.raw.Raw, + 'inventory': inventory.Inventory, + 'activate': activate.Activate, + 'drive-group': drive_group.Deploy, + } + self.plugin_help = "No plugins found/loaded" + if argv is None: + self.argv = sys.argv + else: + self.argv = argv + if parse: + self.main(self.argv) + + def help(self, warning=False): + warning = 'See "ceph-volume --help" for full list of options.' if warning else '' + return self._help.format( + warning=warning, + log_path=conf.log_path, + ceph_path=self.stat_ceph_conf(), + plugins=self.plugin_help, + sub_help=terminal.subhelp(self.mapper), + environ_vars=self.get_environ_vars() + ) + + def get_environ_vars(self): + environ_vars = [] + for key, value in os.environ.items(): + if key.startswith('CEPH_'): + environ_vars.append("%s=%s" % (key, value)) + if not environ_vars: + return '' + else: + environ_vars.insert(0, '\nEnviron Variables:') + return '\n'.join(environ_vars) + + def enable_plugins(self): + """ + Load all plugins available, add them to the mapper and extend the help + string with the information from each one + """ + plugins = _load_library_extensions() + for plugin in plugins: + self.mapper[plugin._ceph_volume_name_] = plugin + self.plugin_help = '\n'.join(['%-19s %s\n' % ( + plugin.name, getattr(plugin, 'help_menu', '')) + for plugin in plugins]) + if self.plugin_help: + self.plugin_help = '\nPlugins:\n' + self.plugin_help + + def load_log_path(self): + conf.log_path = os.getenv('CEPH_VOLUME_LOG_PATH', '/var/log/ceph') + + def stat_ceph_conf(self): + try: + configuration.load(conf.path) + return terminal.green(conf.path) + except exceptions.ConfigurationError as error: + return terminal.red(error) + + def _get_split_args(self): + subcommands = self.mapper.keys() + slice_on_index = len(self.argv) + 1 + pruned_args = self.argv[1:] + for count, arg in enumerate(pruned_args): + if arg in subcommands: + slice_on_index = count + break + return pruned_args[:slice_on_index], pruned_args[slice_on_index:] + + @catches() + def main(self, argv): + # these need to be available for the help, which gets parsed super + # early + configuration.load_ceph_conf_path() + self.load_log_path() + self.enable_plugins() + main_args, subcommand_args = self._get_split_args() + # no flags where passed in, return the help menu instead of waiting for + # argparse which will end up complaning that there are no args + if len(argv) <= 1: + print(self.help(warning=True)) + raise SystemExit(0) + parser = argparse.ArgumentParser( + prog='ceph-volume', + formatter_class=argparse.RawDescriptionHelpFormatter, + description=self.help(), + ) + parser.add_argument( + '--cluster', + default='ceph', + help='Cluster name (defaults to "ceph")', + ) + parser.add_argument( + '--log-level', + default='debug', + choices=['debug', 'info', 'warning', 'error', 'critical'], + help='Change the file log level (defaults to debug)', + ) + parser.add_argument( + '--log-path', + default='/var/log/ceph/', + help='Change the log path (defaults to /var/log/ceph)', + ) + args = parser.parse_args(main_args) + conf.log_path = args.log_path + if os.path.isdir(conf.log_path): + conf.log_path = os.path.join(args.log_path, 'ceph-volume.log') + log.setup(log_level=args.log_level) + log.setup_console() + logger = logging.getLogger(__name__) + logger.info("Running command: ceph-volume %s %s", " ".join(main_args), " ".join(subcommand_args)) + # set all variables from args and load everything needed according to + # them + configuration.load_ceph_conf_path(cluster_name=args.cluster) + try: + conf.ceph = configuration.load(conf.path) + except exceptions.ConfigurationError as error: + # we warn only here, because it is possible that the configuration + # file is not needed, or that it will be loaded by some other means + # (like reading from lvm tags) + logger.warning('ignoring inability to load ceph.conf', exc_info=1) + terminal.yellow(error) + # dispatch to sub-commands + terminal.dispatch(self.mapper, subcommand_args) + + +def _load_library_extensions(): + """ + Locate all setuptools entry points by the name 'ceph_volume_handlers' + and initialize them. + Any third-party library may register an entry point by adding the + following to their setup.py:: + + entry_points = { + 'ceph_volume_handlers': [ + 'plugin_name = mylib.mymodule:Handler_Class', + ], + }, + + `plugin_name` will be used to load it as a sub command. + """ + logger = logging.getLogger('ceph_volume.plugins') + group = 'ceph_volume_handlers' + entry_points = pkg_resources.iter_entry_points(group=group) + plugins = [] + for ep in entry_points: + try: + logger.debug('loading %s' % ep.name) + plugin = ep.load() + plugin._ceph_volume_name_ = ep.name + plugins.append(plugin) + except Exception as error: + logger.exception("Error initializing plugin %s: %s" % (ep, error)) + return plugins |