]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/settings.py
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / settings.py
1 # -*- coding: utf-8 -*-
2 import errno
3 import inspect
4 from ast import literal_eval
5 from typing import Any
6
7 from mgr_module import CLICheckNonemptyFileInput
8
9 from . import mgr
10
11
12 class Setting:
13 """
14 Setting representation that allows to set a default value and a list of allowed data types.
15 :param default_value: The name of the bucket.
16 :param types: a list consisting of the primary/preferred type and, optionally,
17 secondary/legacy types for backward compatibility.
18 """
19
20 def __init__(self, default_value: Any, types: list):
21 if not isinstance(types, list):
22 raise ValueError('Setting types must be a list.')
23 default_value_type = type(default_value)
24 if default_value_type not in types:
25 raise ValueError('Default value type not allowed.')
26 self.default_value = default_value
27 self.types = types
28
29 def types_as_str(self):
30 return ','.join([x.__name__ for x in self.types])
31
32 def cast(self, value):
33 for type_index, setting_type in enumerate(self.types):
34 try:
35 if setting_type.__name__ == 'bool' and str(value).lower() == 'false':
36 return False
37 elif setting_type.__name__ == 'dict':
38 return literal_eval(value)
39 return setting_type(value)
40 except (SyntaxError, TypeError, ValueError) as error:
41 if type_index == len(self.types) - 1:
42 raise error
43
44
45 class Options(object):
46 """
47 If you need to store some configuration value please add the config option
48 name as a class attribute to this class.
49
50 Example::
51
52 GRAFANA_API_HOST = ('localhost', str)
53 GRAFANA_API_PORT = (3000, int)
54 """
55 ENABLE_BROWSABLE_API = Setting(True, [bool])
56 REST_REQUESTS_TIMEOUT = Setting(45, [int])
57
58 # AUTHENTICATION ATTEMPTS
59 ACCOUNT_LOCKOUT_ATTEMPTS = Setting(10, [int])
60
61 # API auditing
62 AUDIT_API_ENABLED = Setting(False, [bool])
63 AUDIT_API_LOG_PAYLOAD = Setting(True, [bool])
64
65 # RGW settings
66 RGW_API_ACCESS_KEY = Setting('', [dict, str])
67 RGW_API_SECRET_KEY = Setting('', [dict, str])
68 RGW_API_ADMIN_RESOURCE = Setting('admin', [str])
69 RGW_API_SSL_VERIFY = Setting(True, [bool])
70
71 # Ceph Issue Tracker API Access Key
72 ISSUE_TRACKER_API_KEY = Setting('', [str])
73
74 # Grafana settings
75 GRAFANA_API_URL = Setting('', [str])
76 GRAFANA_FRONTEND_API_URL = Setting('', [str])
77 GRAFANA_API_USERNAME = Setting('admin', [str])
78 GRAFANA_API_PASSWORD = Setting('admin', [str])
79 GRAFANA_API_SSL_VERIFY = Setting(True, [bool])
80 GRAFANA_UPDATE_DASHBOARDS = Setting(False, [bool])
81
82 # NFS Ganesha settings
83 GANESHA_CLUSTERS_RADOS_POOL_NAMESPACE = Setting('', [str])
84
85 # Prometheus settings
86 PROMETHEUS_API_HOST = Setting('', [str])
87 PROMETHEUS_API_SSL_VERIFY = Setting(True, [bool])
88 ALERTMANAGER_API_HOST = Setting('', [str])
89 ALERTMANAGER_API_SSL_VERIFY = Setting(True, [bool])
90
91 # iSCSI management settings
92 ISCSI_API_SSL_VERIFICATION = Setting(True, [bool])
93
94 # user management settings
95 # Time span of user passwords to expire in days.
96 # The default value is '0' which means that user passwords are
97 # never going to expire.
98 USER_PWD_EXPIRATION_SPAN = Setting(0, [int])
99 # warning levels to notify the user that the password is going
100 # to expire soon
101 USER_PWD_EXPIRATION_WARNING_1 = Setting(10, [int])
102 USER_PWD_EXPIRATION_WARNING_2 = Setting(5, [int])
103
104 # Password policy
105 PWD_POLICY_ENABLED = Setting(True, [bool])
106 # Individual checks
107 PWD_POLICY_CHECK_LENGTH_ENABLED = Setting(True, [bool])
108 PWD_POLICY_CHECK_OLDPWD_ENABLED = Setting(True, [bool])
109 PWD_POLICY_CHECK_USERNAME_ENABLED = Setting(False, [bool])
110 PWD_POLICY_CHECK_EXCLUSION_LIST_ENABLED = Setting(False, [bool])
111 PWD_POLICY_CHECK_COMPLEXITY_ENABLED = Setting(False, [bool])
112 PWD_POLICY_CHECK_SEQUENTIAL_CHARS_ENABLED = Setting(False, [bool])
113 PWD_POLICY_CHECK_REPETITIVE_CHARS_ENABLED = Setting(False, [bool])
114 # Settings
115 PWD_POLICY_MIN_LENGTH = Setting(8, [int])
116 PWD_POLICY_MIN_COMPLEXITY = Setting(10, [int])
117 PWD_POLICY_EXCLUSION_LIST = Setting(','.join(['osd', 'host', 'dashboard', 'pool',
118 'block', 'nfs', 'ceph', 'monitors',
119 'gateway', 'logs', 'crush', 'maps']),
120 [str])
121
122 UNSAFE_TLS_v1_2 = Setting(False, [bool])
123
124 @staticmethod
125 def has_default_value(name):
126 return getattr(Settings, name, None) is None or \
127 getattr(Settings, name) == getattr(Options, name).default_value
128
129
130 class SettingsMeta(type):
131 def __getattr__(cls, attr):
132 setting = getattr(Options, attr)
133 return setting.cast(mgr.get_module_option(attr, setting.default_value))
134
135 def __setattr__(cls, attr, value):
136 if not attr.startswith('_') and hasattr(Options, attr):
137 mgr.set_module_option(attr, str(value))
138 else:
139 setattr(SettingsMeta, attr, value)
140
141 def __delattr__(cls, attr):
142 if not attr.startswith('_') and hasattr(Options, attr):
143 mgr.set_module_option(attr, None)
144
145
146 # pylint: disable=no-init
147 class Settings(object, metaclass=SettingsMeta):
148 pass
149
150
151 def _options_command_map():
152 def filter_attr(member):
153 return not inspect.isroutine(member)
154
155 cmd_map = {}
156 for option, setting in inspect.getmembers(Options, filter_attr):
157 if option.startswith('_'):
158 continue
159 key_get = 'dashboard get-{}'.format(option.lower().replace('_', '-'))
160 key_set = 'dashboard set-{}'.format(option.lower().replace('_', '-'))
161 key_reset = 'dashboard reset-{}'.format(option.lower().replace('_', '-'))
162 cmd_map[key_get] = {'name': option, 'type': None}
163 cmd_map[key_set] = {'name': option, 'type': setting.types_as_str()}
164 cmd_map[key_reset] = {'name': option, 'type': None}
165 return cmd_map
166
167
168 _OPTIONS_COMMAND_MAP = _options_command_map()
169
170
171 def options_command_list():
172 """
173 This function generates a list of ``get`` and ``set`` commands
174 for each declared configuration option in class ``Options``.
175 """
176 def py2ceph(pytype):
177 if pytype == str:
178 return 'CephString'
179 elif pytype == int:
180 return 'CephInt'
181 return 'CephString'
182
183 cmd_list = []
184 for cmd, opt in _OPTIONS_COMMAND_MAP.items():
185 if cmd.startswith('dashboard get'):
186 cmd_list.append({
187 'cmd': '{}'.format(cmd),
188 'desc': 'Get the {} option value'.format(opt['name']),
189 'perm': 'r'
190 })
191 elif cmd.startswith('dashboard set'):
192 cmd_entry = {
193 'cmd': '{} name=value,type={}'
194 .format(cmd, py2ceph(opt['type'])),
195 'desc': 'Set the {} option value'.format(opt['name']),
196 'perm': 'w'
197 }
198 if handles_secret(cmd):
199 cmd_entry['cmd'] = cmd
200 cmd_entry['desc'] = '{} read from -i <file>'.format(cmd_entry['desc'])
201 cmd_list.append(cmd_entry)
202 elif cmd.startswith('dashboard reset'):
203 desc = 'Reset the {} option to its default value'.format(
204 opt['name'])
205 cmd_list.append({
206 'cmd': '{}'.format(cmd),
207 'desc': desc,
208 'perm': 'w'
209 })
210
211 return cmd_list
212
213
214 def options_schema_list():
215 def filter_attr(member):
216 return not inspect.isroutine(member)
217
218 result = []
219 for option, setting in inspect.getmembers(Options, filter_attr):
220 if option.startswith('_'):
221 continue
222 result.append({'name': option, 'default': setting.default_value,
223 'type': setting.types_as_str()})
224
225 return result
226
227
228 def handle_option_command(cmd, inbuf):
229 if cmd['prefix'] not in _OPTIONS_COMMAND_MAP:
230 return -errno.ENOSYS, '', "Command not found '{}'".format(cmd['prefix'])
231
232 opt = _OPTIONS_COMMAND_MAP[cmd['prefix']]
233
234 if cmd['prefix'].startswith('dashboard reset'):
235 delattr(Settings, opt['name'])
236 return 0, 'Option {} reset to default value "{}"'.format(
237 opt['name'], getattr(Settings, opt['name'])), ''
238 elif cmd['prefix'].startswith('dashboard get'):
239 return 0, str(getattr(Settings, opt['name'])), ''
240 elif cmd['prefix'].startswith('dashboard set'):
241 if handles_secret(cmd['prefix']):
242 value, stdout, stderr = get_secret(inbuf=inbuf)
243 if stderr:
244 return value, stdout, stderr
245 else:
246 value = cmd['value']
247 setting = getattr(Options, opt['name'])
248 setattr(Settings, opt['name'], setting.cast(value))
249 return 0, 'Option {} updated'.format(opt['name']), ''
250
251
252 def handles_secret(cmd: str) -> bool:
253 return bool([cmd for secret_word in ['password', 'key'] if (secret_word in cmd)])
254
255
256 @CLICheckNonemptyFileInput(desc='password/secret')
257 def get_secret(inbuf=None):
258 return inbuf, None, None