From 7a84b4e064030b17d04598e836f8f805f48ad2f8 Mon Sep 17 00:00:00 2001 From: Paul Belanger Date: Wed, 4 Nov 2015 13:08:19 -0500 Subject: [PATCH] Refactor Panel class A follow up patch will be adding better code coverage on Panels. Actually validating the values of our fields. To do that, we first need to split out the logic to be better consumed. Change-Id: I9e4ca5b90075b860788eed131eefd03c446a010b Signed-off-by: Paul Belanger --- grafana_dashboards/schema/panel.py | 140 ------------------ grafana_dashboards/schema/panel/__init__.py | 57 +++++++ grafana_dashboards/schema/panel/base.py | 32 ++++ grafana_dashboards/schema/panel/dashlist.py | 30 ++++ grafana_dashboards/schema/panel/graph.py | 38 +++++ grafana_dashboards/schema/panel/singlestat.py | 68 +++++++++ grafana_dashboards/schema/panel/text.py | 30 ++++ 7 files changed, 255 insertions(+), 140 deletions(-) delete mode 100644 grafana_dashboards/schema/panel.py create mode 100644 grafana_dashboards/schema/panel/__init__.py create mode 100644 grafana_dashboards/schema/panel/base.py create mode 100644 grafana_dashboards/schema/panel/dashlist.py create mode 100644 grafana_dashboards/schema/panel/graph.py create mode 100644 grafana_dashboards/schema/panel/singlestat.py create mode 100644 grafana_dashboards/schema/panel/text.py diff --git a/grafana_dashboards/schema/panel.py b/grafana_dashboards/schema/panel.py deleted file mode 100644 index c66c310..0000000 --- a/grafana_dashboards/schema/panel.py +++ /dev/null @@ -1,140 +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. - -import voluptuous as v - - -class Panel(object): - - def __init__(self): - self.base = { - v.Required('editable', default=True): v.All(bool), - v.Required('error', default=False): v.All(bool), - v.Required('span', default=12): v.All(int, v.Range(min=0, max=12)), - v.Required('title'): v.All(str, v.Length(min=1)), - v.Required('type'): v.Any( - 'dashlist', 'graph', 'singlestat', 'text'), - v.Optional('id'): int, - } - - self.dashlist = { - v.Required('limit', default=10): v.All(int), - v.Required('mode', default='starred'): v.Any('search', 'starred'), - v.Required('tag', default=''): v.All(str), - v.Required('query', default=''): v.All(str), - } - self.dashlist.update(self.base) - - self.graph = { - v.Required('bars', default=False): v.All(bool), - v.Required('fill', default=1): v.All(int), - v.Required('lines', default=True): v.All(bool), - v.Required('linewidth', default=2): v.All(int), - v.Required('percentage', default=False): v.All(bool), - v.Required('pointradius', default=5): v.All(int), - v.Required('points', default=False): v.All(bool), - v.Required('stack', default=False): v.All(bool), - v.Required('steppedLine', default=False): v.All(bool), - v.Required('targets', default=[]): v.All(list), - v.Required('x-axis', default=True): v.All(bool), - v.Required('y-axis', default=True): v.All(bool), - } - self.graph.update(self.base) - - # TODO(pabelanger): This is pretty ugly, there much be a better way to - # set default values. - sparkline_defaults = { - 'fillColor': 'rgba(31, 118, 189, 0.18)', - 'full': False, - 'lineColor': 'rgb(31, 120, 193)', - 'show': False, - } - sparkline = { - v.Required( - 'fillColor', default=sparkline_defaults['fillColor'] - ): v.All(str), - v.Required('full', default=False): v.All(bool), - v.Required( - 'lineColor', default=sparkline_defaults['lineColor'] - ): v.All(str), - v.Required('show', default=False): v.All(bool), - } - - self.singlestat = { - v.Required('colorBackground', default=False): v.All(bool), - v.Required('colorValue', default=False): v.All(bool), - v.Required('maxDataPoints', default=100): v.All(int), - v.Required('postfix', default=''): v.All(str), - # Support 0% to 200% by 10 - v.Required( - 'postfixFontSize', default='50%'): v.All( - v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), - v.Required('prefix', default=''): v.All(str), - # Support 0% to 200% by 10 - v.Required( - 'prefixFontSize', default='50%'): v.All( - v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), - v.Required('sparkline', default=sparkline_defaults): sparkline, - v.Required('targets', default=[]): v.All(list), - v.Required('thresholds', default=''): v.All(str), - # Support 0% to 200% by 10 - v.Required( - 'valueFontSize', default='80%'): v.All( - v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), - v.Required('valueName', default='avg'): v.All( - 'avg', 'current', 'max', 'min', 'total'), - v.Optional('decimals'): v.All(int, v.Range(min=0, max=12)), - } - self.singlestat.update(self.base) - - self.text = { - v.Required('content'): v.All(str), - v.Required('mode', default='markdown'): v.Any( - 'html', 'markdown', 'text'), - v.Optional('style'): dict(), - } - self.text.update(self.base) - - def _validate(self): - - def f(data): - res = [] - if not isinstance(data, list): - raise v.Invalid('Should be a list') - - for panel in data: - validate = v.Schema(self.base, extra=True) - validate(panel) - - if panel['type'] == 'dashlist': - schema = v.Schema(self.dashlist) - elif panel['type'] == 'graph': - schema = v.Schema(self.graph) - elif panel['type'] == 'singlestat': - schema = v.Schema(self.singlestat) - elif panel['type'] == 'text': - schema = v.Schema(self.text) - - res.append(schema(panel)) - - return res - - return f - - def get_schema(self): - schema = v.Schema({ - v.Required('panels', default=[]): v.All(self._validate()), - }) - - return schema diff --git a/grafana_dashboards/schema/panel/__init__.py b/grafana_dashboards/schema/panel/__init__.py new file mode 100644 index 0000000..ab103eb --- /dev/null +++ b/grafana_dashboards/schema/panel/__init__.py @@ -0,0 +1,57 @@ +# 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. + +import voluptuous as v + +from grafana_dashboards.schema.panel.base import Base +from grafana_dashboards.schema.panel.dashlist import Dashlist +from grafana_dashboards.schema.panel.graph import Graph +from grafana_dashboards.schema.panel.singlestat import Singlestat +from grafana_dashboards.schema.panel.text import Text + + +class Panel(object): + + def _validate(self): + + def f(data): + res = [] + if not isinstance(data, list): + raise v.Invalid('Should be a list') + + for panel in data: + validate = Base().get_schema() + validate(panel) + + if panel['type'] == 'dashlist': + schema = Dashlist().get_schema() + elif panel['type'] == 'graph': + schema = Graph().get_schema() + elif panel['type'] == 'singlestat': + schema = Singlestat().get_schema() + elif panel['type'] == 'text': + schema = Text().get_schema() + + res.append(schema(panel)) + + return res + + return f + + def get_schema(self): + schema = v.Schema({ + v.Required('panels', default=[]): v.All(self._validate()), + }) + + return schema diff --git a/grafana_dashboards/schema/panel/base.py b/grafana_dashboards/schema/panel/base.py new file mode 100644 index 0000000..25c72e3 --- /dev/null +++ b/grafana_dashboards/schema/panel/base.py @@ -0,0 +1,32 @@ +# 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. + +import voluptuous as v + + +class Base(object): + + def __init__(self): + self.base = { + v.Required('editable', default=True): v.All(bool), + v.Required('error', default=False): v.All(bool), + v.Required('span', default=12): v.All(int, v.Range(min=0, max=12)), + v.Required('title'): v.All(str, v.Length(min=1)), + v.Required('type'): v.Any( + 'dashlist', 'graph', 'singlestat', 'text'), + v.Optional('id'): int, + } + + def get_schema(self): + return v.Schema(self.base, extra=True) diff --git a/grafana_dashboards/schema/panel/dashlist.py b/grafana_dashboards/schema/panel/dashlist.py new file mode 100644 index 0000000..532946f --- /dev/null +++ b/grafana_dashboards/schema/panel/dashlist.py @@ -0,0 +1,30 @@ +# 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. + +import voluptuous as v + +from grafana_dashboards.schema.panel.base import Base + + +class Dashlist(Base): + + def get_schema(self): + dashlist = { + v.Required('limit', default=10): v.All(int), + v.Required('mode', default='starred'): v.Any('search', 'starred'), + v.Required('tag', default=''): v.All(str), + v.Required('query', default=''): v.All(str), + } + dashlist.update(self.base) + return v.Schema(dashlist) diff --git a/grafana_dashboards/schema/panel/graph.py b/grafana_dashboards/schema/panel/graph.py new file mode 100644 index 0000000..20b1dab --- /dev/null +++ b/grafana_dashboards/schema/panel/graph.py @@ -0,0 +1,38 @@ +# 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. + +import voluptuous as v + +from grafana_dashboards.schema.panel.base import Base + + +class Graph(Base): + + def get_schema(self): + graph = { + v.Required('bars', default=False): v.All(bool), + v.Required('fill', default=1): v.All(int), + v.Required('lines', default=True): v.All(bool), + v.Required('linewidth', default=2): v.All(int), + v.Required('percentage', default=False): v.All(bool), + v.Required('pointradius', default=5): v.All(int), + v.Required('points', default=False): v.All(bool), + v.Required('stack', default=False): v.All(bool), + v.Required('steppedLine', default=False): v.All(bool), + v.Required('targets', default=[]): v.All(list), + v.Required('x-axis', default=True): v.All(bool), + v.Required('y-axis', default=True): v.All(bool), + } + graph.update(self.base) + return v.Schema(graph) diff --git a/grafana_dashboards/schema/panel/singlestat.py b/grafana_dashboards/schema/panel/singlestat.py new file mode 100644 index 0000000..cd0e202 --- /dev/null +++ b/grafana_dashboards/schema/panel/singlestat.py @@ -0,0 +1,68 @@ +# 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. + +import voluptuous as v + +from grafana_dashboards.schema.panel.base import Base + + +class Singlestat(Base): + + def get_schema(self): + # TODO(pabelanger): This is pretty ugly, there much be a better way to + # set default values. + sparkline_defaults = { + 'fillColor': 'rgba(31, 118, 189, 0.18)', + 'full': False, + 'lineColor': 'rgb(31, 120, 193)', + 'show': False, + } + sparkline = { + v.Required( + 'fillColor', default=sparkline_defaults['fillColor'] + ): v.All(str), + v.Required('full', default=False): v.All(bool), + v.Required( + 'lineColor', default=sparkline_defaults['lineColor'] + ): v.All(str), + v.Required('show', default=False): v.All(bool), + } + + singlestat = { + v.Required('colorBackground', default=False): v.All(bool), + v.Required('colorValue', default=False): v.All(bool), + v.Required('maxDataPoints', default=100): v.All(int), + v.Required('postfix', default=''): v.All(str), + # Support 0% to 200% by 10 + v.Required( + 'postfixFontSize', default='50%'): v.All( + v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), + v.Required('prefix', default=''): v.All(str), + # Support 0% to 200% by 10 + v.Required( + 'prefixFontSize', default='50%'): v.All( + v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), + v.Required('sparkline', default=sparkline_defaults): sparkline, + v.Required('targets', default=[]): v.All(list), + v.Required('thresholds', default=''): v.All(str), + # Support 0% to 200% by 10 + v.Required( + 'valueFontSize', default='80%'): v.All( + v.Match(r'^[1-9]?[0]{1}%$|^1[0-9]?[0]{1}%$|^200%$')), + v.Required('valueName', default='avg'): v.All( + 'avg', 'current', 'max', 'min', 'total'), + v.Optional('decimals'): v.All(int, v.Range(min=0, max=12)), + } + singlestat.update(self.base) + return v.Schema(singlestat) diff --git a/grafana_dashboards/schema/panel/text.py b/grafana_dashboards/schema/panel/text.py new file mode 100644 index 0000000..8eeb495 --- /dev/null +++ b/grafana_dashboards/schema/panel/text.py @@ -0,0 +1,30 @@ +# 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. + +import voluptuous as v + +from grafana_dashboards.schema.panel.base import Base + + +class Text(Base): + + def get_schema(self): + text = { + v.Required('content'): v.All(str), + v.Required('mode', default='markdown'): v.Any( + 'html', 'markdown', 'text'), + v.Optional('style'): dict(), + } + text.update(self.base) + return v.Schema(text)