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
=======
-h, --help Show the help.
--config-dir DIR Path to a config directory to pull \*.conf files from. This
file set is sorted, so as to provide a predictable parse
order if individual options are over-ridden. The set is
parsed after the file(s) specified via previous
--config-file, arguments hence over-ridden options in the
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.
-h, --help Show this help message and exit
--config-file CONFIG Path to a config file to use. The default files used
is: /etc/grafyaml/grafyaml.conf
--debug Print debugging output (set logging level to DEBUG
instead of default INFO level)
--version Show program's version number and exit
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]
#
# From grafyaml.cache
#
# Directory used by grafyaml to store its cache files. (string value)
#cachedir = ~/.cache/grafyaml
# Maintain a special cache that contains an MD5 of every generated
# dashboard. (boolean value)
#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 os
from oslo_config import cfg
from grafana_dashboards.cache import Cache
from grafana_dashboards.grafana import Grafana
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__)
class Builder(object):
def __init__(self):
self.cache = Cache()
self.grafana = Grafana(CONF.grafana.url, CONF.grafana.apikey)
def __init__(self, config):
self.grafana = self._setup_grafana(config)
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):
files_to_process = []
@ -66,7 +73,7 @@ class Builder(object):
LOG.info('Number of dashboards generated: %d', len(dashboards))
for name in dashboards:
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.cache.set(name, md5)
else:

View File

@ -16,35 +16,14 @@ import logging
import os
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__)
class Cache(object):
def __init__(self):
if not CONF.cache.enabled:
return
cache_dir = self._get_cache_dir()
def __init__(self, cachedir):
cache_dir = self._get_cache_dir(cachedir)
self.region = make_region().configure(
'dogpile.cache.dbm',
arguments={
@ -53,22 +32,19 @@ class Cache(object):
)
def get(self, title):
if CONF.cache.enabled:
res = self.region.get(title)
return res if res else None
return None
res = self.region.get(title)
return res if res else None
def has_changed(self, title, md5):
if CONF.cache.enabled and self.get(title) == md5:
if self.get(title) == md5:
return False
return True
def set(self, title, md5):
if CONF.cache.enabled:
self.region.set(title, md5)
self.region.set(title, md5)
def _get_cache_dir(self):
path = os.path.expanduser(CONF.cache.cachedir)
def _get_cache_dir(self, cachedir):
path = os.path.expanduser(cachedir)
if not os.path.isdir(path):
os.makedirs(path)
return path

View File

@ -12,73 +12,90 @@
# License for the specific language governing permissions and limitations
# under the License.
import inspect
import argparse
import logging
import os
import sys
from oslo_config import cfg
from six.moves import configparser as ConfigParser
from grafana_dashboards.builder import Builder
from grafana_dashboards import config
from grafana_dashboards import version
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class Commands(object):
class Client(object):
def __init__(self):
self.builder = Builder()
def main(self):
self.parse_arguments()
self.read_config()
self.setup_logging()
def execute(self):
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)
self.args.func()
def update(self, path):
LOG.info('Updating dashboards in %s', path)
self.builder.update_dashboard(path)
def parse_arguments(self):
parser = argparse.ArgumentParser()
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):
LOG.info('Validating dashboards in %s', path)
subparsers = parser.add_subparsers(
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:
self.builder.load_files(path)
builder.load_files(self.args.path)
print('SUCCESS!')
except Exception as e:
print('%s: ERROR: %s' % (path, e))
print('%s: ERROR: %s' % (self.args.path, e))
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():
CONF.register_cli_opt(command_opt)
CONF.register_cli_opt(logging_opts)
config.prepare_args(sys.argv)
if CONF.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
Commands().execute()
client = Client()
client.main()
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
oslo.config>=1.11.0
python-slugify
PyYAML>=3.1.0
requests
six>=1.6.0
voluptuous>=0.7

View File

@ -16,13 +16,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
import os
import re
import shutil
import tempfile
import fixtures
from six.moves import configparser as ConfigParser
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'):
@ -52,5 +57,16 @@ class TestCase(testtools.TestCase):
def setUp(self):
super(TestCase, self).setUp()
self.log_fixture = self.useFixture(fixtures.FakeLogger())
self.useFixture(conf_fixture.ConfFixture())
self.log_fixture = self.useFixture(fixtures.FakeLogger(
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):
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,)):
orig = sys.stdout
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):
super(TestCaseBuilder, self).setUp()
self.builder = builder.Builder()
self.builder = builder.Builder(self.config)
@mock.patch('grafana_dashboards.grafana.Grafana.create_dashboard')
def test_update_dashboard(self, mock_grafana):
@ -36,7 +36,7 @@ class TestCaseBuilder(TestCase):
self.assertEqual(mock_grafana.call_count, 1)
# 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.
builder2.update_dashboard(dashboard)
self.assertEqual(mock_grafana.call_count, 1)

View File

@ -12,13 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from grafana_dashboards import cache
from tests.base import TestCase
CONF = cfg.CONF
class TestCaseCache(TestCase):
@ -28,10 +24,10 @@ class TestCaseCache(TestCase):
def setUp(self):
super(TestCaseCache, self).setUp()
self.storage = None
cachedir = self.config.get('cache', 'cachedir')
self.storage = cache.Cache(cachedir)
def test_cache_has_changed(self):
self.storage = cache.Cache()
res = self.storage.has_changed(
'hello-world', self.dashboard['hello-world'])
self.assertTrue(res)
@ -40,28 +36,10 @@ class TestCaseCache(TestCase):
'hello-world', self.dashboard['hello-world'])
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):
self.storage = cache.Cache()
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):
self.storage = cache.Cache()
self.storage.set('hello-world', self.dashboard['hello-world'])
self.assertEqual(
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'])
def test_cache_set_single(self):
self.storage = cache.Cache()
self.storage.set('hello-world', self.dashboard['hello-world'])
self.assertEqual(
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
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]
commands = flake8