mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(plugins): made panels work as plugins
This commit is contained in:
parent
bd6e2d6ca4
commit
4a69de1f30
@ -106,9 +106,18 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
|||||||
defaultDatasource = "-- Grafana --"
|
defaultDatasource = "-- Grafana --"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panels := map[string]interface{}{}
|
||||||
|
for _, panel := range plugins.Panels {
|
||||||
|
panels[panel.Type] = map[string]interface{}{
|
||||||
|
"module": panel.Module,
|
||||||
|
"name": panel.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jsonObj := map[string]interface{}{
|
jsonObj := map[string]interface{}{
|
||||||
"defaultDatasource": defaultDatasource,
|
"defaultDatasource": defaultDatasource,
|
||||||
"datasources": datasources,
|
"datasources": datasources,
|
||||||
|
"panels": panels,
|
||||||
"appSubUrl": setting.AppSubUrl,
|
"appSubUrl": setting.AppSubUrl,
|
||||||
"allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin,
|
"allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin,
|
||||||
"buildInfo": map[string]interface{}{
|
"buildInfo": map[string]interface{}{
|
||||||
|
@ -15,6 +15,13 @@ type DataSourcePlugin struct {
|
|||||||
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PanelPlugin struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Module string `json:"module"`
|
||||||
|
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
||||||
|
}
|
||||||
|
|
||||||
type StaticRootConfig struct {
|
type StaticRootConfig struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DataSources map[string]DataSourcePlugin
|
DataSources map[string]DataSourcePlugin
|
||||||
|
Panels []PanelPlugin
|
||||||
ExternalPlugins []ExternalPlugin
|
ExternalPlugins []ExternalPlugin
|
||||||
StaticRoutes []*StaticRootConfig
|
StaticRoutes []*StaticRootConfig
|
||||||
)
|
)
|
||||||
@ -27,6 +28,7 @@ func Init() error {
|
|||||||
DataSources = make(map[string]DataSourcePlugin)
|
DataSources = make(map[string]DataSourcePlugin)
|
||||||
ExternalPlugins = make([]ExternalPlugin, 0)
|
ExternalPlugins = make([]ExternalPlugin, 0)
|
||||||
StaticRoutes = make([]*StaticRootConfig, 0)
|
StaticRoutes = make([]*StaticRootConfig, 0)
|
||||||
|
Panels = make([]PanelPlugin, 0)
|
||||||
|
|
||||||
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
||||||
checkExternalPluginPaths()
|
checkExternalPluginPaths()
|
||||||
@ -124,6 +126,21 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
|||||||
addStaticRoot(p.StaticRootConfig, currentDir)
|
addStaticRoot(p.StaticRootConfig, currentDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pluginType == "panel" {
|
||||||
|
p := PanelPlugin{}
|
||||||
|
reader.Seek(0, 0)
|
||||||
|
if err := jsonParser.Decode(&p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Type == "" {
|
||||||
|
return errors.New("Did not find type property in plugin.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Panels = append(Panels, p)
|
||||||
|
addStaticRoot(p.StaticRootConfig, currentDir)
|
||||||
|
}
|
||||||
|
|
||||||
if pluginType == "external" {
|
if pluginType == "external" {
|
||||||
p := ExternalPlugin{}
|
p := ExternalPlugin{}
|
||||||
reader.Seek(0, 0)
|
reader.Seek(0, 0)
|
||||||
|
@ -8,13 +8,7 @@ function (_) {
|
|||||||
var defaults = {
|
var defaults = {
|
||||||
datasources : {},
|
datasources : {},
|
||||||
window_title_prefix : 'Grafana - ',
|
window_title_prefix : 'Grafana - ',
|
||||||
panels : {
|
panels : {},
|
||||||
'graph': { path: 'app/panels/graph', name: 'Graph' },
|
|
||||||
'table': { path: 'app/panels/table', name: 'Table' },
|
|
||||||
'singlestat': { path: 'app/panels/singlestat', name: 'Single stat' },
|
|
||||||
'text': { path: 'app/panels/text', name: 'Text' },
|
|
||||||
'dashlist': { path: 'app/panels/dashlist', name: 'Dashboard list' },
|
|
||||||
},
|
|
||||||
new_panel_title: 'Panel Title',
|
new_panel_title: 'Panel Title',
|
||||||
playlist_timespan: "1m",
|
playlist_timespan: "1m",
|
||||||
unsaved_changes_warning: true,
|
unsaved_changes_warning: true,
|
||||||
|
@ -13,9 +13,9 @@ function (angular, $, config) {
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
link: function(scope, elem, attr) {
|
link: function(scope, elem, attr) {
|
||||||
var getter = $parse(attr.type), panelType = getter(scope);
|
var getter = $parse(attr.type), panelType = getter(scope);
|
||||||
var panelPath = config.panels[panelType].path;
|
var module = config.panels[panelType].module;
|
||||||
|
|
||||||
scope.require([panelPath + "/module"], function () {
|
scope.require([module], function () {
|
||||||
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
|
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
|
||||||
elem.append(panelEl);
|
elem.append(panelEl);
|
||||||
$compile(panelEl)(scope);
|
$compile(panelEl)(scope);
|
||||||
|
13
public/app/plugins/external/example/readme.md
vendored
Normal file
13
public/app/plugins/external/example/readme.md
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Example app is available at https://github.com/raintank/grafana-plugin-example
|
||||||
|
|
||||||
|
* Clone plugin repo git@github.com:raintank/grafana-plugin-example.git
|
||||||
|
|
||||||
|
* Modify grafana.ini (or custom.ini if your developing Grafana locally)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[plugin.external-test]
|
||||||
|
path = /<the_path_were_you_cloned_it>/grafana-plugin-example
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
Example app is available at https://github.com/raintank/grafana-plugin-example
|
|
||||||
|
|
||||||
To use, download the example app from github and run it (requires python Flask). Then rename the "_plugin.json" file in this director to "plugin.json" and restart Grafana.
|
|
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"pluginType": "externalPlugin",
|
|
||||||
"settings": {
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"path": "/example/static/*",
|
|
||||||
"method": "*",
|
|
||||||
"req_signed_in": false,
|
|
||||||
"req_grafana_admin": false,
|
|
||||||
"req_role": "Admin",
|
|
||||||
"url": "http://localhost:5000/static"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/example/api/*",
|
|
||||||
"method": "*",
|
|
||||||
"req_signed_in": true,
|
|
||||||
"req_grafana_admin": false,
|
|
||||||
"req_role": "Admin",
|
|
||||||
"url": "http://localhost:5000/api"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"css": [
|
|
||||||
{
|
|
||||||
"href": "/example/static/css/example.css"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"js": [
|
|
||||||
{
|
|
||||||
"src": "/example/static/js/app.js"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"menu_items": [
|
|
||||||
{
|
|
||||||
"text": "Example Plugin",
|
|
||||||
"icon": "fa fa-fw fa-smile-o",
|
|
||||||
"href": "/example/servers",
|
|
||||||
"adminOnly": false,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ function (angular, app, _, config, PanelMeta) {
|
|||||||
module.directive('grafanaPanelDashlist', function() {
|
module.directive('grafanaPanelDashlist', function() {
|
||||||
return {
|
return {
|
||||||
controller: 'DashListPanelCtrl',
|
controller: 'DashListPanelCtrl',
|
||||||
templateUrl: 'app/panels/dashlist/module.html',
|
templateUrl: 'app/plugins/panels/dashlist/module.html',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ function (angular, app, _, config, PanelMeta) {
|
|||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.panelMeta.addEditorTab('Options', 'app/panels/dashlist/editor.html');
|
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panels/dashlist/editor.html');
|
||||||
|
|
||||||
var defaults = {
|
var defaults = {
|
||||||
mode: 'starred',
|
mode: 'starred',
|
8
public/app/plugins/panels/dashlist/plugin.json
Normal file
8
public/app/plugins/panels/dashlist/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "panel",
|
||||||
|
|
||||||
|
"name": "Dashboard list",
|
||||||
|
"type": "dashlist",
|
||||||
|
|
||||||
|
"module": "app/plugins/panels/dashlist/module"
|
||||||
|
}
|
@ -45,7 +45,7 @@ function (angular, _, $) {
|
|||||||
popoverScope.series = seriesInfo;
|
popoverScope.series = seriesInfo;
|
||||||
popoverSrv.show({
|
popoverSrv.show({
|
||||||
element: el,
|
element: el,
|
||||||
templateUrl: 'app/panels/graph/legend.popover.html',
|
templateUrl: 'app/plugins/panels/graph/legend.popover.html',
|
||||||
scope: popoverScope
|
scope: popoverScope
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
|
|||||||
module.directive('grafanaPanelGraph', function() {
|
module.directive('grafanaPanelGraph', function() {
|
||||||
return {
|
return {
|
||||||
controller: 'GraphCtrl',
|
controller: 'GraphCtrl',
|
||||||
templateUrl: 'app/panels/graph/module.html',
|
templateUrl: 'app/plugins/panels/graph/module.html',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,8 +30,8 @@ function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
|
|||||||
metricsEditor: true,
|
metricsEditor: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.panelMeta.addEditorTab('Axes & Grid', 'app/panels/graph/axisEditor.html');
|
$scope.panelMeta.addEditorTab('Axes & Grid', 'app/plugins/panels/graph/axisEditor.html');
|
||||||
$scope.panelMeta.addEditorTab('Display Styles', 'app/panels/graph/styleEditor.html');
|
$scope.panelMeta.addEditorTab('Display Styles', 'app/plugins/panels/graph/styleEditor.html');
|
||||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||||
|
|
||||||
$scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
|
$scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
|
8
public/app/plugins/panels/graph/plugin.json
Normal file
8
public/app/plugins/panels/graph/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "panel",
|
||||||
|
|
||||||
|
"name": "Graph",
|
||||||
|
"type": "graph",
|
||||||
|
|
||||||
|
"module": "app/plugins/panels/graph/module"
|
||||||
|
}
|
@ -16,7 +16,7 @@ function (angular, app, _, kbn, TimeSeries, PanelMeta) {
|
|||||||
module.directive('grafanaPanelSinglestat', function() {
|
module.directive('grafanaPanelSinglestat', function() {
|
||||||
return {
|
return {
|
||||||
controller: 'SingleStatCtrl',
|
controller: 'SingleStatCtrl',
|
||||||
templateUrl: 'app/panels/singlestat/module.html',
|
templateUrl: 'app/plugins/panels/singlestat/module.html',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ function (angular, app, _, kbn, TimeSeries, PanelMeta) {
|
|||||||
|
|
||||||
$scope.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
$scope.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
||||||
|
|
||||||
$scope.panelMeta.addEditorTab('Options', 'app/panels/singlestat/editor.html');
|
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panels/singlestat/editor.html');
|
||||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
8
public/app/plugins/panels/singlestat/plugin.json
Normal file
8
public/app/plugins/panels/singlestat/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "panel",
|
||||||
|
|
||||||
|
"name": "Singlestat",
|
||||||
|
"type": "singlestat",
|
||||||
|
|
||||||
|
"module": "app/plugins/panels/singlestat/module"
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import angular = require('angular');
|
import angular = require('angular');
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
@ -21,7 +21,7 @@ export class TablePanelCtrl {
|
|||||||
metricsEditor: true,
|
metricsEditor: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.panelMeta.addEditorTab('Options', 'app/panels/table/options.html');
|
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panels/table/options.html');
|
||||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||||
|
|
||||||
var panelDefaults = {
|
var panelDefaults = {
|
@ -1,5 +1,4 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
///<reference path="../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular = require('angular');
|
import angular = require('angular');
|
||||||
import $ = require('jquery');
|
import $ = require('jquery');
|
||||||
@ -14,7 +13,7 @@ export function tablePanelEditor() {
|
|||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: true,
|
scope: true,
|
||||||
templateUrl: 'app/panels/table/editor.html',
|
templateUrl: 'app/plugins/panels/table/editor.html',
|
||||||
link: function(scope, elem) {
|
link: function(scope, elem) {
|
||||||
scope.transformers = transformers;
|
scope.transformers = transformers;
|
||||||
scope.unitFormats = kbn.getUnitFormats();
|
scope.unitFormats = kbn.getUnitFormats();
|
@ -1,4 +1,4 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import angular = require('angular');
|
import angular = require('angular');
|
||||||
import $ = require('jquery');
|
import $ = require('jquery');
|
||||||
@ -14,7 +14,7 @@ export function tablePanel() {
|
|||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
templateUrl: 'app/panels/table/module.html',
|
templateUrl: 'app/plugins/panels/table/module.html',
|
||||||
controller: TablePanelCtrl,
|
controller: TablePanelCtrl,
|
||||||
link: function(scope, elem) {
|
link: function(scope, elem) {
|
||||||
var data;
|
var data;
|
8
public/app/plugins/panels/table/plugin.json
Normal file
8
public/app/plugins/panels/table/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "panel",
|
||||||
|
|
||||||
|
"name": "Table",
|
||||||
|
"type": "table",
|
||||||
|
|
||||||
|
"module": "app/plugins/panels/table/module"
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
import kbn = require('app/core/utils/kbn');
|
import kbn = require('app/core/utils/kbn');
|
@ -1,4 +1,4 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
import moment = require('moment');
|
import moment = require('moment');
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
@ -16,7 +16,7 @@ function (angular, app, _, require, PanelMeta) {
|
|||||||
module.directive('grafanaPanelText', function() {
|
module.directive('grafanaPanelText', function() {
|
||||||
return {
|
return {
|
||||||
controller: 'TextPanelCtrl',
|
controller: 'TextPanelCtrl',
|
||||||
templateUrl: 'app/panels/text/module.html',
|
templateUrl: 'app/plugins/panels/text/module.html',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ function (angular, app, _, require, PanelMeta) {
|
|||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.panelMeta.addEditorTab('Edit text', 'app/panels/text/editor.html');
|
$scope.panelMeta.addEditorTab('Edit text', 'app/plugins/panels/text/editor.html');
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
||||||
var _d = {
|
var _d = {
|
||||||
@ -84,7 +84,7 @@ function (angular, app, _, require, PanelMeta) {
|
|||||||
$scope.updateContent(converter.makeHtml(text));
|
$scope.updateContent(converter.makeHtml(text));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
require(['./lib/showdown'], function (Showdown) {
|
require(['vendor/showdown'], function (Showdown) {
|
||||||
converter = new Showdown.converter();
|
converter = new Showdown.converter();
|
||||||
$scope.updateContent(converter.makeHtml(text));
|
$scope.updateContent(converter.makeHtml(text));
|
||||||
});
|
});
|
8
public/app/plugins/panels/text/plugin.json
Normal file
8
public/app/plugins/panels/text/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"pluginType": "panel",
|
||||||
|
|
||||||
|
"name": "Text",
|
||||||
|
"type": "text",
|
||||||
|
|
||||||
|
"module": "app/plugins/panels/text/module"
|
||||||
|
}
|
@ -2,7 +2,7 @@ define([
|
|||||||
'./helpers',
|
'./helpers',
|
||||||
'app/features/panel/panel_srv',
|
'app/features/panel/panel_srv',
|
||||||
'app/features/panel/panel_helper',
|
'app/features/panel/panel_helper',
|
||||||
'app/panels/graph/module'
|
'app/plugins/panels/graph/module'
|
||||||
], function(helpers) {
|
], function(helpers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ define([
|
|||||||
'angular',
|
'angular',
|
||||||
'jquery',
|
'jquery',
|
||||||
'app/core/time_series',
|
'app/core/time_series',
|
||||||
'app/panels/graph/graph'
|
'app/plugins/panels/graph/graph'
|
||||||
], function(helpers, angular, $, TimeSeries) {
|
], function(helpers, angular, $, TimeSeries) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
'jquery',
|
'jquery',
|
||||||
'app/panels/graph/graph.tooltip'
|
'app/plugins/panels/graph/graph.tooltip'
|
||||||
], function($, GraphTooltip) {
|
], function($, GraphTooltip) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
'./helpers',
|
'./helpers',
|
||||||
'app/panels/graph/seriesOverridesCtrl'
|
'app/plugins/panels/graph/seriesOverridesCtrl'
|
||||||
], function(helpers) {
|
], function(helpers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ define([
|
|||||||
'./helpers',
|
'./helpers',
|
||||||
'app/features/panel/panel_srv',
|
'app/features/panel/panel_srv',
|
||||||
'app/features/panel/panel_helper',
|
'app/features/panel/panel_helper',
|
||||||
'app/panels/singlestat/module'
|
'app/plugins/panels/singlestat/module'
|
||||||
], function(helpers) {
|
], function(helpers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user