Replace oslo_config dependency with argparse

This is for the same reasoning as oslo_log. We don't want to depending
on OpenStack libraries.

Change-Id: I34e66af578d3f4b5ac5e710554aad91524285816
Signed-off-by: Paul Belanger <pabelanger@redhat.com>
This commit is contained in:
Paul Belanger 2015-10-07 18:06:07 -04:00
parent 3a66817727
commit 4e643e5d06
13 changed files with 146 additions and 218 deletions

View File

@ -16,20 +16,12 @@ files.
OPTIONS OPTIONS
======= =======
-h, --help Show the help. -h, --help Show this help message and exit
--config-dir DIR Path to a config directory to pull \*.conf files from. This --config-file CONFIG Path to a config file to use. The default files used
file set is sorted, so as to provide a predictable parse is: /etc/grafyaml/grafyaml.conf
order if individual options are over-ridden. The set is --debug Print debugging output (set logging level to DEBUG
parsed after the file(s) specified via previous instead of default INFO level)
--config-file, arguments hence over-ridden options in the --version Show program's version number and exit
directory take precedence.
--config-file PATH Path to a config file to use. Multiple config files can be
specified, with values in later files taking precedence. The
default files used are: None.
--debug Print debugging output(set logging level to DEBUG instead
of default INFO level).
--nodebug The inverse of --debug.
--version Show program's version number and exit.
COMMANDS COMMANDS
======== ========

View File

@ -1,28 +1,14 @@
[DEFAULT]
[grafana]
#
# From grafyaml.builder
#
# URL for grafana server. (string value)
#url = http://grafana.example.org
# API key for access grafana. (string value)
#apikey = <None>
[cache] [cache]
#
# From grafyaml.cache
#
# Directory used by grafyaml to store its cache files. (string value) # Directory used by grafyaml to store its cache files. (string value)
#cachedir = ~/.cache/grafyaml #cachedir = ~/.cache/grafyaml
# Maintain a special cache that contains an MD5 of every generated # Maintain a special cache that contains an MD5 of every generated
# dashboard. (boolean value) # dashboard. (boolean value)
#enabled = true #enabled = true
[grafana]
# URL for grafana server. (string value)
#url = http://localhost:8080
# API key for access grafana. (string value)
#apikey = <None>

View File

@ -15,37 +15,44 @@
import logging import logging
import os import os
from oslo_config import cfg
from grafana_dashboards.cache import Cache from grafana_dashboards.cache import Cache
from grafana_dashboards.grafana import Grafana from grafana_dashboards.grafana import Grafana
from grafana_dashboards.parser import YamlParser from grafana_dashboards.parser import YamlParser
grafana_opts = [
cfg.StrOpt(
'url', default='http://grafana.example.org',
help='URL for grafana server.'),
cfg.StrOpt(
'apikey', default=None,
help='API key for access grafana.'),
]
grafana_group = cfg.OptGroup(
name='grafana', title='Grafana options')
list_opts = lambda: [(grafana_group, grafana_opts), ]
CONF = cfg.CONF
CONF.register_group(grafana_group)
CONF.register_opts(grafana_opts, group='grafana')
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class Builder(object): class Builder(object):
def __init__(self):
self.cache = Cache() def __init__(self, config):
self.grafana = Grafana(CONF.grafana.url, CONF.grafana.apikey) self.grafana = self._setup_grafana(config)
self.parser = YamlParser() self.parser = YamlParser()
self.cache = self._setup_cache(config)
def _setup_cache(self, config):
if config.has_option('cache', 'enabled'):
self.cache_enabled = config.getboolean('cache', 'enabled')
else:
self.cache_enabled = True
if config.has_option('cache', 'cachedir'):
cachedir = config.get('cache', 'cachedir')
else:
cachedir = '~/.cache/grafyaml'
return Cache(cachedir)
def _setup_grafana(self, config):
if config.has_option('grafana', 'apikey'):
key = config.get('grafana', 'apikey')
else:
key = None
if config.has_option('grafana', 'url'):
url = config.get('grafana', 'url')
else:
url = 'http://localhost:8080'
return Grafana(url, key)
def load_files(self, path): def load_files(self, path):
files_to_process = [] files_to_process = []
@ -66,7 +73,7 @@ class Builder(object):
LOG.info('Number of dashboards generated: %d', len(dashboards)) LOG.info('Number of dashboards generated: %d', len(dashboards))
for name in dashboards: for name in dashboards:
data, md5 = self.parser.get_dashboard(name) data, md5 = self.parser.get_dashboard(name)
if self.cache.has_changed(name, md5): if self.cache.has_changed(name, md5) or not self.cache_enabled:
self.grafana.create_dashboard(name, data, overwrite=True) self.grafana.create_dashboard(name, data, overwrite=True)
self.cache.set(name, md5) self.cache.set(name, md5)
else: else:

