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
This commit is contained in:
parent
c0db06dd69
commit
bb5178406c
64
README.md
64
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)
|
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.
|
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`.
|
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`.
|
||||||
|
|
|
@ -14,14 +14,34 @@ function (angular, _, moment, sdk, dateMath, kbn) {
|
||||||
|
|
||||||
function MonascaDatasource(instanceSettings, $q, backendSrv, templateSrv) {
|
function MonascaDatasource(instanceSettings, $q, backendSrv, templateSrv) {
|
||||||
this.url = instanceSettings.url;
|
this.url = instanceSettings.url;
|
||||||
|
this.url_version = '/v2.0'
|
||||||
this.name = instanceSettings.name;
|
this.name = instanceSettings.name;
|
||||||
|
|
||||||
if (instanceSettings.jsonData) {
|
if (instanceSettings.jsonData) {
|
||||||
this.token = instanceSettings.jsonData.token;
|
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 {
|
} else {
|
||||||
this.token = null;
|
this.token = null;
|
||||||
this.keystoneAuth = null;
|
this.keystoneAuth = null;
|
||||||
|
this.useHorizonProxy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.useHorizonProxy == true ) {
|
||||||
|
this.url_version = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.q = $q;
|
this.q = $q;
|
||||||
|
@ -75,31 +95,36 @@ function (angular, _, moment, sdk, dateMath, kbn) {
|
||||||
};
|
};
|
||||||
|
|
||||||
MonascaDatasource.prototype.metricsQuery = function(params) {
|
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() {
|
MonascaDatasource.prototype.namesQuery = function() {
|
||||||
var datasource = this;
|
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');
|
return datasource.convertDataList(data, 'name');
|
||||||
}).catch(function(err) {throw err;});
|
}).catch(function(err) {throw err;});
|
||||||
};
|
};
|
||||||
|
|
||||||
MonascaDatasource.prototype.dimensionNamesQuery = function(params) {
|
MonascaDatasource.prototype.dimensionNamesQuery = function(params) {
|
||||||
var datasource = this;
|
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');
|
return datasource.convertDataList(data, 'dimension_name');
|
||||||
}).catch(function(err) {throw err;});
|
}).catch(function(err) {throw err;});
|
||||||
};
|
};
|
||||||
|
|
||||||
MonascaDatasource.prototype.dimensionValuesQuery = function(params) {
|
MonascaDatasource.prototype.dimensionValuesQuery = function(params) {
|
||||||
var datasource = this;
|
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');
|
return datasource.convertDataList(data, 'dimension_value');
|
||||||
}).catch(function(err) {throw err;});
|
}).catch(function(err) {throw err;});
|
||||||
};
|
};
|
||||||
|
|
||||||
MonascaDatasource.prototype.convertDataList = function(data, key) {
|
MonascaDatasource.prototype.convertDataList = function(data, key) {
|
||||||
|
if ( JSON.stringify(data.data) === '{}' ) { return {}; }
|
||||||
var values = data.data.elements.map(function(element) {
|
var values = data.data.elements.map(function(element) {
|
||||||
return element[key];
|
return element[key];
|
||||||
});
|
});
|
||||||
|
@ -149,10 +174,10 @@ function (angular, _, moment, sdk, dateMath, kbn) {
|
||||||
if (options.aggregator && options.aggregator != 'none') {
|
if (options.aggregator && options.aggregator != 'none') {
|
||||||
params.statistics = options.aggregator;
|
params.statistics = options.aggregator;
|
||||||
params.period = options.period;
|
params.period = options.period;
|
||||||
path = '/v2.0/metrics/statistics?';
|
path = this.url_version + '/metrics/statistics?';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
path = '/v2.0/metrics/measurements?';
|
path = this.url_version + '/metrics/measurements?';
|
||||||
}
|
}
|
||||||
path += Object.keys(params).map(function(key) {
|
path += Object.keys(params).map(function(key) {
|
||||||
return key + '=' + params[key];
|
return key + '=' + params[key];
|
||||||
|
|
|
@ -33,22 +33,27 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-group">
|
<div class="gf-form-group">
|
||||||
<h3 class="page-heading">Monasca details</h3>
|
<h3 class="page-heading">Authentication</h3>
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form max-width-30">
|
<div class="gf-form max-width-30" ng-show='ctrl.current.jsonData.authMode == "Token"'>
|
||||||
<span class="gf-form-label width-7">Token</span>
|
<span class="gf-form-label width-7">Token</span>
|
||||||
<input class="gf-form-input" type="text" ng-model='ctrl.current.jsonData.token' placeholder=""></input>
|
<input class="gf-form-input" type="text" ng-model='ctrl.current.jsonData.token' placeholder=""></input>
|
||||||
<info-popover mode="right-absolute">
|
<info-popover mode="right-absolute">
|
||||||
<span>A token is required to autenticate if Keystone auth is not set.</span>
|
<span>A token is required to autenticate if neither Keystone auth nor Horizon auth is set.</span>
|
||||||
</info-popover>
|
</info-popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form max-width-30">
|
<div class="gf-form max-width-30">
|
||||||
<span class="gf-form-label width-7">Auth</span>
|
<span class="gf-form-label width-7">Auth</span>
|
||||||
<gf-form-switch class="gf-form" label="Keystone Auth"
|
<div class="gf-form-select-wrapper gf-form-select-wrapper--has-help-icon max-width-24">
|
||||||
checked="ctrl.current.jsonData.keystoneAuth" switch-class="max-width-6">
|
<select class="gf-form-input" ng-model="ctrl.current.jsonData.authMode" ng-options="f for f in ['Keystone', 'Horizon', 'Token']"></select>
|
||||||
</gf-form-switch>
|
<info-popover mode="right-absolute">
|
||||||
|
Keystone = Authenticate against Keystone (requires Keystone support in Grafana)<br>
|
||||||
|
Horizon = Use Horizon session for authentication (requires direct access and monasca-ui Horizon plugin)<br>
|
||||||
|
Token = Use static Keystone token for authentication
|
||||||
|
</info-popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue