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