View File

@ -16,35 +16,14 @@ import logging
import os import os
from dogpile.cache.region import make_region from dogpile.cache.region import make_region
from oslo_config import cfg
cache_opts = [
cfg.StrOpt(
'cachedir', default='~/.cache/grafyaml',
help='Directory used by grafyaml to store its cache files.'),
cfg.BoolOpt(
'enabled', default=True,
help='Maintain a special cache that contains an MD5 of every '
'generated dashboard.'),
]
cache_group = cfg.OptGroup(
name='cache', title='Cache options')
list_opts = lambda: [(cache_group, cache_opts), ]
CONF = cfg.CONF
CONF.register_opts(cache_opts)
CONF.register_opts(cache_opts, group='cache')
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class Cache(object): class Cache(object):
def __init__(self): def __init__(self, cachedir):
if not CONF.cache.enabled: cache_dir = self._get_cache_dir(cachedir)
return
cache_dir = self._get_cache_dir()
self.region = make_region().configure( self.region = make_region().configure(
'dogpile.cache.dbm', 'dogpile.cache.dbm',
arguments={ arguments={
@ -53,22 +32,19 @@ class Cache(object):
) )
def get(self, title): def get(self, title):
if CONF.cache.enabled: res = self.region.get(title)
res = self.region.get(title) return res if res else None
return res if res else None
return None
def has_changed(self, title, md5): def has_changed(self, title, md5):
if CONF.cache.enabled and self.get(title) == md5: if self.get(title) == md5:
return False return False
return True return True
def set(self, title, md5): def set(self, title, md5):
if CONF.cache.enabled: self.region.set(title, md5)
self.region.set(title, md5)
def _get_cache_dir(self): def _get_cache_dir(self, cachedir):
path = os.path.expanduser(CONF.cache.cachedir) path = os.path.expanduser(cachedir)
if not os.path.isdir(path): if not os.path.isdir(path):
os.makedirs(path) os.makedirs(path)
return path return path

View File

@ -12,73 +12,90 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import inspect import argparse
import logging import logging
import os
import sys import sys
from oslo_config import cfg from six.moves import configparser as ConfigParser
from grafana_dashboards.builder import Builder from grafana_dashboards.builder import Builder
from grafana_dashboards import config from grafana_dashboards import version
CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class Commands(object): class Client(object):
def __init__(self): def main(self):
self.builder = Builder() self.parse_arguments()
self.read_config()
self.setup_logging()
def execute(self): self.args.func()
exec_method = getattr(self, CONF.action.name)
args = inspect.getargspec(exec_method)
args.args.remove('self')
kwargs = {}
for arg in args.args:
kwargs[arg] = getattr(CONF.action, arg)
exec_method(**kwargs)
def update(self, path): def parse_arguments(self):
LOG.info('Updating dashboards in %s', path) parser = argparse.ArgumentParser()
self.builder.update_dashboard(path) parser.add_argument(
'--config-file', dest='config', help='Path to a config file to '
'use. The default file used is: /etc/grafyaml/grafyaml.conf')
parser.add_argument(
'--debug', dest='debug', action='store_true',
help='Print debugging output (set logging level to DEBUG instead '
' of default INFO level)')
parser.add_argument(
'--version', dest='version', action='version',
version=version.version_info.release_string(), help="show "
"program's version number and exit")
def validate(self, path): subparsers = parser.add_subparsers(
LOG.info('Validating dashboards in %s', path) title='commands')
parser_update = subparsers.add_parser('update')
parser_update.add_argument(
'path', help='colon-separated list of paths to YAML files or'
' directories')
parser_update.set_defaults(func=self.update)
parser_validate = subparsers.add_parser('validate')
parser_validate.add_argument(
'path', help='colon-separated list of paths to YAML files or'
' directories')
parser_validate.set_defaults(func=self.validate)
self.args = parser.parse_args()
def read_config(self):
self.config = ConfigParser.ConfigParser()
if self.args.config:
fp = self.args.config
else:
fp = '/etc/grafyaml/grafyaml.conf'
self.config.read(os.path.expanduser(fp))
def setup_logging(self):
if self.args.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
def update(self):
LOG.info('Updating dashboards in %s', self.args.path)
builder = Builder(self.config)
builder.update_dashboard(self.args.path)
def validate(self):
LOG.info('Validating dashboards in %s', self.args.path)
builder = Builder(self.config)
try: try:
self.builder.load_files(path) builder.load_files(self.args.path)
print('SUCCESS!') print('SUCCESS!')
except Exception as e: except Exception as e:
print('%s: ERROR: %s' % (path, e)) print('%s: ERROR: %s' % (self.args.path, e))
sys.exit(1) sys.exit(1)
def add_command_parsers(subparsers):
parser_update = subparsers.add_parser('update')
parser_update.add_argument(
'path', help='colon-separated list of paths to YAML files or'
' directories')
parser_validate = subparsers.add_parser('validate')
parser_validate.add_argument(
'path', help='colon-separated list of paths to YAML files or'
' directories')
command_opt = cfg.SubCommandOpt('action', handler=add_command_parsers)
logging_opts = cfg.BoolOpt(
'debug', default=False, help='Print debugging output (set logging level '
'to DEBUG instead of default INFO level).')
def main(): def main():
CONF.register_cli_opt(command_opt) client = Client()
CONF.register_cli_opt(logging_opts) client.main()
config.prepare_args(sys.argv)
if CONF.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
Commands().execute()
sys.exit(0) sys.exit(0)

View File

@ -1,23 +0,0 @@
# Copyright 2015 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from grafana_dashboards import version
def prepare_args(argv):
cfg.CONF(
argv[1:], project='grafana_dashboards',
version=version.version_info.release_string())

View File

@ -1,6 +1,6 @@
dogpile.cache dogpile.cache
oslo.config>=1.11.0
python-slugify python-slugify
PyYAML>=3.1.0 PyYAML>=3.1.0
requests requests
six>=1.6.0
voluptuous>=0.7 voluptuous>=0.7

View File

@ -16,13 +16,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
import os import os
import re import re
import shutil
import tempfile
import fixtures import fixtures
from six.moves import configparser as ConfigParser
import testtools import testtools
from tests import conf_fixture FIXTURE_DIR = os.path.join(
os.path.dirname(__file__), 'fixtures')
def get_scenarios(fixtures_path, in_ext='yaml', out_ext='json'): def get_scenarios(fixtures_path, in_ext='yaml', out_ext='json'):
@ -52,5 +57,16 @@ class TestCase(testtools.TestCase):
def setUp(self): def setUp(self):
super(TestCase, self).setUp() super(TestCase, self).setUp()
self.log_fixture = self.useFixture(fixtures.FakeLogger()) self.log_fixture = self.useFixture(fixtures.FakeLogger(
self.useFixture(conf_fixture.ConfFixture()) level=logging.DEBUG))
self.setup_config()
self.cachedir = tempfile.mkdtemp()
self.config.set('cache', 'cachedir', self.cachedir)
self.addCleanup(self.cleanup_cachedir)
def setup_config(self):
self.config = ConfigParser.ConfigParser()
self.config.read(os.path.join(FIXTURE_DIR, 'grafyaml.conf'))
def cleanup_cachedir(self):
shutil.rmtree(self.cachedir)

View File

@ -23,15 +23,6 @@ from tests.base import TestCase
class TestCase(TestCase): class TestCase(TestCase):
def setUp(self):
super(TestCase, self).setUp()
def clear():
cmd.CONF.reset()
cmd.CONF.unregister_opt(cmd.command_opt)
cmd.CONF.reset()
self.addCleanup(clear)
def shell(self, argstr, exitcodes=(0,)): def shell(self, argstr, exitcodes=(0,)):
orig = sys.stdout orig = sys.stdout
orig_stderr = sys.stderr orig_stderr = sys.stderr

5
tests/fixtures/grafyaml.conf vendored Normal file
View File

@ -0,0 +1,5 @@
[grafana]
url = http://grafana.example.org
[cache]
enabled = true

View File

@ -24,7 +24,7 @@ class TestCaseBuilder(TestCase):
def setUp(self): def setUp(self):
super(TestCaseBuilder, self).setUp() super(TestCaseBuilder, self).setUp()
self.builder = builder.Builder() self.builder = builder.Builder(self.config)
@mock.patch('grafana_dashboards.grafana.Grafana.create_dashboard') @mock.patch('grafana_dashboards.grafana.Grafana.create_dashboard')
def test_update_dashboard(self, mock_grafana): def test_update_dashboard(self, mock_grafana):
@ -36,7 +36,7 @@ class TestCaseBuilder(TestCase):
self.assertEqual(mock_grafana.call_count, 1) self.assertEqual(mock_grafana.call_count, 1)
# Create a new builder to avoid duplicate dashboards. # Create a new builder to avoid duplicate dashboards.
builder2 = builder.Builder() builder2 = builder.Builder(self.config)
# Update again with same dashboard, ensure we don't update grafana. # Update again with same dashboard, ensure we don't update grafana.
builder2.update_dashboard(dashboard) builder2.update_dashboard(dashboard)
self.assertEqual(mock_grafana.call_count, 1) self.assertEqual(mock_grafana.call_count, 1)

View File

@ -12,13 +12,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_config import cfg
from grafana_dashboards import cache from grafana_dashboards import cache
from tests.base import TestCase from tests.base import TestCase
CONF = cfg.CONF
class TestCaseCache(TestCase): class TestCaseCache(TestCase):
@ -28,10 +24,10 @@ class TestCaseCache(TestCase):
def setUp(self): def setUp(self):
super(TestCaseCache, self).setUp() super(TestCaseCache, self).setUp()
self.storage = None cachedir = self.config.get('cache', 'cachedir')
self.storage = cache.Cache(cachedir)
def test_cache_has_changed(self): def test_cache_has_changed(self):
self.storage = cache.Cache()
res = self.storage.has_changed( res = self.storage.has_changed(
'hello-world', self.dashboard['hello-world']) 'hello-world', self.dashboard['hello-world'])
self.assertTrue(res) self.assertTrue(res)
@ -40,28 +36,10 @@ class TestCaseCache(TestCase):
'hello-world', self.dashboard['hello-world']) 'hello-world', self.dashboard['hello-world'])
self.assertFalse(res) self.assertFalse(res)
def test_cache_disabled_has_changed(self):
CONF.cache.enabled = False
self.storage = cache.Cache()
res = self.storage.has_changed(
'hello-world', self.dashboard['hello-world'])
self.assertTrue(res)
self.storage.set('hello-world', self.dashboard['hello-world'])
res = self.storage.has_changed(
'hello-world', self.dashboard['hello-world'])
self.assertTrue(res)
def test_cache_get_empty(self): def test_cache_get_empty(self):
self.storage = cache.Cache()
self.assertEqual(self.storage.get('empty'), None) self.assertEqual(self.storage.get('empty'), None)
def test_cache_disabled_get_empty(self):
CONF.cache.enabled = False
self.storage = cache.Cache()
self.assertEqual(self.storage.get('disabled'), None)
def test_cache_set_multiple(self): def test_cache_set_multiple(self):
self.storage = cache.Cache()
self.storage.set('hello-world', self.dashboard['hello-world']) self.storage.set('hello-world', self.dashboard['hello-world'])
self.assertEqual( self.assertEqual(
self.storage.get('hello-world'), self.dashboard['hello-world']) self.storage.get('hello-world'), self.dashboard['hello-world'])
@ -77,15 +55,6 @@ class TestCaseCache(TestCase):
self.storage.get('hello-world'), self.dashboard['hello-world']) self.storage.get('hello-world'), self.dashboard['hello-world'])
def test_cache_set_single(self): def test_cache_set_single(self):
self.storage = cache.Cache()
self.storage.set('hello-world', self.dashboard['hello-world']) self.storage.set('hello-world', self.dashboard['hello-world'])
self.assertEqual( self.assertEqual(
self.storage.get('hello-world'), self.dashboard['hello-world']) self.storage.get('hello-world'), self.dashboard['hello-world'])
def test_cache_disabled_set_single(self):
CONF.cache.enabled = False
self.storage = cache.Cache()
self.storage.set('hello-world', self.dashboard['hello-world'])
# Make sure cache is empty.
self.assertEqual(
self.storage.get('hello-world'), None)

View File

@ -12,14 +12,6 @@ deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = python setup.py test --slowest --testr-args='{posargs}' commands = python setup.py test --slowest --testr-args='{posargs}'
[testenv:genconfig]
commands =
oslo-config-generator \
--namespace grafyaml.builder \
--namespace grafyaml.cache \
--namespace oslo.log \
--output-file etc/grafyaml.conf
[testenv:pep8] [testenv:pep8]
commands = flake8 commands = flake8