from .controllers.auth import Auth
from .tools import load_controllers, json_error_page, SessionExpireAtBrowserCloseTool, \
NotificationQueue
+from .settings import options_command_list, handle_option_command
# cherrypy likes to sys.exit on error. don't let it take us down too!
'perm': 'w'
}
]
+ COMMANDS.extend(options_command_list())
@property
def url_prefix(self):
logger.info('Stopped server')
def handle_command(self, cmd):
+ res = handle_option_command(cmd)
+ if res[0] != -errno.ENOSYS:
+ return res
if cmd['prefix'] == 'dashboard set-login-credentials':
Auth.set_login_credentials(cmd['username'], cmd['password'])
return 0, 'Username and password updated', ''
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import errno
+import inspect
+from six import add_metaclass
+
+from . import mgr
+
+
+class Options(object):
+ """
+ If you need to store some configuration value please add the config option
+ name as a class attribute to this class.
+
+ Example::
+
+ GRAFANA_API_HOST = ('localhost', str)
+ GRAFANA_API_PORT = (3000, int)
+ """
+ pass
+
+
+class SettingsMeta(type):
+ def __getattr__(cls, attr):
+ default, stype = getattr(Options, attr)
+ return stype(mgr.get_config(attr, default))
+
+ def __setattr__(cls, attr, value):
+ if not attr.startswith('_') and hasattr(Options, attr):
+ mgr.set_config(attr, str(value))
+ else:
+ setattr(SettingsMeta, attr, value)
+
+
+# pylint: disable=no-init
+@add_metaclass(SettingsMeta)
+class Settings(object):
+ pass
+
+
+def _options_command_map():
+ def filter_attr(member):
+ return not inspect.isroutine(member)
+
+ cmd_map = {}
+ for option, value in inspect.getmembers(Options, filter_attr):
+ if option.startswith('_'):
+ continue
+ key_get = 'dashboard get-{}'.format(option.lower().replace('_', '-'))
+ key_set = 'dashboard set-{}'.format(option.lower().replace('_', '-'))
+ cmd_map[key_get] = {'name': option, 'type': None}
+ cmd_map[key_set] = {'name': option, 'type': value[1]}
+ return cmd_map
+
+
+_OPTIONS_COMMAND_MAP = _options_command_map()
+
+
+def options_command_list():
+ """
+ This function generates a list of ``get`` and ``set`` commands
+ for each declared configuration option in class ``Options``.
+ """
+ def py2ceph(pytype):
+ if pytype == str:
+ return 'CephString'
+ elif pytype == int:
+ return 'CephInt'
+ return 'CephString'
+
+ cmd_list = []
+ for cmd, opt in _OPTIONS_COMMAND_MAP.items():
+ if not opt['type']:
+ cmd_list.append({
+ 'cmd': '{}'.format(cmd),
+ 'desc': 'Get the {} option value'.format(opt['name']),
+ 'perm': 'r'
+ })
+ else:
+ cmd_list.append({
+ 'cmd': '{} name=value,type={}'
+ .format(cmd, py2ceph(opt['type'])),
+ 'desc': 'Set the {} option value'.format(opt['name']),
+ 'perm': 'w'
+ })
+
+ return cmd_list
+
+
+def handle_option_command(cmd):
+ if cmd['prefix'] not in _OPTIONS_COMMAND_MAP:
+ return (-errno.ENOSYS, '', "Command not found '{}'".format(cmd['prefix']))
+
+ opt = _OPTIONS_COMMAND_MAP[cmd['prefix']]
+ if not opt['type']:
+ # get option
+ return 0, str(getattr(Settings, opt['name'])), ''
+
+ # set option
+ setattr(Settings, opt['name'], opt['type'](cmd['value']))
+ return 0, 'Option {} updated'.format(opt['name']), ''
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import errno
+import unittest
+
+from .. import mgr
+from .. import settings
+from ..settings import Settings, handle_option_command
+
+
+class SettingsTest(unittest.TestCase):
+ CONFIG_KEY_DICT = {}
+
+ @classmethod
+ def setUpClass(cls):
+ # pylint: disable=protected-access
+ settings.Options.GRAFANA_API_HOST = ('localhost', str)
+ settings.Options.GRAFANA_API_PORT = (3000, int)
+ settings._OPTIONS_COMMAND_MAP = settings._options_command_map()
+
+ @classmethod
+ def mock_set_config(cls, attr, val):
+ cls.CONFIG_KEY_DICT[attr] = val
+
+ @classmethod
+ def mock_get_config(cls, attr, default):
+ return cls.CONFIG_KEY_DICT.get(attr, default)
+
+ def setUp(self):
+ self.CONFIG_KEY_DICT.clear()
+ mgr.set_config.side_effect = self.mock_set_config
+ mgr.get_config.side_effect = self.mock_get_config
+ if Settings.GRAFANA_API_HOST != 'localhost':
+ Settings.GRAFANA_API_HOST = 'localhost'
+ if Settings.GRAFANA_API_PORT != 3000:
+ Settings.GRAFANA_API_PORT = 3000
+
+ def test_get_setting(self):
+ self.assertEqual(Settings.GRAFANA_API_HOST, 'localhost')
+
+ def test_set_setting(self):
+ Settings.GRAFANA_API_HOST = 'grafanahost'
+ self.assertEqual(Settings.GRAFANA_API_HOST, 'grafanahost')
+
+ def test_get_cmd(self):
+ r, out, err = handle_option_command(
+ {'prefix': 'dashboard get-grafana-api-port'})
+ self.assertEqual(r, 0)
+ self.assertEqual(out, '3000')
+ self.assertEqual(err, '')
+
+ def test_set_cmd(self):
+ r, out, err = handle_option_command(
+ {'prefix': 'dashboard set-grafana-api-port',
+ 'value': '4000'})
+ self.assertEqual(r, 0)
+ self.assertEqual(out, 'Option GRAFANA_API_PORT updated')
+ self.assertEqual(err, '')
+
+ def test_inv_cmd(self):
+ r, out, err = handle_option_command(
+ {'prefix': 'dashboard get-non-existent-option'})
+ self.assertEqual(r, -errno.ENOSYS)
+ self.assertEqual(out, '')
+ self.assertEqual(err, "Command not found "
+ "'dashboard get-non-existent-option'")
+
+ def test_sync(self):
+ Settings.GRAFANA_API_PORT = 5000
+ r, out, err = handle_option_command(
+ {'prefix': 'dashboard get-grafana-api-port'})
+ self.assertEqual(r, 0)
+ self.assertEqual(out, '5000')
+ self.assertEqual(err, '')
+ r, out, err = handle_option_command(
+ {'prefix': 'dashboard set-grafana-api-host',
+ 'value': 'new-local-host'})
+ self.assertEqual(r, 0)
+ self.assertEqual(out, 'Option GRAFANA_API_HOST updated')
+ self.assertEqual(err, '')
+ self.assertEqual(Settings.GRAFANA_API_HOST, 'new-local-host')
+
+ def test_attribute_error(self):
+ with self.assertRaises(AttributeError) as ctx:
+ _ = Settings.NON_EXISTENT_OPTION
+
+ self.assertEqual(str(ctx.exception),
+ "type object 'Options' has no attribute 'NON_EXISTENT_OPTION'")