def memory_usage(datasource): return G.Graph( title="Memory Usage", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( # 2 ^ 30 bytes format="gbytes", label="Memory", ), G.YAxis( show=False, ), ], targets=[ G.Target( expr=""" sum(machine_memory_bytes) / 2 ^ 30 """, legendFormat="Total Physical Memory", refId="A", ), G.Target( expr=""" rss:container_memory:total / 2 ^ 30 """, legendFormat="Total Container RSS", refId="B", ), ], )
def test_auto_refids(): """auto_ref_ids() provides refIds for all targets without refIds already set.""" dashboard = G.Dashboard( title="Test dashboard", rows=[ G.Row(panels=[ G.Graph( title="CPU Usage by Namespace (rate[5m])", dataSource="My data source", targets=[ G.Target( expr='whatever #Q', legendFormat='{{namespace}}', ), G.Target( expr='hidden whatever', legendFormat='{{namespace}}', refId='Q', hide=True ), G.Target( expr='another target' ), ], yAxes=[ G.YAxis(format=G.SHORT_FORMAT, label="CPU seconds"), G.YAxis(format=G.SHORT_FORMAT), ], ).auto_ref_ids() ]), ], ) assert dashboard.rows[0].panels[0].targets[0].refId == 'A' assert dashboard.rows[0].panels[0].targets[1].refId == 'Q' assert dashboard.rows[0].panels[0].targets[2].refId == 'B'
def test_auto_refids_preserves_provided_ids(): """ auto_ref_ids() provides refIds for all targets without refIds already set. """ dashboard = G.Dashboard( title="Test dashboard", rows=[ G.Row(panels=[ G.Graph( title="CPU Usage by Namespace (rate[5m])", targets=[ G.Target( expr='whatever #Q', legendFormat='{{namespace}}', ), G.Target( expr='hidden whatever', legendFormat='{{namespace}}', refId='Q', ), G.Target( expr='another target' ), ], ).auto_ref_ids() ]), ], ) assert dashboard.rows[0].panels[0].targets[0].refId == 'A' assert dashboard.rows[0].panels[0].targets[1].refId == 'Q' assert dashboard.rows[0].panels[0].targets[2].refId == 'B'
def PromGraph(data_source, title, expressions, **kwargs): """Create a graph that renders Prometheus data. :param str data_source: The name of the data source that provides Prometheus data. :param title: The title of the graph. :param expressions: List of tuples of (legend, expr), where 'expr' is a Prometheus expression. Or a list of dict where keys are Target's args. :param kwargs: Passed on to Graph. """ letters = string.ascii_uppercase expressions = list(expressions) if len(expressions) > len(letters): raise ValueError( 'Too many expressions. Can support at most {}, but got {}'.format( len(letters), len(expressions))) if all(isinstance(expr, dict) for expr in expressions): targets = [ G.Target(refId=refId, **args) for (args, refId) in zip(expressions, letters) ] else: targets = [ G.Target(expr, legend, refId=refId) for ((legend, expr), refId) in zip(expressions, letters) ] return G.Graph(title=title, dataSource=data_source, targets=targets, **kwargs)
def api_call_latency(title, verb, scope, threshold): return d.Graph( title=title, targets=[ g.Target(expr=str(threshold), legendFormat="threshold"), g.Target( expr='apiserver:apiserver_request_latency_1m:histogram_quantile{quantile="0.99", verb=~"%(verb)s", scope=~"%(scope)s"}' % {"verb": verb, "scope": scope}, legendFormat="{{verb}} {{scope}}/{{resource}}", ), ], yAxes=g.single_y_axis(format=g.SECONDS_FORMAT), )
def s4_customer_deployments(datasource): return G.Graph( title="Customer Deployments", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="none", label="Total Customer Deployments", min=0, max=100, ), G.YAxis( show=False, ), ], targets=[ G.Target( # Each replicaset and pod end up with their own series. Label # these more succinctly. Leave them distinct in case it is # interesting to see where restarts have happened. expr=""" label_replace( s4_deployment_gauge{pod=~"subscription-converger-.*"}, "shortpod", "# Deploys ($1)", "pod", "subscription-converger-(.*)" ) """, refId="A", legendFormat="{{shortpod}}", ), G.Target( # As above. expr=""" label_replace( s4_running_pod_gauge{pod=~"subscription-converger-.*"}, "shortpod", "# Running ($1)", "pod", "subscription-converger-(.*)" ) """, refId="B", legendFormat="{{shortpod}}", ), ], )
def process_open_fds(datasource): return G.Graph( title="Open File Descriptors", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="none", label="Count", ), G.YAxis( show=False, ), ], targets=[ G.Target( expr=""" process_open_fds{pod=~".+"} """, refId="A", legendFormat="{{pod}}", ), ], )
def unhandled_errors(datasource): return G.Graph( title="Unhandled Errors", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="none", label="Count", ), G.YAxis( show=False, ), ], targets=[ G.Target( expr=""" sum(s4_unhandled_error_counter) """, refId="A", legendFormat="Total Unhandled Errors", ), ], )
def last_convergence(datasource): return G.Graph( title="Since Last Convergence", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="none", label="Period", ), G.YAxis( show=False, ), ], targets=[ G.Target( expr=""" time() - max( s4_last_convergence_succeeded{ pod=~"subscription-converger-.*" } ) """, refId="A", legendFormat="Time Since Last Convergence Success", ), ], )
def filesystem_usage(datasource): return G.Graph( title="Filesystem Usage", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="percent", ), G.YAxis( show=False, ), ], targets=[ G.Target( # Get the proportion used of each filesystem on a volume from # a PersistentVolumeClaim on each node of the cluster. It's # hard to figure out the role each filesystem serves from this # graph (since all we get is the PVC name). Better than # nothing, though. Hopefully later we can do better. expr=""" 100 * filesystem_used_bytes{volume=~"pvc-.*"} / filesystem_size_bytes{volume=~"pvc-.*"} """, legendFormat="{{volume}}", refId="A", ), ], )
def api_call_latency(title, verb, scope, threshold): return d.Graph( title=title, targets=[ g.Target(expr=str(threshold), legendFormat="threshold"), g.Target( expr=d.one_line(expression % { "verb": verb, "scope": scope }), # TODO(github.com/grafana/grafana/issues/19410): uncomment once fixed # legendFormat="{{verb}} {{scope}}/{{resource}}", ), ], yAxes=g.single_y_axis(format=g.SECONDS_FORMAT), )
def test_auto_id(): """auto_panel_ids() provides IDs for all panels without IDs already set.""" dashboard = G.Dashboard( title="Test dashboard", rows=[ G.Row(panels=[ G.Graph( title="CPU Usage by Namespace (rate[5m])", dataSource="My data source", targets=[ G.Target( expr='whatever', legendFormat='{{namespace}}', refId='A', ), ], yAxes=[ G.YAxis(format=G.SHORT_FORMAT, label="CPU seconds"), G.YAxis(format=G.SHORT_FORMAT), ], ) ]), ], ).auto_panel_ids() assert dashboard.rows[0].panels[0].id == 1
def api_call_latency(title, metric, verb, scope, limit): return d.Graph( title=title, targets=[ g.Target(expr=str(limit), legendFormat="limit"), g.Target( expr= 'quantile_over_time(0.99, %(metric)s{quantile="0.99", verb=~"%(verb)s", scope=~"%(scope)s"}[12h])' % { "metric": metric, "verb": verb, "scope": scope }), ], yAxes=g.single_y_axis(format=g.SECONDS_FORMAT), )
def test_table(): t = G.Table( dataSource='some data source', targets=[ G.Target(expr='some expr'), ], title='table title', transformations=[ { "id": "seriesToRows", "options": {} }, { "id": "organize", "options": { "excludeByName": { "Time": True }, "indexByName": {}, "renameByName": { "Value": "Dummy" } } } ] ) assert len(t.to_json_data()['transformations']) == 2 assert t.to_json_data()['transformations'][0]["id"] == "seriesToRows"
def dummy_alert_condition() -> G.AlertCondition: return G.AlertCondition( target=G.Target(), evaluator=G.Evaluator(type=G.EVAL_GT, params=42), timeRange=G.TimeRange(from_time='5m', to_time='now'), operator=G.OP_AND, reducerType=G.RTYPE_AVG, )
def show_quantiles(queryTemplate, quantiles=None, legend=""): quantiles = quantiles or QUANTILES targets = [] for quantile in quantiles: q = "{:.2f}".format(quantile) l = legend or q targets.append(g.Target(expr=queryTemplate.format(quantile=q), legendFormat=l)) return targets
def test_stat_no_repeat(): t = G.Stat(title='dummy', dataSource='data source', targets=[G.Target(expr='some expr')]) assert t.to_json_data()['repeat'] is None assert t.to_json_data()['repeatDirection'] is None assert t.to_json_data()['maxPerRow'] is None
def min_max_avg(base, by, legend=""): return [ g.Target( expr="{func}({query}) by ({by})".format(func=f, query=base, by=", ".join(by)), legendFormat="{func} {legend}".format(func=f, legend=legend), ) for f in ("min", "avg", "max") ]
def test_stat_with_repeat(): t = G.Stat(title='dummy', dataSource='data source', targets=[G.Target(expr='some expr')], repeat=G.Repeat(variable="repetitionVariable", direction='h', maxPerRow=10)) assert t.to_json_data()['repeat'] == 'repetitionVariable' assert t.to_json_data()['repeatDirection'] == 'h' assert t.to_json_data()['maxPerRow'] == 10
def simple_graph(title, exprs, yAxes=None, legend=""): if not isinstance(exprs, (list, tuple)): exprs = [exprs] if legend != "" and len(exprs) != 1: raise ValueError("legend can be specified only for a 1-element exprs") return Graph( title=title, # One graph per row. targets=[g.Target(expr=expr, legendFormat=legend) for expr in exprs], yAxes=yAxes or g.YAxes(), )
def simple_graph(title, exprs, yAxes=None): if not isinstance(exprs, (list, tuple)): exprs = [exprs] return g.Graph( title=title, dataSource="$source", # One graph per row. span=g.TOTAL_SPAN, targets=[g.Target(expr=expr) for expr in exprs], yAxes=yAxes or g.YAxes(), tooltip=DECREASING_ORDER_TOOLTIP, )
def api_call_latency(title, verb, scope, threshold): return d.Graph( title=title, targets=[ g.Target(expr=str(threshold), legendFormat="threshold"), g.Target( expr=d.one_line(""" apiserver:apiserver_request_latency_1m:histogram_quantile{ quantile="0.99", verb=~"%(verb)s", scope=~"%(scope)s", resource=~"${resource:regex}s*", }""" % { "verb": verb, "scope": scope }), # TODO(github.com/grafana/grafana/issues/19410): uncomment once fixed # legendFormat="{{verb}} {{scope}}/{{resource}}", ), ], yAxes=g.single_y_axis(format=g.SECONDS_FORMAT), )
def run(self): templateList = [ G.Template(default="", dataSource="default", name="serverid", label="ServerID", query="label_values(serverid)") ] dashboard = G.Dashboard(title=self.options.title, templating=G.Templating(list=templateList)) # Simple table processing - could be enhanced to use GridPos etc. for metric in metrics: if 'section' in metric: dashboard.rows.append( G.Row(title=metric['section'], showTitle=True)) continue if 'row' in metric: dashboard.rows.append(G.Row(title='', showTitle=False)) continue graph = G.Graph(title=metric['title'], dataSource='default', maxDataPoints=1000, legend=G.Legend(show=True, alignAsTable=True, min=True, max=True, avg=True, current=True, total=True, sort='max', sortDesc=True), yAxes=G.single_y_axis()) ref_id = 'A' for texp in metric['expr']: graph.targets.append(G.Target(expr=texp, refId=ref_id)) ref_id = chr(ord(ref_id) + 1) dashboard.rows[-1].panels.append(graph) # Auto-number panels - returns new dashboard dashboard = dashboard.auto_panel_ids() s = io.StringIO() write_dashboard(dashboard, s) print("""{ "dashboard": %s } """ % s.getvalue())
def simple_graph(title, exprs, legend="", interval="5s", **kwargs): if not isinstance(exprs, (list, tuple)): exprs = [exprs] if legend != "" and len(exprs) != 1: raise ValueError("legend can be specified only for a 1-element exprs") return Graph( title=title, # One graph per row. targets=[ g.Target(expr=expr, legendFormat=legend, interval=interval, intervalFactor=1) for expr in exprs ], **kwargs)
def network_usage(datasource): return G.Graph( title="Network Usage", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( # 2^20 bytes / second format="MBs", label="Transferred", ), G.YAxis( show=False, ), ], targets=[ G.Target( # Get the rate of data received on the public interface (eth0) # for each entire node (id="/") over the last minute. expr=""" receive:container_network_bytes:rate1m / 2 ^ 20 """, legendFormat="receive", refId="A", ), G.Target( # And rate of data sent. expr=""" transmit:container_network_bytes:rate1m / 2 ^ 20 """, legendFormat="transmit", refId="B", ), ], )
def _row(title): return core.Row(panels=[ core.Graph(title=title, dataSource='prometheus', targets=[ core.Target( expr=title, legendFormat='{{namespace}}', ), ], yAxes=[ core.YAxis(format=core.NO_FORMAT), core.YAxis(format=core.SHORT_FORMAT), ]) ])
def test_graph_panel_alert(): data_source = 'dummy data source' targets = ['dummy_prom_query'] title = 'dummy title' alert = [ G.AlertCondition(G.Target(), G.Evaluator('a', 'b'), G.TimeRange('5', '6'), 'd', 'e') ] thresholds = [ G.GraphThreshold(20.0), G.GraphThreshold(40.2, colorMode="ok") ] graph = G.Graph(data_source, targets, title, thresholds=thresholds, alert=alert) data = graph.to_json_data() assert data['targets'] == targets assert data['datasource'] == data_source assert data['title'] == title assert data['alert'] == alert assert data['thresholds'] == []
def test_table_styled_columns(): t = G.Table.with_styled_columns( columns=[ (G.Column('Foo', 'foo'), G.ColumnStyle()), (G.Column('Bar', 'bar'), None), ], dataSource='some data source', targets=[ G.Target(expr='some expr'), ], title='table title', ) assert t.columns == [ G.Column('Foo', 'foo'), G.Column('Bar', 'bar'), ] assert t.styles == [ G.ColumnStyle(pattern='Foo'), ]
def cpu_usage(datasource, intervals): return G.Graph( title="CPU usage", dataSource=datasource, xAxis=X_TIME, yAxes=[ G.YAxis( format="percent", label="Average", min=0, max=100, ), G.YAxis( format="percent", label="Average", ), ], targets=list( G.Target( # CPU usage (as a percentage of maximum possible) averaged # over a period is given as 100 times the sum (over all # containers) of the rate of increase (in seconds) divided by # the maximum possible increase (1 second per CPU). # # The sums are taken from recording rules because recomputing # them for every point on the graph for every graph request # becomes prohitively expensive. Only a few specific rates # are "recorded" and the ``interval`` parameter must match one # of those. :( # # See prometheus.yaml for the recording rules. expr=""" 100 * cpu:container_usage_seconds:rate{} / cores:machine_cpu:total """.format(interval), legendFormat="CPU Usage ({} avg)".format(interval), refId=refId(n), ) for n, interval in enumerate(intervals), ), )
def test_serialization(): """Serializing a graph doesn't explode.""" graph = G.Graph( title="CPU Usage by Namespace (rate[5m])", dataSource="My data source", targets=[ G.Target( expr='namespace:container_cpu_usage_seconds_total:sum_rate', legendFormat='{{namespace}}', refId='A', ), ], id=1, yAxes=[ G.YAxis(format=G.SHORT_FORMAT, label="CPU seconds / second"), G.YAxis(format=G.SHORT_FORMAT), ], ) stream = StringIO() _gen.write_dashboard(graph, stream) assert stream.getvalue() != ''