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