From bb5178406c03d37a957b64ef59c61ecdec504fc3 Mon Sep 17 00:00:00 2001 From: Johannes Grassler Date: Thu, 19 Oct 2017 14:29:12 +0200 Subject: [PATCH] Add support for monasca-ui metrics API proxy This commit adds support for the Monasca API proxy provided by the monasca-ui Horizon plugin. This Horizon plugin combined with direct access in the Monasca data source allows using a user's Horizon session for authenticating and authorizing access to the Monasca API. Change-Id: I98bd8ff7bcc55dcd911e87037b64840035542155 Story: 2001305 Task: 5856 --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++ datasource.js | 39 ++++++++++++++++++++++----- partials/config.html | 17 +++++++----- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index df43060..504a20f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,70 @@ Team and repository tags For more information on Monasca see the [Monasca documentation](https://wiki.openstack.org/wiki/Monasca) +## Authentication Options + +### Horizon Session + +The [Monasca Horizon plugin](https://github.com/openstack/monasca-ui) offers +Horizon integration for Monasca. Among other things this plugin proxies the +Monasca metrics API, using the Horizon session for authentication (as opposed +to a Keystone token). This proxied API can be used to let this plugin access +the Monasca API with the privileges of the user logged in to Horizon. + +Note that this is entirely separate from Grafana's user management. + +Setting this up requires the following steps: + +1. Install and configure the `monasca-ui` Horizon plugin. Specifically you will + need to set `GRAFANA_URL` to `/grafana` and point `GRAFANA_LINKS` to your + dashboards which can either be JSON dashboards you point to or in-database + dashboards. In the former case set the links' `raw` attribute to `True` and + their `path` attribute to the dashboard's path or full URL. In the + latter case, set the links' `raw` attribute to `False` (or omit it entirely) + and set their `path` attributes to the database dashboards' names. + +2. Enable `mod_proxy` and `mod_proxy_http` in Apache: + + ``` + a2enmod proxy proxy_http + ``` + +3. Configure the VHost hosting your Horizon instance with a proxy path that + points at your Grafana instance (the example assumes you are running Horizon + on Apache - adapt as required for other web servers): + + ``` + ProxyPass "/grafana" "http://my.grafana.server:3000" + ProxyPassReverse "/grafana" "http://my.grafana.server:3000" + + ``` + +4. Configure Grafana's `[server/root_url]` setting to point at your dashboard + node's `/grafana` path: + + ``` + [server] + root_url = %(protocol)s://%(domain)s/grafana + ``` + +5. Configure the plugin as follows: + + * Http settings: + * Url: `http://my.dashboard.server/monitoring/proxy` (substitute your + dashboard's actual host name for `my.dashboard.server` here) + * Access: direct + * Authentication + * Auth: Horizon + +Steps (2) and (3) are neccessary to ensure both Grafana and Horizon are on the +same Host/Port from the browser's perspective. Otherwise the browser's XSS +protection mechanisms will omit the Horizon session cookie from any requests +triggered by the `monasca-grafana-datasource` plugin. + +### Keystone Authentication + When combined with Grafana Keystone authentication this datasource supports using login credentials to authenticate queries. +### Keystone Token + Without the Grafana Keystone auth, this datasource can be used by inserting a keystone token into the datasource. To get a keystone token download the python-openstackclient, source credentials and run `openstack token issue`. diff --git a/datasource.js b/datasource.js index 7122541..62bd62e 100644 --- a/datasource.js +++ b/datasource.js @@ -14,14 +14,34 @@ function (angular, _, moment, sdk, dateMath, kbn) { function MonascaDatasource(instanceSettings, $q, backendSrv, templateSrv) { this.url = instanceSettings.url; + this.url_version = '/v2.0' this.name = instanceSettings.name; if (instanceSettings.jsonData) { this.token = instanceSettings.jsonData.token; - this.keystoneAuth = instanceSettings.jsonData.keystoneAuth; + switch ( instanceSettings.jsonData.authMode ) { + case "Keystone": + this.keystoneAuth = true; + this.useHorizonProxy = false; + break; + case "Horizon": + this.keystoneAuth = false; + this.useHorizonProxy = true; + this.token = null; + break; + case "Token": + this.keystoneAuth = false; + this.useHorizonProxy = false; + break; + } } else { this.token = null; this.keystoneAuth = null; + this.useHorizonProxy = false; + } + + if ( this.useHorizonProxy == true ) { + this.url_version = ''; } this.q = $q; @@ -75,31 +95,36 @@ function (angular, _, moment, sdk, dateMath, kbn) { }; MonascaDatasource.prototype.metricsQuery = function(params) { - return this._limitedMonascaRequest('/v2.0/metrics', params, true).catch(function(err) {throw err;}); + var url = this.url_version + '/metrics'; + return this._limitedMonascaRequest(url, params, true).catch(function(err) {throw err;}); }; MonascaDatasource.prototype.namesQuery = function() { var datasource = this; - return this._limitedMonascaRequest('/v2.0/metrics/names', {}, false).then(function(data) { + var url = this.url_version + '/metrics/names'; + return this._limitedMonascaRequest(url, {}, false).then(function(data) { return datasource.convertDataList(data, 'name'); }).catch(function(err) {throw err;}); }; MonascaDatasource.prototype.dimensionNamesQuery = function(params) { var datasource = this; - return this._limitedMonascaRequest('/v2.0/metrics/dimensions/names', params, false).then(function(data) { + var url = this.url_version + '/metrics/dimensions/names'; + return this._limitedMonascaRequest(url, params, false).then(function(data) { return datasource.convertDataList(data, 'dimension_name'); }).catch(function(err) {throw err;}); }; MonascaDatasource.prototype.dimensionValuesQuery = function(params) { var datasource = this; - return this._limitedMonascaRequest('/v2.0/metrics/dimensions/names/values', params, false).then(function(data) { + var url = this.url_version + '/metrics/dimensions/names/values'; + return this._limitedMonascaRequest(url, params, false).then(function(data) { return datasource.convertDataList(data, 'dimension_value'); }).catch(function(err) {throw err;}); }; MonascaDatasource.prototype.convertDataList = function(data, key) { + if ( JSON.stringify(data.data) === '{}' ) { return {}; } var values = data.data.elements.map(function(element) { return element[key]; }); @@ -149,10 +174,10 @@ function (angular, _, moment, sdk, dateMath, kbn) { if (options.aggregator && options.aggregator != 'none') { params.statistics = options.aggregator; params.period = options.period; - path = '/v2.0/metrics/statistics?'; + path = this.url_version + '/metrics/statistics?'; } else { - path = '/v2.0/metrics/measurements?'; + path = this.url_version + '/metrics/measurements?'; } path += Object.keys(params).map(function(key) { return key + '=' + params[key]; diff --git a/partials/config.html b/partials/config.html index be0fd0d..e4ec812 100644 --- a/partials/config.html +++ b/partials/config.html @@ -33,22 +33,27 @@
-

Monasca details

+

Authentication

-
+
Token - A token is required to autenticate if Keystone auth is not set. + A token is required to autenticate if neither Keystone auth nor Horizon auth is set.
Auth - - +
+ + + Keystone = Authenticate against Keystone (requires Keystone support in Grafana)
+ Horizon = Use Horizon session for authentication (requires direct access and monasca-ui Horizon plugin)
+ Token = Use static Keystone token for authentication +
+