mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Began work on plugin system
This commit is contained in:
parent
011fdf7ab6
commit
5bd5713a52
@ -24,6 +24,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
datasources := make(map[string]interface{})
|
datasources := make(map[string]interface{})
|
||||||
|
var defaultDatasource string
|
||||||
|
|
||||||
for _, ds := range orgDataSources {
|
for _, ds := range orgDataSources {
|
||||||
url := ds.Url
|
url := ds.Url
|
||||||
@ -35,7 +36,10 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
|||||||
var dsMap = map[string]interface{}{
|
var dsMap = map[string]interface{}{
|
||||||
"type": ds.Type,
|
"type": ds.Type,
|
||||||
"url": url,
|
"url": url,
|
||||||
"default": ds.IsDefault,
|
}
|
||||||
|
|
||||||
|
if ds.IsDefault {
|
||||||
|
defaultDatasource = ds.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds.Type == m.DS_INFLUXDB_08 {
|
if ds.Type == m.DS_INFLUXDB_08 {
|
||||||
@ -69,6 +73,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
jsonObj := map[string]interface{}{
|
jsonObj := map[string]interface{}{
|
||||||
|
"defaultDatasource": defaultDatasource,
|
||||||
"datasources": datasources,
|
"datasources": datasources,
|
||||||
"appSubUrl": setting.AppSubUrl,
|
"appSubUrl": setting.AppSubUrl,
|
||||||
"buildInfo": map[string]interface{}{
|
"buildInfo": map[string]interface{}{
|
||||||
|
@ -83,6 +83,8 @@ func importDashboard(path string, orgId int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
dash := m.NewDashboard("temp")
|
dash := m.NewDashboard("temp")
|
||||||
jsonParser := json.NewDecoder(reader)
|
jsonParser := json.NewDecoder(reader)
|
||||||
|
|
||||||
|
81
pkg/plugins/plugins.go
Normal file
81
pkg/plugins/plugins.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginMeta struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
List []*PluginMeta
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginScanner struct {
|
||||||
|
pluginPath string
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func Scan(pluginDir string) error {
|
||||||
|
List = make([]*PluginMeta, 0)
|
||||||
|
|
||||||
|
scanner := &PluginScanner{
|
||||||
|
pluginPath: pluginDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := filepath.Walk(pluginDir, scanner.walker); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scanner.errors) > 0 {
|
||||||
|
return errors.New("Some plugins failed to load")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scanner *PluginScanner) walker(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Name() == "plugin.json" {
|
||||||
|
pluginMeta, err := loadPluginMeta(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(3, "Failed to load plugin json file: %v, err: %v", path, err)
|
||||||
|
scanner.errors = append(scanner.errors, err)
|
||||||
|
} else {
|
||||||
|
List = append(List, pluginMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPluginMeta(path string) (*PluginMeta, error) {
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
jsonParser := json.NewDecoder(reader)
|
||||||
|
|
||||||
|
pluginMeta := &PluginMeta{}
|
||||||
|
if err := jsonParser.Decode(pluginMeta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluginMeta, nil
|
||||||
|
}
|
19
pkg/plugins/plugins_test.go
Normal file
19
pkg/plugins/plugins_test.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPluginScans(t *testing.T) {
|
||||||
|
|
||||||
|
Convey("When scaning for plugins", t, func() {
|
||||||
|
path, _ := filepath.Abs("../../src/app/plugins")
|
||||||
|
err := Scan(path)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(List), ShouldEqual, 1)
|
||||||
|
})
|
||||||
|
}
|
@ -8,14 +8,13 @@ function (angular, _, config) {
|
|||||||
|
|
||||||
var module = angular.module('grafana.controllers');
|
var module = angular.module('grafana.controllers');
|
||||||
|
|
||||||
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, datasourceSrv, $timeout) {
|
module.controller('SearchCtrl', function($scope, $location, $timeout, backendSrv) {
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.giveSearchFocus = 0;
|
$scope.giveSearchFocus = 0;
|
||||||
$scope.selectedIndex = -1;
|
$scope.selectedIndex = -1;
|
||||||
$scope.results = {dashboards: [], tags: [], metrics: []};
|
$scope.results = {dashboards: [], tags: [], metrics: []};
|
||||||
$scope.query = { query: '', tag: '', starred: false };
|
$scope.query = { query: '', tag: '', starred: false };
|
||||||
$scope.db = datasourceSrv.getGrafanaDB();
|
|
||||||
$scope.currentSearchId = 0;
|
$scope.currentSearchId = 0;
|
||||||
|
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
@ -61,8 +60,7 @@ function (angular, _, config) {
|
|||||||
$scope.currentSearchId = $scope.currentSearchId + 1;
|
$scope.currentSearchId = $scope.currentSearchId + 1;
|
||||||
var localSearchId = $scope.currentSearchId;
|
var localSearchId = $scope.currentSearchId;
|
||||||
|
|
||||||
return $scope.db.searchDashboards($scope.query)
|
return backendSrv.get('/api/search', $scope.query).then(function(results) {
|
||||||
.then(function(results) {
|
|
||||||
if (localSearchId < $scope.currentSearchId) { return; }
|
if (localSearchId < $scope.currentSearchId) { return; }
|
||||||
|
|
||||||
$scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length;
|
$scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length;
|
||||||
|
@ -2,11 +2,11 @@ define([
|
|||||||
'./panellinkeditor/module',
|
'./panellinkeditor/module',
|
||||||
'./annotations/annotationsSrv',
|
'./annotations/annotationsSrv',
|
||||||
'./templating/templateSrv',
|
'./templating/templateSrv',
|
||||||
'./graphite/datasource',
|
// './graphite/datasource',
|
||||||
'./influxdb/datasource',
|
// './influxdb/datasource',
|
||||||
'./influxdb_08/datasource',
|
// './influxdb_08/datasource',
|
||||||
'./opentsdb/datasource',
|
// './opentsdb/datasource',
|
||||||
'./elasticsearch/datasource',
|
// './elasticsearch/datasource',
|
||||||
'./dashboard/all',
|
'./dashboard/all',
|
||||||
'./panel/all',
|
'./panel/all',
|
||||||
'./profile/profileCtrl',
|
'./profile/profileCtrl',
|
||||||
|
@ -94,8 +94,8 @@ function (angular, _, config) {
|
|||||||
$scope.fullscreen = false;
|
$scope.fullscreen = false;
|
||||||
$scope.editor = { index: 1 };
|
$scope.editor = { index: 1 };
|
||||||
|
|
||||||
$scope.datasources = datasourceSrv.getMetricSources();
|
// $scope.datasources = datasourceSrv.getMetricSources();
|
||||||
$scope.setDatasource($scope.panel.datasource);
|
// $scope.setDatasource($scope.panel.datasource);
|
||||||
$scope.dashboardViewState.registerPanel($scope);
|
$scope.dashboardViewState.registerPanel($scope);
|
||||||
|
|
||||||
if ($scope.get_data) {
|
if ($scope.get_data) {
|
||||||
|
@ -23,7 +23,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv) {
|
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv, datasourceSrv) {
|
||||||
|
|
||||||
$scope.panelMeta = new PanelMeta({
|
$scope.panelMeta = new PanelMeta({
|
||||||
panelName: 'Graph',
|
panelName: 'Graph',
|
||||||
@ -183,7 +183,8 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
|||||||
|
|
||||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
|
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
|
||||||
|
|
||||||
return $scope.datasource.query(metricsQuery)
|
return datasourceSrv.get($scope.panel.datasource).then(function(ds) {
|
||||||
|
return ds.query(metricsQuery)
|
||||||
.then($scope.dataHandler)
|
.then($scope.dataHandler)
|
||||||
.then(null, function(err) {
|
.then(null, function(err) {
|
||||||
$scope.panelMeta.loading = false;
|
$scope.panelMeta.loading = false;
|
||||||
@ -192,6 +193,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
|||||||
$scope.seriesList = [];
|
$scope.seriesList = [];
|
||||||
$scope.render([]);
|
$scope.render([]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.dataHandler = function(results) {
|
$scope.dataHandler = function(results) {
|
||||||
|
20
src/app/plugins/datasources/graphite/module.js
Normal file
20
src/app/plugins/datasources/graphite/module.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
],
|
||||||
|
function (angular) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.services');
|
||||||
|
|
||||||
|
module.factory('MyDataSource', function() {
|
||||||
|
|
||||||
|
function MyDataSource(datasource) {
|
||||||
|
this.type = 'my_ds';
|
||||||
|
this.datasource = datasource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MyDataSource;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
7
src/app/plugins/datasources/graphite/plugin.json
Normal file
7
src/app/plugins/datasources/graphite/plugin.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "datasource",
|
||||||
|
"name": "My Data source",
|
||||||
|
"keyName": "graphite"
|
||||||
|
"serviceName": "MyDataSource",
|
||||||
|
"editPartial": "./partials/edit.html",
|
||||||
|
}
|
@ -7,9 +7,7 @@ function (angular) {
|
|||||||
|
|
||||||
var module = angular.module('grafana.routes');
|
var module = angular.module('grafana.routes');
|
||||||
|
|
||||||
module.controller('DashFromDBProvider', function($scope, datasourceSrv, $routeParams, backendSrv) {
|
module.controller('DashFromDBProvider', function($scope, $routeParams, backendSrv) {
|
||||||
|
|
||||||
var db = datasourceSrv.getGrafanaDB();
|
|
||||||
|
|
||||||
if (!$routeParams.id) {
|
if (!$routeParams.id) {
|
||||||
backendSrv.get('/api/dashboards/home').then(function(result) {
|
backendSrv.get('/api/dashboards/home').then(function(result) {
|
||||||
@ -22,9 +20,9 @@ function (angular) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getDashboard($routeParams.id, false).then(function(result) {
|
return backendSrv.get('/api/dashboards/db/' + $routeParams.id).then(function(result) {
|
||||||
$scope.initDashboard(result, $scope);
|
$scope.initDashboard(result, $scope);
|
||||||
}).then(null, function() {
|
}, function() {
|
||||||
$scope.initDashboard({
|
$scope.initDashboard({
|
||||||
meta: {},
|
meta: {},
|
||||||
model: { title: 'Not found' }
|
model: { title: 'Not found' }
|
||||||
|
@ -16,46 +16,22 @@ function (angular, _, config) {
|
|||||||
'grafana': 'GrafanaDatasource',
|
'grafana': 'GrafanaDatasource',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.service('datasourceSrv', function($q, $http, $injector) {
|
var plugins = {
|
||||||
|
datasources: {
|
||||||
|
'graphite': {
|
||||||
|
'serviceName': 'GraphiteDatasource',
|
||||||
|
'module': 'features/graphite/datasource'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.service('datasourceSrv', function($q, $injector, $rootScope) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.datasources = {};
|
||||||
|
|
||||||
this.init = function(dsSettingList) {
|
this.init = function(dsSettingList) {
|
||||||
config.datasources = dsSettingList;
|
config.datasources = dsSettingList;
|
||||||
|
|
||||||
this.datasources = {};
|
|
||||||
this.metricSources = [];
|
|
||||||
this.annotationSources = [];
|
|
||||||
|
|
||||||
_.each(dsSettingList, function(value, key) {
|
|
||||||
var ds = this.datasourceFactory(value);
|
|
||||||
ds.name = key;
|
|
||||||
if (value.default) {
|
|
||||||
this.default = ds;
|
|
||||||
ds.default = true;
|
|
||||||
}
|
|
||||||
this.datasources[key] = ds;
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
if (!this.default) {
|
|
||||||
this.default = this.datasources[_.keys(this.datasources)[0]];
|
|
||||||
this.default.default = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create list of different source types
|
|
||||||
_.each(this.datasources, function(value, key) {
|
|
||||||
if (value.supportMetrics) {
|
|
||||||
this.metricSources.push({
|
|
||||||
name: value.name,
|
|
||||||
value: value.default ? null : key,
|
|
||||||
default: value.default,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (value.supportAnnotations) {
|
|
||||||
this.annotationSources.push({ name: key, editorSrc: value.annotationEditorSrc });
|
|
||||||
}
|
|
||||||
if (value.grafanaDB) {
|
|
||||||
this.grafanaDB = value;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.datasourceFactory = function(ds) {
|
this.datasourceFactory = function(ds) {
|
||||||
@ -65,10 +41,35 @@ function (angular, _, config) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.get = function(name) {
|
this.get = function(name) {
|
||||||
if (!name) { return this.default; }
|
if (!name) {
|
||||||
if (this.datasources[name]) { return this.datasources[name]; }
|
return this.get(config.defaultDatasource);
|
||||||
|
}
|
||||||
|
|
||||||
return this.default;
|
if (this.datasources[name]) {
|
||||||
|
return $q.when(this.datasources[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.loadDatasource(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loadDatasource = function(name) {
|
||||||
|
var datasourceConfig = config.datasources[name];
|
||||||
|
var pluginDef = plugins.datasources[datasourceConfig.type];
|
||||||
|
|
||||||
|
if (!pluginDef) {
|
||||||
|
throw { message: "No plugin definition for data source: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
$rootScope.require([pluginDef.module], function() {
|
||||||
|
var AngularService = $injector.get(pluginDef.serviceName);
|
||||||
|
var instance = new AngularService(datasourceConfig);
|
||||||
|
self.datasources[name] = instance;
|
||||||
|
deferred.resolve(instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getAll = function() {
|
this.getAll = function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user