]>
Commit | Line | Data |
---|---|---|
d2e6a577 FG |
1 | from __future__ import print_function |
2 | import argparse | |
3 | import os | |
4 | import pkg_resources | |
5 | import sys | |
6 | import logging | |
7 | ||
d2e6a577 | 8 | from ceph_volume.decorators import catches |
f6b5b4d7 | 9 | from ceph_volume import log, devices, configuration, conf, exceptions, terminal, inventory, drive_group |
d2e6a577 FG |
10 | |
11 | ||
12 | class Volume(object): | |
13 | _help = """ | |
14 | ceph-volume: Deploy Ceph OSDs using different device technologies like lvm or | |
15 | physical disks. | |
16 | ||
d2e6a577 FG |
17 | Log Path: {log_path} |
18 | Ceph Conf: {ceph_path} | |
19 | ||
20 | {sub_help} | |
21 | {plugins} | |
22 | {environ_vars} | |
23 | {warning} | |
24 | """ | |
25 | ||
26 | def __init__(self, argv=None, parse=True): | |
1adf2230 AA |
27 | self.mapper = { |
28 | 'lvm': devices.lvm.LVM, | |
29 | 'simple': devices.simple.Simple, | |
92f5a8d4 | 30 | 'raw': devices.raw.Raw, |
91327a77 | 31 | 'inventory': inventory.Inventory, |
f6b5b4d7 | 32 | 'drive-group': drive_group.Deploy, |
1adf2230 | 33 | } |
d2e6a577 FG |
34 | self.plugin_help = "No plugins found/loaded" |
35 | if argv is None: | |
36 | self.argv = sys.argv | |
37 | else: | |
38 | self.argv = argv | |
39 | if parse: | |
40 | self.main(self.argv) | |
41 | ||
42 | def help(self, warning=False): | |
43 | warning = 'See "ceph-volume --help" for full list of options.' if warning else '' | |
44 | return self._help.format( | |
45 | warning=warning, | |
d2e6a577 FG |
46 | log_path=conf.log_path, |
47 | ceph_path=self.stat_ceph_conf(), | |
48 | plugins=self.plugin_help, | |
49 | sub_help=terminal.subhelp(self.mapper), | |
50 | environ_vars=self.get_environ_vars() | |
51 | ) | |
52 | ||
53 | def get_environ_vars(self): | |
54 | environ_vars = [] | |
55 | for key, value in os.environ.items(): | |
56 | if key.startswith('CEPH_'): | |
57 | environ_vars.append("%s=%s" % (key, value)) | |
58 | if not environ_vars: | |
59 | return '' | |
60 | else: | |
61 | environ_vars.insert(0, '\nEnviron Variables:') | |
62 | return '\n'.join(environ_vars) | |
63 | ||
64 | def enable_plugins(self): | |
65 | """ | |
66 | Load all plugins available, add them to the mapper and extend the help | |
67 | string with the information from each one | |
68 | """ | |
69 | plugins = _load_library_extensions() | |
70 | for plugin in plugins: | |
71 | self.mapper[plugin._ceph_volume_name_] = plugin | |
72 | self.plugin_help = '\n'.join(['%-19s %s\n' % ( | |
73 | plugin.name, getattr(plugin, 'help_menu', '')) | |
74 | for plugin in plugins]) | |
75 | if self.plugin_help: | |
76 | self.plugin_help = '\nPlugins:\n' + self.plugin_help | |
77 | ||
d2e6a577 FG |
78 | def load_log_path(self): |
79 | conf.log_path = os.getenv('CEPH_VOLUME_LOG_PATH', '/var/log/ceph') | |
80 | ||
81 | def stat_ceph_conf(self): | |
82 | try: | |
83 | configuration.load(conf.path) | |
84 | return terminal.green(conf.path) | |
85 | except exceptions.ConfigurationError as error: | |
86 | return terminal.red(error) | |
87 | ||
88 | def _get_split_args(self): | |
89 | subcommands = self.mapper.keys() | |
90 | slice_on_index = len(self.argv) + 1 | |
91 | pruned_args = self.argv[1:] | |
92 | for count, arg in enumerate(pruned_args): | |
93 | if arg in subcommands: | |
94 | slice_on_index = count | |
95 | break | |
96 | return pruned_args[:slice_on_index], pruned_args[slice_on_index:] | |
97 | ||
98 | @catches() | |
99 | def main(self, argv): | |
100 | # these need to be available for the help, which gets parsed super | |
101 | # early | |
91327a77 | 102 | configuration.load_ceph_conf_path() |
d2e6a577 FG |
103 | self.load_log_path() |
104 | self.enable_plugins() | |
105 | main_args, subcommand_args = self._get_split_args() | |
106 | # no flags where passed in, return the help menu instead of waiting for | |
107 | # argparse which will end up complaning that there are no args | |
108 | if len(argv) <= 1: | |
109 | print(self.help(warning=True)) | |
91327a77 | 110 | raise SystemExit(0) |
d2e6a577 FG |
111 | parser = argparse.ArgumentParser( |
112 | prog='ceph-volume', | |
113 | formatter_class=argparse.RawDescriptionHelpFormatter, | |
114 | description=self.help(), | |
115 | ) | |
116 | parser.add_argument( | |
117 | '--cluster', | |
118 | default='ceph', | |
119 | help='Cluster name (defaults to "ceph")', | |
120 | ) | |
121 | parser.add_argument( | |
122 | '--log-level', | |
123 | default='debug', | |
124 | help='Change the file log level (defaults to debug)', | |
125 | ) | |
126 | parser.add_argument( | |
127 | '--log-path', | |
128 | default='/var/log/ceph/', | |
129 | help='Change the log path (defaults to /var/log/ceph)', | |
130 | ) | |
131 | args = parser.parse_args(main_args) | |
132 | conf.log_path = args.log_path | |
133 | if os.path.isdir(conf.log_path): | |
134 | conf.log_path = os.path.join(args.log_path, 'ceph-volume.log') | |
135 | log.setup() | |
494da23a | 136 | log.setup_console() |
b32b8144 | 137 | logger = logging.getLogger(__name__) |
3a9019d9 | 138 | logger.info("Running command: ceph-volume %s %s", " ".join(main_args), " ".join(subcommand_args)) |
d2e6a577 FG |
139 | # set all variables from args and load everything needed according to |
140 | # them | |
91327a77 | 141 | configuration.load_ceph_conf_path(cluster_name=args.cluster) |
b32b8144 FG |
142 | try: |
143 | conf.ceph = configuration.load(conf.path) | |
144 | except exceptions.ConfigurationError as error: | |
145 | # we warn only here, because it is possible that the configuration | |
146 | # file is not needed, or that it will be loaded by some other means | |
147 | # (like reading from lvm tags) | |
148 | logger.exception('ignoring inability to load ceph.conf') | |
149 | terminal.red(error) | |
d2e6a577 FG |
150 | # dispatch to sub-commands |
151 | terminal.dispatch(self.mapper, subcommand_args) | |
152 | ||
153 | ||
154 | def _load_library_extensions(): | |
155 | """ | |
156 | Locate all setuptools entry points by the name 'ceph_volume_handlers' | |
157 | and initialize them. | |
158 | Any third-party library may register an entry point by adding the | |
159 | following to their setup.py:: | |
160 | ||
161 | entry_points = { | |
162 | 'ceph_volume_handlers': [ | |
163 | 'plugin_name = mylib.mymodule:Handler_Class', | |
164 | ], | |
165 | }, | |
166 | ||
167 | `plugin_name` will be used to load it as a sub command. | |
168 | """ | |
169 | logger = logging.getLogger('ceph_volume.plugins') | |
170 | group = 'ceph_volume_handlers' | |
171 | entry_points = pkg_resources.iter_entry_points(group=group) | |
172 | plugins = [] | |
173 | for ep in entry_points: | |
174 | try: | |
175 | logger.debug('loading %s' % ep.name) | |
176 | plugin = ep.load() | |
177 | plugin._ceph_volume_name_ = ep.name | |
178 | plugins.append(plugin) | |
179 | except Exception as error: | |
180 | logger.exception("Error initializing plugin %s: %s" % (ep, error)) | |
181 | return plugins |