2017-01-31 19:42:18 +00:00
|
|
|
// Copyright 2016 Circonus, Inc. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Dashboard API support - Fetch, Create, Update, Delete, and Search
|
|
|
|
// See: https://login.circonus.com/resources/api/calls/dashboard
|
|
|
|
|
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"regexp"
|
|
|
|
|
|
|
|
"github.com/circonus-labs/circonus-gometrics/api/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DashboardGridLayout defines layout
|
|
|
|
type DashboardGridLayout struct {
|
|
|
|
Height uint `json:"height"`
|
|
|
|
Width uint `json:"width"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DashboardAccessConfig defines access config
|
|
|
|
type DashboardAccessConfig struct {
|
2018-09-10 22:03:17 +00:00
|
|
|
BlackDash bool `json:"black_dash"`
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
Fullscreen bool `json:"fullscreen"`
|
|
|
|
FullscreenHideTitle bool `json:"fullscreen_hide_title"`
|
|
|
|
Nickname string `json:"nickname"`
|
|
|
|
ScaleText bool `json:"scale_text"`
|
|
|
|
SharedID string `json:"shared_id"`
|
|
|
|
TextSize uint `json:"text_size"`
|
2017-01-31 19:42:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DashboardOptions defines options
|
|
|
|
type DashboardOptions struct {
|
2018-09-10 22:03:17 +00:00
|
|
|
AccessConfigs []DashboardAccessConfig `json:"access_configs"`
|
|
|
|
FullscreenHideTitle bool `json:"fullscreen_hide_title"`
|
|
|
|
HideGrid bool `json:"hide_grid"`
|
|
|
|
Linkages [][]string `json:"linkages"`
|
|
|
|
ScaleText bool `json:"scale_text"`
|
|
|
|
TextSize uint `json:"text_size"`
|
2017-01-31 19:42:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ChartTextWidgetDatapoint defines datapoints for charts
|
|
|
|
type ChartTextWidgetDatapoint struct {
|
|
|
|
AccountID string `json:"account_id,omitempty"` // metric cluster, metric
|
|
|
|
CheckID uint `json:"_check_id,omitempty"` // metric
|
|
|
|
ClusterID uint `json:"cluster_id,omitempty"` // metric cluster
|
|
|
|
ClusterTitle string `json:"_cluster_title,omitempty"` // metric cluster
|
|
|
|
Label string `json:"label,omitempty"` // metric
|
|
|
|
Label2 string `json:"_label,omitempty"` // metric cluster
|
|
|
|
Metric string `json:"metric,omitempty"` // metric
|
|
|
|
MetricType string `json:"_metric_type,omitempty"` // metric
|
|
|
|
NumericOnly bool `json:"numeric_only,omitempty"` // metric cluster
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChartWidgetDefinitionLegend defines chart widget definition legend
|
|
|
|
type ChartWidgetDefinitionLegend struct {
|
|
|
|
Show bool `json:"show,omitempty"`
|
|
|
|
Type string `json:"type,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChartWidgetWedgeLabels defines chart widget wedge labels
|
|
|
|
type ChartWidgetWedgeLabels struct {
|
|
|
|
OnChart bool `json:"on_chart,omitempty"`
|
|
|
|
ToolTips bool `json:"tooltips,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChartWidgetWedgeValues defines chart widget wedge values
|
|
|
|
type ChartWidgetWedgeValues struct {
|
|
|
|
Angle string `json:"angle,omitempty"`
|
|
|
|
Color string `json:"color,omitempty"`
|
|
|
|
Show bool `json:"show,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChartWidgtDefinition defines chart widget definition
|
|
|
|
type ChartWidgtDefinition struct {
|
|
|
|
Datasource string `json:"datasource,omitempty"`
|
|
|
|
Derive string `json:"derive,omitempty"`
|
|
|
|
DisableAutoformat bool `json:"disable_autoformat,omitempty"`
|
|
|
|
Formula string `json:"formula,omitempty"`
|
|
|
|
Legend ChartWidgetDefinitionLegend `json:"legend,omitempty"`
|
|
|
|
Period uint `json:"period,omitempty"`
|
|
|
|
PopOnHover bool `json:"pop_onhover,omitempty"`
|
|
|
|
WedgeLabels ChartWidgetWedgeLabels `json:"wedge_labels,omitempty"`
|
|
|
|
WedgeValues ChartWidgetWedgeValues `json:"wedge_values,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ForecastGaugeWidgetThresholds defines forecast widget thresholds
|
|
|
|
type ForecastGaugeWidgetThresholds struct {
|
|
|
|
Colors []string `json:"colors,omitempty"` // forecasts, gauges
|
|
|
|
Flip bool `json:"flip,omitempty"` // gauges
|
|
|
|
Values []string `json:"values,omitempty"` // forecasts, gauges
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatusWidgetAgentStatusSettings defines agent status settings
|
|
|
|
type StatusWidgetAgentStatusSettings struct {
|
|
|
|
Search string `json:"search,omitempty"`
|
|
|
|
ShowAgentTypes string `json:"show_agent_types,omitempty"`
|
|
|
|
ShowContact bool `json:"show_contact,omitempty"`
|
|
|
|
ShowFeeds bool `json:"show_feeds,omitempty"`
|
|
|
|
ShowSetup bool `json:"show_setup,omitempty"`
|
|
|
|
ShowSkew bool `json:"show_skew,omitempty"`
|
|
|
|
ShowUpdates bool `json:"show_updates,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatusWidgetHostStatusSettings defines host status settings
|
|
|
|
type StatusWidgetHostStatusSettings struct {
|
|
|
|
LayoutStyle string `json:"layout_style,omitempty"`
|
|
|
|
Search string `json:"search,omitempty"`
|
|
|
|
SortBy string `json:"sort_by,omitempty"`
|
|
|
|
TagFilterSet []string `json:"tag_filter_set,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DashboardWidgetSettings defines settings specific to widget
|
2018-09-10 22:03:17 +00:00
|
|
|
// Note: optional attributes which are structs need to be pointers so they will be omitted
|
2017-01-31 19:42:18 +00:00
|
|
|
type DashboardWidgetSettings struct {
|
2018-09-10 22:03:17 +00:00
|
|
|
AccountID string `json:"account_id,omitempty"` // alerts, clusters, gauges, graphs, lists, status
|
|
|
|
Acknowledged string `json:"acknowledged,omitempty"` // alerts
|
|
|
|
AgentStatusSettings *StatusWidgetAgentStatusSettings `json:"agent_status_settings,omitempty"` // status
|
|
|
|
Algorithm string `json:"algorithm,omitempty"` // clusters
|
|
|
|
Autoformat bool `json:"autoformat,omitempty"` // text
|
|
|
|
BodyFormat string `json:"body_format,omitempty"` // text
|
|
|
|
ChartType string `json:"chart_type,omitempty"` // charts
|
|
|
|
CheckUUID string `json:"check_uuid,omitempty"` // gauges
|
|
|
|
Cleared string `json:"cleared,omitempty"` // alerts
|
|
|
|
ClusterID uint `json:"cluster_id,omitempty"` // clusters
|
|
|
|
ClusterName string `json:"cluster_name,omitempty"` // clusters
|
|
|
|
ContactGroups []uint `json:"contact_groups,omitempty"` // alerts
|
|
|
|
ContentType string `json:"content_type,omitempty"` // status
|
|
|
|
Datapoints []ChartTextWidgetDatapoint `json:"datapoints,omitempty"` // charts, text
|
|
|
|
DateWindow string `json:"date_window,omitempty"` // graphs
|
|
|
|
Definition *ChartWidgtDefinition `json:"definition,omitempty"` // charts
|
|
|
|
Dependents string `json:"dependents,omitempty"` // alerts
|
|
|
|
DisableAutoformat bool `json:"disable_autoformat,omitempty"` // gauges
|
|
|
|
Display string `json:"display,omitempty"` // alerts
|
|
|
|
Format string `json:"format,omitempty"` // forecasts
|
|
|
|
Formula string `json:"formula,omitempty"` // gauges
|
|
|
|
GraphUUID string `json:"graph_id,omitempty"` // graphs
|
|
|
|
HideXAxis bool `json:"hide_xaxis,omitempty"` // graphs
|
|
|
|
HideYAxis bool `json:"hide_yaxis,omitempty"` // graphs
|
|
|
|
HostStatusSettings *StatusWidgetHostStatusSettings `json:"host_status_settings,omitempty"` // status
|
|
|
|
KeyInline bool `json:"key_inline,omitempty"` // graphs
|
|
|
|
KeyLoc string `json:"key_loc,omitempty"` // graphs
|
|
|
|
KeySize uint `json:"key_size,omitempty"` // graphs
|
|
|
|
KeyWrap bool `json:"key_wrap,omitempty"` // graphs
|
|
|
|
Label string `json:"label,omitempty"` // graphs
|
|
|
|
Layout string `json:"layout,omitempty"` // clusters
|
|
|
|
Limit uint `json:"limit,omitempty"` // lists
|
|
|
|
Maintenance string `json:"maintenance,omitempty"` // alerts
|
|
|
|
Markup string `json:"markup,omitempty"` // html
|
|
|
|
MetricDisplayName string `json:"metric_display_name,omitempty"` // gauges
|
|
|
|
MetricName string `json:"metric_name,omitempty"` // gauges
|
|
|
|
MinAge string `json:"min_age,omitempty"` // alerts
|
|
|
|
OffHours []uint `json:"off_hours,omitempty"` // alerts
|
|
|
|
OverlaySetID string `json:"overlay_set_id,omitempty"` // graphs
|
|
|
|
Period uint `json:"period,omitempty"` // gauges, text, graphs
|
|
|
|
RangeHigh int `json:"range_high,omitempty"` // gauges
|
|
|
|
RangeLow int `json:"range_low,omitempty"` // gauges
|
|
|
|
Realtime bool `json:"realtime,omitempty"` // graphs
|
|
|
|
ResourceLimit string `json:"resource_limit,omitempty"` // forecasts
|
|
|
|
ResourceUsage string `json:"resource_usage,omitempty"` // forecasts
|
|
|
|
Search string `json:"search,omitempty"` // alerts, lists
|
|
|
|
Severity string `json:"severity,omitempty"` // alerts
|
|
|
|
ShowFlags bool `json:"show_flags,omitempty"` // graphs
|
|
|
|
Size string `json:"size,omitempty"` // clusters
|
|
|
|
TagFilterSet []string `json:"tag_filter_set,omitempty"` // alerts
|
|
|
|
Threshold float32 `json:"threshold,omitempty"` // clusters
|
|
|
|
Thresholds *ForecastGaugeWidgetThresholds `json:"thresholds,omitempty"` // forecasts, gauges
|
|
|
|
TimeWindow string `json:"time_window,omitempty"` // alerts
|
|
|
|
Title string `json:"title,omitempty"` // alerts, charts, forecasts, gauges, html
|
|
|
|
TitleFormat string `json:"title_format,omitempty"` // text
|
|
|
|
Trend string `json:"trend,omitempty"` // forecasts
|
|
|
|
Type string `json:"type,omitempty"` // gauges, lists
|
|
|
|
UseDefault bool `json:"use_default,omitempty"` // text
|
|
|
|
ValueType string `json:"value_type,omitempty"` // gauges, text
|
|
|
|
WeekDays []string `json:"weekdays,omitempty"` // alerts
|
2017-01-31 19:42:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DashboardWidget defines widget
|
|
|
|
type DashboardWidget struct {
|
|
|
|
Active bool `json:"active"`
|
|
|
|
Height uint `json:"height"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Origin string `json:"origin"`
|
|
|
|
Settings DashboardWidgetSettings `json:"settings"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
WidgetID string `json:"widget_id"`
|
|
|
|
Width uint `json:"width"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dashboard defines a dashboard. See https://login.circonus.com/resources/api/calls/dashboard for more information.
|
|
|
|
type Dashboard struct {
|
|
|
|
AccountDefault bool `json:"account_default"`
|
|
|
|
Active bool `json:"_active,omitempty"`
|
|
|
|
CID string `json:"_cid,omitempty"`
|
|
|
|
Created uint `json:"_created,omitempty"`
|
|
|
|
CreatedBy string `json:"_created_by,omitempty"`
|
|
|
|
GridLayout DashboardGridLayout `json:"grid_layout"`
|
|
|
|
LastModified uint `json:"_last_modified,omitempty"`
|
|
|
|
Options DashboardOptions `json:"options"`
|
|
|
|
Shared bool `json:"shared"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
UUID string `json:"_dashboard_uuid,omitempty"`
|
|
|
|
Widgets []DashboardWidget `json:"widgets"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDashboard returns a new Dashboard (with defaults, if applicable)
|
|
|
|
func NewDashboard() *Dashboard {
|
|
|
|
return &Dashboard{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FetchDashboard retrieves dashboard with passed cid.
|
|
|
|
func (a *API) FetchDashboard(cid CIDType) (*Dashboard, error) {
|
|
|
|
if cid == nil || *cid == "" {
|
|
|
|
return nil, fmt.Errorf("Invalid dashboard CID [none]")
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboardCID := string(*cid)
|
|
|
|
|
|
|
|
matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !matched {
|
|
|
|
return nil, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID)
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := a.Get(string(*cid))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Debug {
|
|
|
|
a.Log.Printf("[DEBUG] fetch dashboard, received JSON: %s", string(result))
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboard := new(Dashboard)
|
|
|
|
if err := json.Unmarshal(result, dashboard); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dashboard, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FetchDashboards retrieves all dashboards available to the API Token.
|
|
|
|
func (a *API) FetchDashboards() (*[]Dashboard, error) {
|
|
|
|
result, err := a.Get(config.DashboardPrefix)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var dashboards []Dashboard
|
|
|
|
if err := json.Unmarshal(result, &dashboards); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dashboards, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateDashboard updates passed dashboard.
|
|
|
|
func (a *API) UpdateDashboard(cfg *Dashboard) (*Dashboard, error) {
|
|
|
|
if cfg == nil {
|
|
|
|
return nil, fmt.Errorf("Invalid dashboard config [nil]")
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboardCID := string(cfg.CID)
|
|
|
|
|
|
|
|
matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !matched {
|
|
|
|
return nil, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID)
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonCfg, err := json.Marshal(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Debug {
|
|
|
|
a.Log.Printf("[DEBUG] update dashboard, sending JSON: %s", string(jsonCfg))
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := a.Put(dashboardCID, jsonCfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboard := &Dashboard{}
|
|
|
|
if err := json.Unmarshal(result, dashboard); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dashboard, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateDashboard creates a new dashboard.
|
|
|
|
func (a *API) CreateDashboard(cfg *Dashboard) (*Dashboard, error) {
|
|
|
|
if cfg == nil {
|
|
|
|
return nil, fmt.Errorf("Invalid dashboard config [nil]")
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonCfg, err := json.Marshal(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Debug {
|
|
|
|
a.Log.Printf("[DEBUG] create dashboard, sending JSON: %s", string(jsonCfg))
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := a.Post(config.DashboardPrefix, jsonCfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboard := &Dashboard{}
|
|
|
|
if err := json.Unmarshal(result, dashboard); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return dashboard, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteDashboard deletes passed dashboard.
|
|
|
|
func (a *API) DeleteDashboard(cfg *Dashboard) (bool, error) {
|
|
|
|
if cfg == nil {
|
|
|
|
return false, fmt.Errorf("Invalid dashboard config [nil]")
|
|
|
|
}
|
|
|
|
return a.DeleteDashboardByCID(CIDType(&cfg.CID))
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteDashboardByCID deletes dashboard with passed cid.
|
|
|
|
func (a *API) DeleteDashboardByCID(cid CIDType) (bool, error) {
|
|
|
|
if cid == nil || *cid == "" {
|
|
|
|
return false, fmt.Errorf("Invalid dashboard CID [none]")
|
|
|
|
}
|
|
|
|
|
|
|
|
dashboardCID := string(*cid)
|
|
|
|
|
|
|
|
matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if !matched {
|
|
|
|
return false, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = a.Delete(dashboardCID)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SearchDashboards returns dashboards matching the specified
|
|
|
|
// search query and/or filter. If nil is passed for both parameters
|
|
|
|
// all dashboards will be returned.
|
|
|
|
func (a *API) SearchDashboards(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Dashboard, error) {
|
|
|
|
q := url.Values{}
|
|
|
|
|
|
|
|
if searchCriteria != nil && *searchCriteria != "" {
|
|
|
|
q.Set("search", string(*searchCriteria))
|
|
|
|
}
|
|
|
|
|
|
|
|
if filterCriteria != nil && len(*filterCriteria) > 0 {
|
|
|
|
for filter, criteria := range *filterCriteria {
|
|
|
|
for _, val := range criteria {
|
|
|
|
q.Add(filter, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if q.Encode() == "" {
|
|
|
|
return a.FetchDashboards()
|
|
|
|
}
|
|
|
|
|
|
|
|
reqURL := url.URL{
|
|
|
|
Path: config.DashboardPrefix,
|
|
|
|
RawQuery: q.Encode(),
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := a.Get(reqURL.String())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("[ERROR] API call error %+v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var dashboards []Dashboard
|
|
|
|
if err := json.Unmarshal(result, &dashboards); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dashboards, nil
|
|
|
|
}
|