Плагин Grafana's DataSource Плагин Практика II

Grafana внешний интерфейс JavaScript Angular.js

Практика разработки плагина источника данных Grafana 1Основы, которые необходимо знать для разработки источника данных, описаны в . В этой статье будут представлены конкретные методы разработки в проекте.

Модуль источника данных

Для взаимодействия с остальной частью Grafana файл плагина должен экспортировать следующие 5 модулей:

Datasource  // Required
QueryCtrl  // Required
ConfigCtrl  // Required
QueryOptionsCtrl  // 
AnnotationsQueryCtrl  //

функция-конструктор

Плагин источника данных взаимодействует с источником данных и преобразует данные во временные ряды. Источник данных выполняет следующие функции:

query(options)  // 用于panel查询数据
testDatasource()  // 用于自定义数据源的页面,测试当前配置的数据源是可用的
annotationQuery(options) // 用于dashboard获取注释信息
metricFindQuery(options) // used by query editor to get metric suggestions.

Параметры, передаваемые функцией-конструктором:

constructor(instanceSettings, $q, backendSrv, templateSrv) {}
// instanceSettings对象为:
{
  id: 5,
  jsonData: {
    keepCookies: [],
    tlsAuth: false,
    tlsAuthWithCACert: false,
    tlsSkipVerify: false,
  },
  meta: {
    alerting: false,
    annotations: true,
    baseUrl: "public/plugins/grafana-server-datasource",
    dependencies: {
      grafanaVersion: "3.x.x",
      plugins: [],
    },
    id: "grafana-server-datasource",
    includes: null,
    info: {
      author: {
        name:"liuchunhui",
        url:"https://grafana.com",
      },
      description: "代理服务端作为数据源",
      links: [
        {name: "Github", url: ""},
        {name: "MIT License", url: ""}
      ],
      logos: {
        large:"public/plugins/grafana-server-datasource/img/server-logo.png",
        small:"public/plugins/grafana-server-datasource/img/server-logo.png"
      },
     screenshots:null
     updated:"2018-04-23"
     version:"1.0.0"
    },
    metrics: true,
   module: "plugins/grafana-server-datasource/module",
   name: "代理服务端",
   routes: null,
   type: "datasource",
  }
  name:"代理服务端数据源",
  type:"grafana-server-datasource",
  url:"/api/datasources/proxy/5",
}
// $q 是个函数
// backendSrv对象为:
{
  $http: ƒ p(e),
  $q: ƒ M(t),
  $timeout: ƒ o(o,s,u),
  HTTP_REQUEST_CANCELLED: -1,
  alertSrv: {
   $rootScope: object,
   $timeout: ƒ o(o,s,u)
   list: []
  }
  contextSrv: {
    isEditor: true,
    isGrafanaAdmin: true,
    isSignedIn: true,
    sidemenu: true,
    sidemenuSmallBreakpoint: false,
   user: {  // 当前登录的用户信息
      email:"admin@localhost",
     gravatarUrl:"/avatar/46d229b033af06a191ff2267bca9ae56",
     helpFlags1:0,
     id:1,
     isGrafanaAdmin:true,
     isSignedIn:true,
     lightTheme:true,
     locale:"zh-CN",
     login:"admin",
     name:"admin",
     orgCount:1,
     orgId:1,
     orgName:"Main Org.",
     orgRole:"Admin",
     timezone:"browser",
    },
    version:"5.0.1",
  },
  inFlightRequests:{},
  noBackendCache:true,
}
// templateSrv对象为:
{
  builtIns: {
    __interval:{text: "1s", value: "1s"},
    __interval_ms:{text: "100", value: "100"}
  },
  grafanaVariables: {},
  index:{},
  regex:/\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?\]\]|\${(\w+)(?::(\w+))?}/g
}

функция запроса

