1// Copyright 2016 Circonus, Inc. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Dashboard API support - Fetch, Create, Update, Delete, and Search 6// See: https://login.circonus.com/resources/api/calls/dashboard 7 8package api 9 10import ( 11 "encoding/json" 12 "fmt" 13 "net/url" 14 "regexp" 15 16 "github.com/circonus-labs/circonus-gometrics/api/config" 17) 18 19// DashboardGridLayout defines layout 20type DashboardGridLayout struct { 21 Height uint `json:"height"` 22 Width uint `json:"width"` 23} 24 25// DashboardAccessConfig defines access config 26type DashboardAccessConfig struct { 27 BlackDash bool `json:"black_dash"` 28 Enabled bool `json:"enabled"` 29 Fullscreen bool `json:"fullscreen"` 30 FullscreenHideTitle bool `json:"fullscreen_hide_title"` 31 Nickname string `json:"nickname"` 32 ScaleText bool `json:"scale_text"` 33 SharedID string `json:"shared_id"` 34 TextSize uint `json:"text_size"` 35} 36 37// DashboardOptions defines options 38type DashboardOptions struct { 39 AccessConfigs []DashboardAccessConfig `json:"access_configs"` 40 FullscreenHideTitle bool `json:"fullscreen_hide_title"` 41 HideGrid bool `json:"hide_grid"` 42 Linkages [][]string `json:"linkages"` 43 ScaleText bool `json:"scale_text"` 44 TextSize uint `json:"text_size"` 45} 46 47// ChartTextWidgetDatapoint defines datapoints for charts 48type ChartTextWidgetDatapoint struct { 49 AccountID string `json:"account_id,omitempty"` // metric cluster, metric 50 CheckID uint `json:"_check_id,omitempty"` // metric 51 ClusterID uint `json:"cluster_id,omitempty"` // metric cluster 52 ClusterTitle string `json:"_cluster_title,omitempty"` // metric cluster 53 Label string `json:"label,omitempty"` // metric 54 Label2 string `json:"_label,omitempty"` // metric cluster 55 Metric string `json:"metric,omitempty"` // metric 56 MetricType string `json:"_metric_type,omitempty"` // metric 57 NumericOnly bool `json:"numeric_only,omitempty"` // metric cluster 58} 59 60// ChartWidgetDefinitionLegend defines chart widget definition legend 61type ChartWidgetDefinitionLegend struct { 62 Show bool `json:"show,omitempty"` 63 Type string `json:"type,omitempty"` 64} 65 66// ChartWidgetWedgeLabels defines chart widget wedge labels 67type ChartWidgetWedgeLabels struct { 68 OnChart bool `json:"on_chart,omitempty"` 69 ToolTips bool `json:"tooltips,omitempty"` 70} 71 72// ChartWidgetWedgeValues defines chart widget wedge values 73type ChartWidgetWedgeValues struct { 74 Angle string `json:"angle,omitempty"` 75 Color string `json:"color,omitempty"` 76 Show bool `json:"show,omitempty"` 77} 78 79// ChartWidgtDefinition defines chart widget definition 80type ChartWidgtDefinition struct { 81 Datasource string `json:"datasource,omitempty"` 82 Derive string `json:"derive,omitempty"` 83 DisableAutoformat bool `json:"disable_autoformat,omitempty"` 84 Formula string `json:"formula,omitempty"` 85 Legend ChartWidgetDefinitionLegend `json:"legend,omitempty"` 86 Period uint `json:"period,omitempty"` 87 PopOnHover bool `json:"pop_onhover,omitempty"` 88 WedgeLabels ChartWidgetWedgeLabels `json:"wedge_labels,omitempty"` 89 WedgeValues ChartWidgetWedgeValues `json:"wedge_values,omitempty"` 90} 91 92// ForecastGaugeWidgetThresholds defines forecast widget thresholds 93type ForecastGaugeWidgetThresholds struct { 94 Colors []string `json:"colors,omitempty"` // forecasts, gauges 95 Flip bool `json:"flip,omitempty"` // gauges 96 Values []string `json:"values,omitempty"` // forecasts, gauges 97} 98 99// StatusWidgetAgentStatusSettings defines agent status settings 100type StatusWidgetAgentStatusSettings struct { 101 Search string `json:"search,omitempty"` 102 ShowAgentTypes string `json:"show_agent_types,omitempty"` 103 ShowContact bool `json:"show_contact,omitempty"` 104 ShowFeeds bool `json:"show_feeds,omitempty"` 105 ShowSetup bool `json:"show_setup,omitempty"` 106 ShowSkew bool `json:"show_skew,omitempty"` 107 ShowUpdates bool `json:"show_updates,omitempty"` 108} 109 110// StatusWidgetHostStatusSettings defines host status settings 111type StatusWidgetHostStatusSettings struct { 112 LayoutStyle string `json:"layout_style,omitempty"` 113 Search string `json:"search,omitempty"` 114 SortBy string `json:"sort_by,omitempty"` 115 TagFilterSet []string `json:"tag_filter_set,omitempty"` 116} 117 118// DashboardWidgetSettings defines settings specific to widget 119// Note: optional attributes which are structs need to be pointers so they will be omitted 120type DashboardWidgetSettings struct { 121 AccountID string `json:"account_id,omitempty"` // alerts, clusters, gauges, graphs, lists, status 122 Acknowledged string `json:"acknowledged,omitempty"` // alerts 123 AgentStatusSettings *StatusWidgetAgentStatusSettings `json:"agent_status_settings,omitempty"` // status 124 Algorithm string `json:"algorithm,omitempty"` // clusters 125 Autoformat bool `json:"autoformat,omitempty"` // text 126 BodyFormat string `json:"body_format,omitempty"` // text 127 ChartType string `json:"chart_type,omitempty"` // charts 128 CheckUUID string `json:"check_uuid,omitempty"` // gauges 129 Cleared string `json:"cleared,omitempty"` // alerts 130 ClusterID uint `json:"cluster_id,omitempty"` // clusters 131 ClusterName string `json:"cluster_name,omitempty"` // clusters 132 ContactGroups []uint `json:"contact_groups,omitempty"` // alerts 133 ContentType string `json:"content_type,omitempty"` // status 134 Datapoints []ChartTextWidgetDatapoint `json:"datapoints,omitempty"` // charts, text 135 DateWindow string `json:"date_window,omitempty"` // graphs 136 Definition *ChartWidgtDefinition `json:"definition,omitempty"` // charts 137 Dependents string `json:"dependents,omitempty"` // alerts 138 DisableAutoformat bool `json:"disable_autoformat,omitempty"` // gauges 139 Display string `json:"display,omitempty"` // alerts 140 Format string `json:"format,omitempty"` // forecasts 141 Formula string `json:"formula,omitempty"` // gauges 142 GraphUUID string `json:"graph_id,omitempty"` // graphs 143 HideXAxis bool `json:"hide_xaxis,omitempty"` // graphs 144 HideYAxis bool `json:"hide_yaxis,omitempty"` // graphs 145 HostStatusSettings *StatusWidgetHostStatusSettings `json:"host_status_settings,omitempty"` // status 146 KeyInline bool `json:"key_inline,omitempty"` // graphs 147 KeyLoc string `json:"key_loc,omitempty"` // graphs 148 KeySize uint `json:"key_size,omitempty"` // graphs 149 KeyWrap bool `json:"key_wrap,omitempty"` // graphs 150 Label string `json:"label,omitempty"` // graphs 151 Layout string `json:"layout,omitempty"` // clusters 152 Limit uint `json:"limit,omitempty"` // lists 153 Maintenance string `json:"maintenance,omitempty"` // alerts 154 Markup string `json:"markup,omitempty"` // html 155 MetricDisplayName string `json:"metric_display_name,omitempty"` // gauges 156 MetricName string `json:"metric_name,omitempty"` // gauges 157 MinAge string `json:"min_age,omitempty"` // alerts 158 OffHours []uint `json:"off_hours,omitempty"` // alerts 159 OverlaySetID string `json:"overlay_set_id,omitempty"` // graphs 160 Period uint `json:"period,omitempty"` // gauges, text, graphs 161 RangeHigh int `json:"range_high,omitempty"` // gauges 162 RangeLow int `json:"range_low,omitempty"` // gauges 163 Realtime bool `json:"realtime,omitempty"` // graphs 164 ResourceLimit string `json:"resource_limit,omitempty"` // forecasts 165 ResourceUsage string `json:"resource_usage,omitempty"` // forecasts 166 Search string `json:"search,omitempty"` // alerts, lists 167 Severity string `json:"severity,omitempty"` // alerts 168 ShowFlags bool `json:"show_flags,omitempty"` // graphs 169 Size string `json:"size,omitempty"` // clusters 170 TagFilterSet []string `json:"tag_filter_set,omitempty"` // alerts 171 Threshold float32 `json:"threshold,omitempty"` // clusters 172 Thresholds *ForecastGaugeWidgetThresholds `json:"thresholds,omitempty"` // forecasts, gauges 173 TimeWindow string `json:"time_window,omitempty"` // alerts 174 Title string `json:"title,omitempty"` // alerts, charts, forecasts, gauges, html 175 TitleFormat string `json:"title_format,omitempty"` // text 176 Trend string `json:"trend,omitempty"` // forecasts 177 Type string `json:"type,omitempty"` // gauges, lists 178 UseDefault bool `json:"use_default,omitempty"` // text 179 ValueType string `json:"value_type,omitempty"` // gauges, text 180 WeekDays []string `json:"weekdays,omitempty"` // alerts 181} 182 183// DashboardWidget defines widget 184type DashboardWidget struct { 185 Active bool `json:"active"` 186 Height uint `json:"height"` 187 Name string `json:"name"` 188 Origin string `json:"origin"` 189 Settings DashboardWidgetSettings `json:"settings"` 190 Type string `json:"type"` 191 WidgetID string `json:"widget_id"` 192 Width uint `json:"width"` 193} 194 195// Dashboard defines a dashboard. See https://login.circonus.com/resources/api/calls/dashboard for more information. 196type Dashboard struct { 197 AccountDefault bool `json:"account_default"` 198 Active bool `json:"_active,omitempty"` 199 CID string `json:"_cid,omitempty"` 200 Created uint `json:"_created,omitempty"` 201 CreatedBy string `json:"_created_by,omitempty"` 202 GridLayout DashboardGridLayout `json:"grid_layout"` 203 LastModified uint `json:"_last_modified,omitempty"` 204 Options DashboardOptions `json:"options"` 205 Shared bool `json:"shared"` 206 Title string `json:"title"` 207 UUID string `json:"_dashboard_uuid,omitempty"` 208 Widgets []DashboardWidget `json:"widgets"` 209} 210 211// NewDashboard returns a new Dashboard (with defaults, if applicable) 212func NewDashboard() *Dashboard { 213 return &Dashboard{} 214} 215 216// FetchDashboard retrieves dashboard with passed cid. 217func (a *API) FetchDashboard(cid CIDType) (*Dashboard, error) { 218 if cid == nil || *cid == "" { 219 return nil, fmt.Errorf("Invalid dashboard CID [none]") 220 } 221 222 dashboardCID := string(*cid) 223 224 matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID) 225 if err != nil { 226 return nil, err 227 } 228 if !matched { 229 return nil, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID) 230 } 231 232 result, err := a.Get(string(*cid)) 233 if err != nil { 234 return nil, err 235 } 236 237 if a.Debug { 238 a.Log.Printf("[DEBUG] fetch dashboard, received JSON: %s", string(result)) 239 } 240 241 dashboard := new(Dashboard) 242 if err := json.Unmarshal(result, dashboard); err != nil { 243 return nil, err 244 } 245 246 return dashboard, nil 247} 248 249// FetchDashboards retrieves all dashboards available to the API Token. 250func (a *API) FetchDashboards() (*[]Dashboard, error) { 251 result, err := a.Get(config.DashboardPrefix) 252 if err != nil { 253 return nil, err 254 } 255 256 var dashboards []Dashboard 257 if err := json.Unmarshal(result, &dashboards); err != nil { 258 return nil, err 259 } 260 261 return &dashboards, nil 262} 263 264// UpdateDashboard updates passed dashboard. 265func (a *API) UpdateDashboard(cfg *Dashboard) (*Dashboard, error) { 266 if cfg == nil { 267 return nil, fmt.Errorf("Invalid dashboard config [nil]") 268 } 269 270 dashboardCID := string(cfg.CID) 271 272 matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID) 273 if err != nil { 274 return nil, err 275 } 276 if !matched { 277 return nil, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID) 278 } 279 280 jsonCfg, err := json.Marshal(cfg) 281 if err != nil { 282 return nil, err 283 } 284 285 if a.Debug { 286 a.Log.Printf("[DEBUG] update dashboard, sending JSON: %s", string(jsonCfg)) 287 } 288 289 result, err := a.Put(dashboardCID, jsonCfg) 290 if err != nil { 291 return nil, err 292 } 293 294 dashboard := &Dashboard{} 295 if err := json.Unmarshal(result, dashboard); err != nil { 296 return nil, err 297 } 298 299 return dashboard, nil 300} 301 302// CreateDashboard creates a new dashboard. 303func (a *API) CreateDashboard(cfg *Dashboard) (*Dashboard, error) { 304 if cfg == nil { 305 return nil, fmt.Errorf("Invalid dashboard config [nil]") 306 } 307 308 jsonCfg, err := json.Marshal(cfg) 309 if err != nil { 310 return nil, err 311 } 312 313 if a.Debug { 314 a.Log.Printf("[DEBUG] create dashboard, sending JSON: %s", string(jsonCfg)) 315 } 316 317 result, err := a.Post(config.DashboardPrefix, jsonCfg) 318 if err != nil { 319 return nil, err 320 } 321 322 dashboard := &Dashboard{} 323 if err := json.Unmarshal(result, dashboard); err != nil { 324 return nil, err 325 } 326 327 return dashboard, nil 328} 329 330// DeleteDashboard deletes passed dashboard. 331func (a *API) DeleteDashboard(cfg *Dashboard) (bool, error) { 332 if cfg == nil { 333 return false, fmt.Errorf("Invalid dashboard config [nil]") 334 } 335 return a.DeleteDashboardByCID(CIDType(&cfg.CID)) 336} 337 338// DeleteDashboardByCID deletes dashboard with passed cid. 339func (a *API) DeleteDashboardByCID(cid CIDType) (bool, error) { 340 if cid == nil || *cid == "" { 341 return false, fmt.Errorf("Invalid dashboard CID [none]") 342 } 343 344 dashboardCID := string(*cid) 345 346 matched, err := regexp.MatchString(config.DashboardCIDRegex, dashboardCID) 347 if err != nil { 348 return false, err 349 } 350 if !matched { 351 return false, fmt.Errorf("Invalid dashboard CID [%s]", dashboardCID) 352 } 353 354 _, err = a.Delete(dashboardCID) 355 if err != nil { 356 return false, err 357 } 358 359 return true, nil 360} 361 362// SearchDashboards returns dashboards matching the specified 363// search query and/or filter. If nil is passed for both parameters 364// all dashboards will be returned. 365func (a *API) SearchDashboards(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]Dashboard, error) { 366 q := url.Values{} 367 368 if searchCriteria != nil && *searchCriteria != "" { 369 q.Set("search", string(*searchCriteria)) 370 } 371 372 if filterCriteria != nil && len(*filterCriteria) > 0 { 373 for filter, criteria := range *filterCriteria { 374 for _, val := range criteria { 375 q.Add(filter, val) 376 } 377 } 378 } 379 380 if q.Encode() == "" { 381 return a.FetchDashboards() 382 } 383 384 reqURL := url.URL{ 385 Path: config.DashboardPrefix, 386 RawQuery: q.Encode(), 387 } 388 389 result, err := a.Get(reqURL.String()) 390 if err != nil { 391 return nil, fmt.Errorf("[ERROR] API call error %+v", err) 392 } 393 394 var dashboards []Dashboard 395 if err := json.Unmarshal(result, &dashboards); err != nil { 396 return nil, err 397 } 398 399 return &dashboards, nil 400} 401