diff --git a/dashboards/cloud-cost-groups.jsonnet b/dashboards/cloud-cost-groups.jsonnet index 805a781..440db48 100644 --- a/dashboards/cloud-cost-groups.jsonnet +++ b/dashboards/cloud-cost-groups.jsonnet @@ -3,6 +3,7 @@ local grafonnet = import '../../vendor/gen/grafonnet-v11.4.0/main.libsonnet'; local dashboard = grafonnet.dashboard; local bc = grafonnet.panel.barChart; local bg = grafonnet.panel.barGauge; +local tb = grafonnet.panel.table; local var = grafonnet.dashboard.variable; local common = import './common.libsonnet'; @@ -19,7 +20,7 @@ local TotalHub = - workshop: a hub for events such as workshops and tutorials, e.g. workshop..2i2c.cloud ||| ) - + bg.panelOptions.withGridPos(h=8, w=8, x=0, y=0) + + bg.panelOptions.withGridPos(h=7, w=8, x=0, y=0) + bg.queryOptions.withTargets([ common.queryHubTarget { @@ -80,7 +81,7 @@ local TotalComponent = - support: compute and storage for support functions ||| ) - + bg.panelOptions.withGridPos(h=8, w=8, x=8, y=0) + + bg.panelOptions.withGridPos(h=7, w=8, x=8, y=0) + bg.queryOptions.withTargets([ common.queryComponentTarget { @@ -127,7 +128,7 @@ local Top5 = Shows the top 5 users by cost across all hubs and components over the selected time period. ||| ) - + bg.panelOptions.withGridPos(h=8, w=8, x=16, y=0) + + bg.panelOptions.withGridPos(h=7, w=8, x=16, y=0) + bg.queryOptions.withTargets([ common.queryUsersTarget { @@ -191,6 +192,104 @@ local Top5 = ]) ; +local TotalGroup = + common.bgOptions + + bg.new('Total by Group') + + bg.panelOptions.withDescription( + ||| + Total costs by group are summed over the time period selected. + + Note: Users with multiple group memberships are double-counted. E.g. if user 1 is a member of group 1 and group 2, then the user's individual costs are included in the total sums of each group. + ||| + ) + + bg.panelOptions.withGridPos(h=7, w=12, x=0, y=8) + + bg.queryOptions.withTargets([ + common.queryGroupTarget + { + url: 'http://jupyterhub-cost-monitoring.support.svc.cluster.local/total-costs-per-group?from=${__from:date}&to=${__to:date}', + }, + ]) + + bg.queryOptions.withTransformations([ + bg.queryOptions.transformation.withId('groupBy') + + bg.queryOptions.transformation.withOptions({ + fields: { + Cost: { + aggregations: [ + 'sum', + ], + operation: 'aggregate', + }, + Group: { + aggregations: [], + operation: 'groupby', + }, + }, + }), + bg.queryOptions.transformation.withId('sortBy') + + bg.queryOptions.transformation.withOptions({ + sort: [ + { + asc: true, + field: 'Group', + }, + ], + }), + bg.queryOptions.transformation.withId('transpose') + ]) + + bg.standardOptions.color.withMode('continuous-BlYlRd') +; + +local MultipleGroup = + tb.new('Users with multiple group memberships') + + tb.panelOptions.withDescription( + ||| + List of users with multiple group memberships. + + Note: Users with multiple group memberships are double-counted. E.g. if user 1 is a member of group 1 and group 2, then the user's individual costs are included in the total sums of each group. + ||| + ) + + tb.panelOptions.withGridPos(h=7, w=12, x=12, y=7) + + tb.queryOptions.withTargets([ + common.queryMultipleGroupTarget + { + url: 'http://jupyterhub-cost-monitoring.support.svc.cluster.local/user-groups?from=${__from:date}&to=${__to:date}', + }, + ]) + + tb.queryOptions.withTransformations([ + tb.queryOptions.transformation.withId('groupBy') + + tb.queryOptions.transformation.withOptions({ + fields: { + usergroup: { + aggregations: [], + operation: 'aggregate', + }, + username_escaped: { + aggregations: [], + operation: 'groupby', + }, + }, + }), + tb.queryOptions.transformation.withId('groupToNestedTable') + + tb.queryOptions.transformation.withOptions({ + fields: { + "usergroup": { + "aggregations": [] + }, + "username_escaped": { + "aggregations": [], + "operation": "groupby" + } + } + }), + tb.queryOptions.transformation.withId('organize') + + tb.queryOptions.transformation.withOptions({ + renameByName: { + username_escaped: 'User', + }, + }), + ]) +; + local Hub = common.bcOptions + bc.new('Hub – $hub_user, Component – $component') @@ -229,6 +328,8 @@ dashboard.new('Group cloud costs') TotalHub, TotalComponent, Top5, + TotalGroup, + MultipleGroup, Hub, ], ) diff --git a/dashboards/common.libsonnet b/dashboards/common.libsonnet index a2f2f19..dd32af3 100644 --- a/dashboards/common.libsonnet +++ b/dashboards/common.libsonnet @@ -202,6 +202,44 @@ local bg = grafonnet.panel.barGauge; refId: 'A', }, + queryGroupTarget: { + datasource: { + type: 'yesoreyeram-infinity-datasource', + uid: '${infinity_datasource}', + }, + columns: [ + { selector: 'date', text: 'Date', type: 'timestamp' }, + { selector: 'cost', text: 'Cost', type: 'number' }, + { selector: 'usergroup', text: 'Group', type: 'string' }, + ], + parser: 'backend', + type: 'json', + source: 'url', + url_options: { + method: 'GET', + data: '', + }, + format: 'table', + refId: 'A', + }, + + queryMultipleGroupTarget: { + datasource: { + type: 'yesoreyeram-infinity-datasource', + uid: '${infinity_datasource}', + }, + parser: 'backend', + root_selector: '$filter($, function($v, $i, $a) {\n $v.username in $distinct(($a[usergroup="multiple"].username))\n})\n', + type: 'json', + source: 'url', + url_options: { + method: 'GET', + data: '', + }, + format: 'table', + refId: 'A', + }, + // grafana ref: https://grafana.com/docs/grafana/v11.1/panels-visualizations/visualizations/time-series/ // grafonnet ref: https://grafana.github.io/grafonnet/API/panel/timeSeries/index.html tsOptions: @@ -286,4 +324,4 @@ local bg = grafonnet.panel.barGauge; + bg.standardOptions.withMin(0) + bg.standardOptions.withDecimals(2) + bg.standardOptions.withUnit('currencyUSD'), -} +} \ No newline at end of file