Функция, которая будет вызываться при фактическом запросе данных. Официальные источники данных имеют два разных результата,time seriesа такжеtable, в настоящее время все официальные источники данных и панели Grafana поддерживаютtime seriesФормат,tableФормат поддерживается только источником данных InfluxDB и панелями таблиц. Мы можем настроить значение типа при разработке подключаемого модуля, но чтобы разработанный подключаемый модуль источника данных также адаптировался к официальному подключаемому модулю панели, затем определите формат данных, возвращаемый источником данных, чтобы он соответствовал формату grafana. источник данных.запросtime seriesВведите формат ответа:

[{
   "target":"upper_75",
    "datapoints":[
      [622, 1450754160000],
      [365, 1450754220000]
   ]
}, {
    "target":"upper_90",
    "datapoints":[
      [861, 1450754160000],
      [767, 1450754220000]
    ]
}]

источник данных.запросtableВведите формат ответа:

[{
   "columns": [{
      "text": "Time",
        "type": "time",
        "sort": true,
        "desc": true,
   }, {
        "text": "mean",
    }, {
        "text": "sum",
    }],
    "rows": [[
      1457425380000,
        null,
        null
   ], [
      1457425370000,
        1002.76215352,
        1002.76215352
   ]],
    "type": "table"
}]

Перейти кdatasource.queryОбъект запроса функции:

{
   "range": {
      "from": moment,  // 全局时间筛选起始日期
      "raw": {from: "now-7d", to: "now"},
      "to": moment,  // 全局时间筛选结束日期
   },
    "rangeRaw": {
      "from": "now-7d",
      "to": "now",
    },
   "interval": "15m",
    "intervalMs": 900000,
   "targets": [{  // 定义的查询条件们
      "refId": "A",
      "target": "upper_75" 
   }, {
      "refId": "B", 
      "target": "upper_90" 
   }],
   "format": "json",
   "maxDataPoints": 2495, //decided by the panel
    "cacheTimeout": undefined,
    "panelId": 2,
    "timezone": "browser"
}

Пример функции запроса:

query(options) {  // 返回的结果数据,panel插件会通过监听'data-received'获取到
    
    const params = {  // 封装http请求参数
        from: options.range.from.format('x'),
        to: options.range.to.format('x'),
        targets: options.queries,
    };

    return this.doRequest({  // 发起http请求并返回结果
        url: this.url + '/card-data',  // 要制定公共接口规范
        method: 'POST',
        data: params
    });
}


doRequest(options) {
    options.withCredentials = this.withCredentials;
    options.headers = this.headers;
    return this.backendSrv.datasourceRequest(options);  // datasourceRequest()是grafana提供的请求datasource函数
}

Функция testDatasource

Когда пользователь нажимает при добавлении нового источника данныхSave&Testкнопку, данные сначала сохраняются в базе данных, а затемtestDatasourceВызовите функцию, определенную в подключаемом модуле источника данных. Эта функция отправляет запрос к источнику данных, проверяет правильность настройки и доступности источника данных и гарантирует, что когда пользователь пишет запрос на новой информационной панели, источник данных правильно настроен. Пример функции:

/**
 * 当保存并测试数据源时会调用该函数
 * 测试数据源是否正常工
 * return { status: "", message: "", title: "" }
 */
testDatasource() {
    return this.doRequest({
        url: this.url + '/',
        method: 'GET',
    }).then(response => {
        if (response.status === 200) {
            return { status: "success", message: "Data source is working", title: "Success" };
        }
    });
}  

функция annotationQuery

Запрос аннотации, объект запроса, переданный в функцию datasource.annotationQuery:

{
   "range": { 
      "from": "2016-03-04T04:07:55.144Z", 
      "to": "2016-03-04T07:07:55.144Z" },
   "rangeRaw": { 
      "from": "now-3h", 
      "to": "now" 
   },
   "annotation": {
      "datasource": "generic datasource",
      "enable": true,
      "name": "annotation name"
   }
}

Ожидаемый результат datasource.annotationQuery:

