]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/settings.py
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / settings.py
CommitLineData
11fdf7f2 1# -*- coding: utf-8 -*-
11fdf7f2
TL
2import errno
3import inspect
f67539c2
TL
4from ast import literal_eval
5from typing import Any
11fdf7f2 6
cd265ab1
TL
7from mgr_module import CLICheckNonemptyFileInput
8
11fdf7f2
TL
9from . import mgr
10
11
f67539c2
TL
12class 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
11fdf7f2
TL
45class 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 """
f67539c2
TL
55 ENABLE_BROWSABLE_API = Setting(True, [bool])
56 REST_REQUESTS_TIMEOUT = Setting(45, [int])
11fdf7f2 57
adb31ebb 58 # AUTHENTICATION ATTEMPTS
f67539c2 59 ACCOUNT_LOCKOUT_ATTEMPTS = Setting(10, [int])
adb31ebb 60
11fdf7f2 61 # API auditing
f67539c2
TL
62 AUDIT_API_ENABLED = Setting(False, [bool])
63 AUDIT_API_LOG_PAYLOAD = Setting(True, [bool])
11fdf7f2
TL
64
65 # RGW settings
f67539c2
TL
66 RGW_API_ACCESS_KEY = Setting('', [dict, str])
67 RGW_API_SECRET_KEY = Setting('', [dict, str])
68 RGW_API_ADMIN_RESOURCE = Setting('admin', [str])
f67539c2 69 RGW_API_SSL_VERIFY = Setting(True, [bool])
11fdf7f2 70
20effc67
TL
71 # Ceph Issue Tracker API Access Key
72 ISSUE_TRACKER_API_KEY = Setting('', [str])
73
11fdf7f2 74 # Grafana settings
f67539c2
TL
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])
11fdf7f2
TL
81
82 # NFS Ganesha settings
f67539c2 83 GANESHA_CLUSTERS_RADOS_POOL_NAMESPACE = Setting('', [str])
11fdf7f2
TL
84
85 # Prometheus settings
f67539c2
TL
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])
11fdf7f2
TL
90
91 # iSCSI management settings
f67539c2 92 ISCSI_API_SSL_VERIFICATION = Setting(True, [bool])
11fdf7f2 93
9f95a23c
TL
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.
f67539c2 98 USER_PWD_EXPIRATION_SPAN = Setting(0, [int])
9f95a23c
TL
99 # warning levels to notify the user that the password is going
100 # to expire soon
f67539c2
TL
101 USER_PWD_EXPIRATION_WARNING_1 = Setting(10, [int])
102 USER_PWD_EXPIRATION_WARNING_2 = Setting(5, [int])
9f95a23c
TL
103
104 # Password policy
f67539c2 105 PWD_POLICY_ENABLED = Setting(True, [bool])
9f95a23c 106 # Individual checks
f67539c2
TL
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])
9f95a23c 114 # Settings
f67539c2
TL
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])
9f95a23c 121
aee94f69
TL
122 UNSAFE_TLS_v1_2 = Setting(False, [bool])
123
11fdf7f2
TL
124 @staticmethod
125 def has_default_value(name):
126 return getattr(Settings, name, None) is None or \
f67539c2 127 getattr(Settings, name) == getattr(Options, name).default_value
11fdf7f2
TL
128
129
130class SettingsMeta(type):
131 def __getattr__(cls, attr):
f67539c2
TL
132 setting = getattr(Options, attr)
133 return setting.cast(mgr.get_module_option(attr, setting.default_value))
11fdf7f2
TL
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
f67539c2 147class Settings(object, metaclass=SettingsMeta):
11fdf7f2
TL
148 pass
149
150
151def _options_command_map():
152 def filter_attr(member):
153 return not inspect.isroutine(member)
154
155 cmd_map = {}
f67539c2 156 for option, setting in inspect.getmembers(Options, filter_attr):
11fdf7f2
TL
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}
f67539c2 163 cmd_map[key_set] = {'name': option, 'type': setting.types_as_str()}
11fdf7f2
TL
164 cmd_map[key_reset] = {'name': option, 'type': None}
165 return cmd_map
166
167
168_OPTIONS_COMMAND_MAP = _options_command_map()
169
170
171def 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'):
cd265ab1 192 cmd_entry = {
11fdf7f2
TL
193 'cmd': '{} name=value,type={}'
194 .format(cmd, py2ceph(opt['type'])),
195 'desc': 'Set the {} option value'.format(opt['name']),
196 'perm': 'w'
cd265ab1
TL
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)
11fdf7f2
TL
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
214def options_schema_list():
215 def filter_attr(member):
216 return not inspect.isroutine(member)
217
218 result = []
f67539c2 219 for option, setting in inspect.getmembers(Options, filter_attr):
11fdf7f2
TL
220 if option.startswith('_'):
221 continue
f67539c2
TL
222 result.append({'name': option, 'default': setting.default_value,
223 'type': setting.types_as_str()})
11fdf7f2
TL
224
225 return result
226
227
cd265ab1 228def handle_option_command(cmd, inbuf):
11fdf7f2
TL
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'):
cd265ab1
TL
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']
f67539c2
TL
247 setting = getattr(Options, opt['name'])
248 setattr(Settings, opt['name'], setting.cast(value))
11fdf7f2 249 return 0, 'Option {} updated'.format(opt['name']), ''
cd265ab1
TL
250
251
252def handles_secret(cmd: str) -> bool:
253 return bool([cmd for secret_word in ['password', 'key'] if (secret_word in cmd)])
254
255
522d829b 256@CLICheckNonemptyFileInput(desc='password/secret')
cd265ab1
TL
257def get_secret(inbuf=None):
258 return inbuf, None, None