The Overview will display a bar chart of daily runs, categorized by report status (changed, unchanged, failed). The chart data is loaded asynchronously from JSON so it doesn't provoke a delay in the page load. The data is JSON enconded. This feature was in the original Puppet Dashboard. The change was proposed and discussed in issue #308 . Application changes: - app.py: New view daily_reports_chart to serve the chart data as JSON. - dailychart.py: Submodule to query and format the chart data. Template changes: - layout.html: New block to add more elements to the HTML header. - index.html, node.html: Add C3 CSS in header block, add DIV placeholder for the chart in content block, add dailychart.js (and dependencies) in script block. Settings: - DAILY_REPORTS_CHART_ENABLED: New setting to turn off the charts. By default is on. - DAILY_REPORTS_CHART_DAYS: Changes the range of days to display in the charts. Javascript changes: - dailychart.js: New script that loads the JSON data for the chart and calls C3 to generate a bar chart. CSS changes: - puppetboard.css: Set fixed height to the chart container to avoid a page resize after the chart is loaded.
79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
from datetime import datetime, timedelta
|
|
from pypuppetdb.utils import UTC
|
|
from pypuppetdb.QueryBuilder import (
|
|
ExtractOperator, FunctionOperator, AndOperator,
|
|
GreaterEqualOperator, LessOperator, EqualsOperator,
|
|
)
|
|
|
|
DATE_FORMAT = "%Y-%m-%d"
|
|
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
|
|
|
|
def _iter_dates(days_number):
|
|
"""Return a list of datetime pairs AB, BC, CD, ... that represent the
|
|
24hs time ranges of today (until this midnight) and the
|
|
previous days.
|
|
"""
|
|
one_day = timedelta(days=1)
|
|
today = datetime.utcnow().replace(hour=0, minute=0, second=0,
|
|
microsecond=0, tzinfo=UTC())
|
|
days_list = list(today + one_day * (1 - i) for i in range(days_number + 1))
|
|
return zip(days_list[1:], days_list)
|
|
|
|
|
|
def _build_query(env, start, end, certname=None):
|
|
"""Build a extract query with optional certname and environment."""
|
|
query = ExtractOperator()
|
|
query.add_field(FunctionOperator('count'))
|
|
query.add_field('status')
|
|
subquery = AndOperator()
|
|
subquery.add(GreaterEqualOperator('start_time', start))
|
|
subquery.add(LessOperator('start_time', end))
|
|
if certname is not None:
|
|
subquery.add(EqualsOperator('certname', certname))
|
|
if env != '*':
|
|
subquery.add(EqualsOperator('environment', env))
|
|
query.add_query(subquery)
|
|
query.add_group_by("status")
|
|
return query
|
|
|
|
|
|
def _format_report_data(day, query_output):
|
|
"""Format the output of the query to a simpler dict."""
|
|
result = {'day': day, 'changed': 0, 'unchanged': 0, 'failed': 0}
|
|
for out in query_output:
|
|
if out['status'] == 'changed':
|
|
result['changed'] = out['count']
|
|
elif out['status'] == 'unchanged':
|
|
result['unchanged'] = out['count']
|
|
elif out['status'] == 'failed':
|
|
result['failed'] = out['count']
|
|
return result
|
|
|
|
|
|
def get_daily_reports_chart(db, env, days_number, certname=None):
|
|
"""Return the sum of each report status (changed, unchanged, failed)
|
|
per day, for today and the previous N days.
|
|
|
|
This information is used to present a chart.
|
|
|
|
:param db: The puppetdb.
|
|
:param env: Sum up the reports in this environment.
|
|
:param days_number: How many days to sum, including today.
|
|
:param certname: If certname is passed, only the reports of that
|
|
certname will be added. If certname is not passed, all reports in
|
|
the database will be considered.
|
|
"""
|
|
result = []
|
|
for start, end in reversed(_iter_dates(days_number)):
|
|
query = _build_query(
|
|
env=env,
|
|
start=start.strftime(DATETIME_FORMAT),
|
|
end=end.strftime(DATETIME_FORMAT),
|
|
certname=certname,
|
|
)
|
|
day = start.strftime(DATE_FORMAT)
|
|
output = db._query('reports', query=query)
|
|
result.append(_format_report_data(day, output))
|
|
return result
|