[{
   "annotation": {
      "name": "annotation name", //should match the annotation name in grafana
       "enabled": true,
       "datasource": "generic datasource",
    },
    "title": "Cluster outage",
    "time": 1457075272576,
    "text": "Joe causes brain split",
    "tags": "joe, cluster, failure"
}]

Может использовать базу данных платформы grafana для доступа к «аннотациям».

функция metricFindQuery

Configttrl модуль

Когда пользователь редактирует или создает новый источник данных этого типа, этот класс будет создан и обработан как контроллер Angular. Для этого класса требуется статический шаблон или переменная templateUrl, которая будет отображаться в представлении этого контроллера. Содержимое этого объекта в классе ConfigCtrl:

{
  current: {
    name: "代理服务端数据源",  // 数据源名称
    isDefault: true,  // 是否是默认
    type: "grafana-server-datasource",  // 数据源插件的id值
    url: "http://localhost:3001",  // HTTP:数据源对应的url
    access: "proxy",  // HTTP:连接数据源类型,有'direct'和'proxy'两种类型可选
    basicAuth: true,  // Auth:Basic Auth选项
    basicAuthUser: "basic auth user",  // Basic Auth Details:User选项,当basicAuth为true时有效
    basicAuthPassword:"basic auth password",  // Basic Auth Details:Password选项,当basicAuth为true时有效
    withCredentials: true,  // Auth:With Credentials选项
    jsonData: {
     tlsAuth: true,  // Auth:TLS Client Auth选项
      tlsAuthWithCACert: true,  // Auth: With CA Cert选项
      tlsSkipVerify: true,  // Auth: Skip TLS Verification (Insecure)选项值
      keepCookies: ["Advanced Cookoe"],  // Advanced HTTP Settings: cookie的白名单
    },
    secureJsonData: {
      tlsCACert: "TLS Auth CA Cert",  // TLS Auth Details:CA Cert选项,当jsonData下的tlsAuthWithCACert值为true时有效
      tlsClientCert: "TLS Auth Client Cert",  // TLS Auth Details:Client Cert选项,当jsonData下的tlsAuth值为true时有效
      tlsClientKey: "TLS Auth Client Key",  // TLS Auth Details:Client Key选项,当jsonData下的tlsAuth值为true时有效
    },
    secureJsonFields: {},
  },
  meta: {
    baseUrl: "public/plugins/grafana-server-datasource",
    defaultNavUrl: "",
    dependencies: {
      grafanaVersion: "3.x.x",
      plugins: [],
    },
    enabled: false,
    hasUpdate: false,
    id: "grafana-server-datasource",
    includes: null,
    info: {  // plugin.json中配置的信息
     author: {
        name: "liuchunhui", 
        url: "https://grafana.com"
      },
      description: "代理服务端作为数据源",
      links: [
        {name: "Github", url: ""},
        {name: "MIT License", url: ""}
      ],
      logos: {
      large:"public/plugins/grafana-server-datasource/img/server-logo.png",
        small:"public/plugins/grafana-server-datasource/img/server-logo.png"
      },
      screenshots:null,
      updated:"2018-04-23",
      version:"1.0.0"
    },
    jsonData: null,
    latestVersion: "",
    module: "plugins/grafana-server-datasource/module",
    name: "代理服务端",
    pinned: false
    state: "",
    type: "datasource",
}

Компонент angular, инкапсулированный grafana на странице шаблона, передается в этомcurrentОбъект, реализующий определения модулей HTTP и Auth:

<datasource-http-settings current="ctrl.current"></datasource-http-settings>

Модуль QueryCtrl

Класс JavaScript, который будет создан и обработан как контроллер Angular, когда пользователь редактирует индикатор на панели. Урок должен начинаться сapp/plugins/sdk.QueryCtrlНаследование класса. Для этого класса требуется статический шаблон или переменная templateUrl, которая будет отображаться в представлении этого контроллера. Когда пользователь находится под панелью, переключитесь обратно наMetricsмодуль, класс инициализируется. Таким образом, мы можем сделать что-то свое в конструкторе. Например, список измерений индикатора можно получить как условие отображения для пользовательской фильтрации.

import { QueryCtrl } from 'app/plugins/sdk';
export default class GenericQueryCtrl extends QueryCtrl {
    constructor($scope, $injector) {
        super($scope, $injector);

        // 获取参数列表请求
        this.requestParams().then(response => {
            const targets = response.data.target;
            this.options = response.data.options;
            this.text = response.data.text;
            this.keys = Object.keys(targets);

            for (let key in targets) {
                this.target[key] = this.target[key] || targets[key];

            }
        });
    }
    requestParams() {  // 请求获取参数列表
        const params = {
            header: {
                'Content-Type': 'application/json'
            },
            method: 'GET',
            retry: 0,
            url: this.datasource.url + '/param-list'
        };
        return this.datasource.backendSrv.datasourceRequest(params);  // 使用grafana提供的http请求函数
    }
    onChangeInternal() {  // 刷新面板
        this.panelCtrl.refresh(); // grafana自带方法使面板更新数据
    }

    toggleEditorMode() {  // 是否开启编辑模式
        this.target.rawQuery = !this.target.rawQuery;
    }
}

GenericQueryCtrl.templateUrl = './page/query.html';

Шаблон query.html для контроллера QueryCtrl:

<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
    <div class="gf-form"
         ng-if="!ctrl.target.rawQuery"
         ng-repeat="key in ctrl.keys">
        <span class="gf-form-label width-7">
            {{ctrl.text[key]}}
        </span>
        <select class="gf-form-input width-25"
                ng-model="ctrl.target[key]"
                ng-change="ctrl.onChangeInternal()">
            <option ng-repeat="option in ctrl.options[key]"
                    value="{{option.name}}">
                {{option.desc}}
            </option>
        </select>
    </div>
    <div ng-if="ctrl.target.rawQuery">
        <textarea class="gf-form-input" rows="5" spellcheck="false" ng-blur="ctrl.onChangeInternal()" />
    </div>
</query-editor-row>

<query-editor-row query-ctrl="ctrl">Содержимое тега будет добавлено вAdd QueryВ шаблоне можно включить атрибут has-text-edit-mode="true" в тегеToggle Edit ModeФункция. Параметр target.rawQuery в контроллере QueryCtrl отмечает переключение между двумя режимами редактирования, но эти два режима необходимо определить в коде.

Модуль AnnotationsQueryCtrl

Класс JavaScript, который будет создан и обработан как контроллер Angular, когда пользователь выберет этот тип источника данных в меню шаблона источника данных. Для этого класса требуется статический шаблон или переменная templateUrl, которая будет отображаться в представлении этого контроллера. Поля, привязанные к этому контроллеру, затем отправляются в функцию annotationQuery объекта базы данных. Возможность настройки дашборда при разработке плагиновBuilt in queryусловия о. Код AnnotationQueryCtrl:

export default class GenericAnnotationsQueryCtrl {}
GenericAnnotationsQueryCtrl.templateUrl = './page/annotationsQuery.html';

код annotationsQuery.html:

<h5 class="section-heading">注解查询条件设定</h5>
<div class="gf-form-group">
    <div class="gf-form">
        <input type="text" class="gf-form-input" ng-model='ctrl.annotation.query' placeholder="" />
    </div>
</div>

Модуль QueryOptionsCtrl

Класс JavaScript, который будет создан и обработан как контроллер Angular, когда пользователь редактирует индикатор на панели. Этот контроллер отвечает за обработку общепанельных настроек источника данных, таких как интервал, скорость и агрегация (при необходимости). Для этого класса требуется статический шаблон или переменная templateUrl, которая будет отображаться в представлении этого контроллера.

Код QueryOptionsCtrl:

export default class GenericQueryOptionsCtrl {}
GenericQueryOptionsCtrl.templateUrl = './page/queryOptions.html';

код queryOptions.html:

<section class="grafana-metric-options" >
    <div class="gf-form"></div>
</section>