1 # -*- coding: utf-8 -*-
4 from ast
import literal_eval
7 from mgr_module
import CLICheckNonemptyFileInput
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.
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
29 def types_as_str(self
):
30 return ','.join([x
.__name
__ for x
in self
.types
])
32 def cast(self
, value
):
33 for type_index
, setting_type
in enumerate(self
.types
):
35 if setting_type
.__name
__ == 'bool' and str(value
).lower() == '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:
45 class Options(object):
47 If you need to store some configuration value please add the config option
48 name as a class attribute to this class.
52 GRAFANA_API_HOST = ('localhost', str)
53 GRAFANA_API_PORT = (3000, int)
55 ENABLE_BROWSABLE_API
= Setting(True, [bool])
56 REST_REQUESTS_TIMEOUT
= Setting(45, [int])
58 # AUTHENTICATION ATTEMPTS
59 ACCOUNT_LOCKOUT_ATTEMPTS
= Setting(10, [int])
62 AUDIT_API_ENABLED
= Setting(False, [bool])
63 AUDIT_API_LOG_PAYLOAD
= Setting(True, [bool])
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])
71 # Ceph Issue Tracker API Access Key
72 ISSUE_TRACKER_API_KEY
= Setting('', [str])
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])
82 # NFS Ganesha settings
83 GANESHA_CLUSTERS_RADOS_POOL_NAMESPACE
= Setting('', [str])
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])
91 # iSCSI management settings
92 ISCSI_API_SSL_VERIFICATION
= Setting(True, [bool])
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
101 USER_PWD_EXPIRATION_WARNING_1
= Setting(10, [int])
102 USER_PWD_EXPIRATION_WARNING_2
= Setting(5, [int])
105 PWD_POLICY_ENABLED
= Setting(True, [bool])
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])
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']),
123 def has_default_value(name
):
124 return getattr(Settings
, name
, None) is None or \
125 getattr(Settings
, name
) == getattr(Options
, name
).default_value
128 class SettingsMeta(type):
129 def __getattr__(cls
, attr
):
130 setting
= getattr(Options
, attr
)
131 return setting
.cast(mgr
.get_module_option(attr
, setting
.default_value
))
133 def __setattr__(cls
, attr
, value
):
134 if not attr
.startswith('_') and hasattr(Options
, attr
):
135 mgr
.set_module_option(attr
, str(value
))
137 setattr(SettingsMeta
, attr
, value
)
139 def __delattr__(cls
, attr
):
140 if not attr
.startswith('_') and hasattr(Options
, attr
):
141 mgr
.set_module_option(attr
, None)
144 # pylint: disable=no-init
145 class Settings(object, metaclass
=SettingsMeta
):
149 def _options_command_map():
150 def filter_attr(member
):
151 return not inspect
.isroutine(member
)
154 for option
, setting
in inspect
.getmembers(Options
, filter_attr
):
155 if option
.startswith('_'):
157 key_get
= 'dashboard get-{}'.format(option
.lower().replace('_', '-'))
158 key_set
= 'dashboard set-{}'.format(option
.lower().replace('_', '-'))
159 key_reset
= 'dashboard reset-{}'.format(option
.lower().replace('_', '-'))
160 cmd_map
[key_get
] = {'name': option
, 'type': None}
161 cmd_map
[key_set
] = {'name': option
, 'type': setting
.types_as_str()}
162 cmd_map
[key_reset
] = {'name': option
, 'type': None}
166 _OPTIONS_COMMAND_MAP
= _options_command_map()
169 def options_command_list():
171 This function generates a list of ``get`` and ``set`` commands
172 for each declared configuration option in class ``Options``.
182 for cmd
, opt
in _OPTIONS_COMMAND_MAP
.items():
183 if cmd
.startswith('dashboard get'):
185 'cmd': '{}'.format(cmd
),
186 'desc': 'Get the {} option value'.format(opt
['name']),
189 elif cmd
.startswith('dashboard set'):
191 'cmd': '{} name=value,type={}'
192 .format(cmd
, py2ceph(opt
['type'])),
193 'desc': 'Set the {} option value'.format(opt
['name']),
196 if handles_secret(cmd
):
197 cmd_entry
['cmd'] = cmd
198 cmd_entry
['desc'] = '{} read from -i <file>'.format(cmd_entry
['desc'])
199 cmd_list
.append(cmd_entry
)
200 elif cmd
.startswith('dashboard reset'):
201 desc
= 'Reset the {} option to its default value'.format(
204 'cmd': '{}'.format(cmd
),
212 def options_schema_list():
213 def filter_attr(member
):
214 return not inspect
.isroutine(member
)
217 for option
, setting
in inspect
.getmembers(Options
, filter_attr
):
218 if option
.startswith('_'):
220 result
.append({'name': option
, 'default': setting
.default_value
,
221 'type': setting
.types_as_str()})
226 def handle_option_command(cmd
, inbuf
):
227 if cmd
['prefix'] not in _OPTIONS_COMMAND_MAP
:
228 return -errno
.ENOSYS
, '', "Command not found '{}'".format(cmd
['prefix'])
230 opt
= _OPTIONS_COMMAND_MAP
[cmd
['prefix']]
232 if cmd
['prefix'].startswith('dashboard reset'):
233 delattr(Settings
, opt
['name'])
234 return 0, 'Option {} reset to default value "{}"'.format(
235 opt
['name'], getattr(Settings
, opt
['name'])), ''
236 elif cmd
['prefix'].startswith('dashboard get'):
237 return 0, str(getattr(Settings
, opt
['name'])), ''
238 elif cmd
['prefix'].startswith('dashboard set'):
239 if handles_secret(cmd
['prefix']):
240 value
, stdout
, stderr
= get_secret(inbuf
=inbuf
)
242 return value
, stdout
, stderr
245 setting
= getattr(Options
, opt
['name'])
246 setattr(Settings
, opt
['name'], setting
.cast(value
))
247 return 0, 'Option {} updated'.format(opt
['name']), ''
250 def handles_secret(cmd
: str) -> bool:
251 return bool([cmd
for secret_word
in ['password', 'key'] if (secret_word
in cmd
)])
254 @CLICheckNonemptyFileInput(desc
='password/secret')
255 def get_secret(inbuf
=None):
256 return inbuf
, None, None