Add monasca api read-only user role

This will allow dashboard/operator users to see but not add data.

Change-Id: I16a2329356cc9e7d03d2f3e1394127006d6e487e
Implements: blueprint read-only-api-user
This commit is contained in:
Brad Klein 2016-07-15 13:50:22 -06:00
parent 9d64d3d124
commit 5f37594fe7
17 changed files with 268 additions and 30 deletions

View File

@ -14,6 +14,7 @@ Derrick Johnson <johnson.derrick@gmail.com>
Dexter Fryar <dexter.fryar@outlook.com>
Ghanshyam <ghanshyam.mann@nectechnologies.in>
Haiwei Xu <xu-haiwei@mxw.nes.nec.co.jp>
Igor Natanael <igornsa@lsd.ufcg.edu.br>
Janonymous <janonymous.codevulture@gmail.com>
Jeremy Stanley <fungi@yuggoth.org>
Joe Keen <joe.keen@hp.com>
@ -35,6 +36,7 @@ Ryan Brandt <ryan.brandt@hp.com>
SamKirsch10 <sam.kirsch@hp.com>
Shinya Kawabata <s-kawabata@wx.jp.nec.com>
Srinivas Sakhamuri <srini.openstack@gmail.com>
Swapnil Kulkarni (coolsvap) <me@coolsvap.net>
Thomas Graichen <thomas.graichen@sap.com>
Tim Kuhlman <tim.kuhlman@hp.com>
Tomasz Trębski <tomasz.trebski@ts.fujitsu.com>

View File

@ -42,6 +42,7 @@ For secure operation of the Monasca API, the API must be configured to use Keyst
* keystore - The keystore holding the SSL Client certificate if connSSLClientAuth is true
* keystorePassword - The password for the keystore
* defaultAuthorizedRoles - An array of roles that authorize a user to access the complete Monasca API. User must have at least one of these roles. See below
* readOnlyAuthorizedRoles - An array of roles that authorize a user to only GET (but not POST, PUT...) metrics. See Keystone Roles below
* agentAuthorizedRoles - An array of roles that authorize only the posting of metrics. See Keystone Roles below
* adminAuthMethod - "password" if the Monasca API should adminUser and adminPassword to login to the Keystone server to check the user's token, "token" if the Monasca API should use adminToken
* adminUser - Admin user name
@ -61,6 +62,8 @@ The reason for the "Agent access" level is because the Monasca Agent must be con
To configure the user to have full access, the user must have a role that is listed in defaultAuthorizedRoles. To configure a user to have only "Agent access", the user must have a role in agentAuthorizedRoles and none of the roles in defaultAuthorizedRoles.
If you want to give users the ability to only view data, configure one or more roles in the readOnlyAuthorizedRoles list.
## Design Overview
### Architectural layers

View File

@ -165,7 +165,11 @@ def main(argv):
{'username': 'demo',
'project': 'demo',
'password': 'secretadmin',
'role': 'monasca-user'}
'role': 'monasca-user'},
{'username': 'monasca-read-only-user',
'project': 'mini-mon',
'password': 'password',
'role': 'monasca-read-only-user'}
]
service_host = argv[0]

View File

@ -105,6 +105,7 @@ middleware:
connRetryTimes: 2
connRetryInterval: 50
defaultAuthorizedRoles: [user, domainuser, domainadmin, monasca-user, admin]
readOnlyAuthorizedRoles: [monasca-read-only-user]
agentAuthorizedRoles: [monasca-agent]
adminAuthMethod: password
adminUser: "admin"

View File

@ -31,6 +31,9 @@ default_authorized_roles = user, domainuser, domainadmin, monasca-user
# The roles that are allowed to only POST metrics to the API. This role would be used by the Monasca Agent.
agent_authorized_roles = monasca-agent
# The roles that are allowed to only GET metrics from the API.
read_only_authorized_roles = monasca-read-only-user
# The roles that are allowed to access the API on behalf of another tenant.
# For example, a service can POST metrics to another tenant if they are a member of the "delegate" role.
delegate_authorized_roles = admin

View File

@ -31,6 +31,9 @@ default_authorized_roles = user, domainuser, domainadmin, monasca-user
# The roles that are allowed to only POST metrics to the API. This role would be used by the Monasca Agent.
agent_authorized_roles = monasca-agent
# The roles that are allowed to only GET metrics from the API.
read_only_authorized_roles = monasca-read-only-user
# The roles that are allowed to access the API on behalf of another tenant.
# For example, a service can POST metrics to another tenant if they are a member of the "delegate" role.
delegate_authorized_roles = admin

View File

@ -82,6 +82,7 @@ middleware:
connRetryTimes: 2
connRetryInterval: 50
defaultAuthorizedRoles: [user, domainuser, domainadmin, monasca-user]
readOnlyAuthorizedRoles: [monasca-read-only-user]
agentAuthorizedRoles: [monasca-agent]
adminAuthMethod: password
adminUser: admin

View File

@ -212,7 +212,8 @@ public class MonApiApplication extends Application<ApiConfig> {
environment.servlets().addFilter(
"post-auth",
new PostAuthenticationFilter(config.middleware.defaultAuthorizedRoles,
config.middleware.agentAuthorizedRoles));
config.middleware.agentAuthorizedRoles,
config.middleware.readOnlyAuthorizedRoles));
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");

View File

@ -47,6 +47,8 @@ public class MiddlewareConfiguration {
@JsonProperty
public List<String> defaultAuthorizedRoles;
@JsonProperty
public List<String> readOnlyAuthorizedRoles;
@JsonProperty
public List<String> agentAuthorizedRoles;
@JsonProperty
public String delegateAuthorizedRole;

View File

@ -49,15 +49,27 @@ public class PostAuthenticationFilter implements Filter {
private final List<String> defaultAuthorizedRoles = new ArrayList<String>();
private final List<String> agentAuthorizedRoles = new ArrayList<String>();
private final List<String> readOnlyAuthorizedRoles = new ArrayList<String>();
public PostAuthenticationFilter(List<String> defaultAuthorizedRoles,
List<String> agentAuthorizedRoles) {
List<String> agentAuthorizedRoles,
List<String> readOnlyAuthorizedRoles) {
for (String defaultRole : defaultAuthorizedRoles) {
this.defaultAuthorizedRoles.add(defaultRole.toLowerCase());
}
for (String agentRole : agentAuthorizedRoles) {
this.agentAuthorizedRoles.add(agentRole.toLowerCase());
}
//
// Check for null here so we can support backward compatibility
// of not setting readOnlyAuthorizedRoles in the config file.
//
if (null != readOnlyAuthorizedRoles) {
for (String readOnlyRole : readOnlyAuthorizedRoles) {
this.readOnlyAuthorizedRoles.add(readOnlyRole.toLowerCase());
}
}
}
@Override
@ -126,6 +138,8 @@ public class PostAuthenticationFilter implements Filter {
return false;
boolean agentUser = false;
boolean readOnlyUser = false;
for (String role : rolesFromKeystone.toString().split(",")) {
String lowerCaseRole = role.toLowerCase();
if ((defaultAuthorizedRoles != null) && defaultAuthorizedRoles.contains(lowerCaseRole)) {
@ -134,11 +148,20 @@ public class PostAuthenticationFilter implements Filter {
if ((agentAuthorizedRoles != null) && agentAuthorizedRoles.contains(lowerCaseRole)) {
agentUser = true;
}
if ((readOnlyAuthorizedRoles != null) && readOnlyAuthorizedRoles.contains(lowerCaseRole)) {
readOnlyUser = true;
}
}
if (agentUser) {
request.setAttribute(X_MONASCA_AGENT, true);
return true;
}
if (readOnlyUser && request.getMethod().equals("GET")) {
return true;
}
return false;
}

View File

@ -77,6 +77,7 @@ middleware:
connRetryTimes: 2
connRetryInterval: 50
defaultAuthorizedRoles: [user, domainuser, domainadmin,heat_stack_owner,_member_]
readOnlyAuthorizedRoles: [monasca-read-only-user]
agentAuthorizedRoles: [monasca-agent]
adminAuthMethod: password
adminUser: admin

View File

@ -42,6 +42,9 @@ security_opts = [cfg.ListOpt('default_authorized_roles', default=['admin'],
cfg.ListOpt('agent_authorized_roles', default=['agent'],
help='Roles that are only allowed to POST to '
'the API'),
cfg.ListOpt('read_only_authorized_roles', default=['monasca-read-only-user'],
help='Roles that are only allowed to GET from '
'the API'),
cfg.ListOpt('delegate_authorized_roles', default=['admin'],
help='Roles that are allowed to POST metrics on '
'behalf of another tenant')]

View File

@ -43,6 +43,9 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_alarmdefs_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._alarm_definitions_repo = simport.load(
cfg.CONF.repositories.alarm_definitions_driver)()
@ -81,7 +84,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
def on_get(self, req, res, alarm_definition_id=None):
if alarm_definition_id is None:
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarmdefs_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
name = helpers.get_query_name(req)
dimensions = helpers.get_query_dimensions(req)
@ -114,7 +117,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
res.status = falcon.HTTP_200
else:
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarmdefs_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
result = self._alarm_definition_show(tenant_id,

View File

@ -39,6 +39,9 @@ class Alarms(alarms_api_v2.AlarmsV2API,
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_alarms_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._alarms_repo = simport.load(
cfg.CONF.repositories.alarms_driver)()
@ -112,7 +115,7 @@ class Alarms(alarms_api_v2.AlarmsV2API,
res.status = falcon.HTTP_204
def on_get(self, req, res, alarm_id=None):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
if alarm_id is None:
@ -384,8 +387,9 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
try:
super(AlarmsCount, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_alarms_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._alarms_repo = simport.load(
cfg.CONF.repositories.alarms_driver)()
@ -394,7 +398,7 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
raise exceptions.RepositoryException(ex)
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
query_parms = falcon.uri.parse_query_string(req.query_string)
@ -487,8 +491,9 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
try:
super(AlarmsStateHistory, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_alarms_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._alarms_repo = simport.load(
cfg.CONF.repositories.alarms_driver)()
self._metrics_repo = simport.load(
@ -501,7 +506,7 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
def on_get(self, req, res, alarm_id=None):
if alarm_id is None:
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
start_timestamp = helpers.get_query_starttime_timestamp(req, False)
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
@ -517,7 +522,7 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
res.status = falcon.HTTP_200
else:
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
offset = helpers.get_query_param(req, 'offset')
limit = helpers.get_limit(req)

View File

@ -52,10 +52,11 @@ class Metrics(metrics_api_v2.MetricsV2API):
try:
super(Metrics, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._delegate_authorized_roles = (
cfg.CONF.security.delegate_authorized_roles)
self._get_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._post_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.agent_authorized_roles)
@ -129,7 +130,7 @@ class Metrics(metrics_api_v2.MetricsV2API):
res.status = falcon.HTTP_204
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
name = helpers.get_query_name(req)
helpers.validate_query_name(name)
@ -152,10 +153,11 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
try:
super(MetricsMeasurements, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._delegate_authorized_roles = (
cfg.CONF.security.delegate_authorized_roles)
self._get_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._post_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.agent_authorized_roles)
@ -168,7 +170,7 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
ex.message)
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
name = helpers.get_query_name(req, True)
helpers.validate_query_name(name)
@ -212,8 +214,9 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
try:
super(MetricsStatistics, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._metrics_repo = simport.load(
cfg.CONF.repositories.metrics_driver)()
@ -223,7 +226,7 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
ex.message)
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
name = helpers.get_query_name(req, True)
helpers.validate_query_name(name)
@ -270,8 +273,9 @@ class MetricsNames(metrics_api_v2.MetricsNamesV2API):
try:
super(MetricsNames, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._metrics_repo = simport.load(
cfg.CONF.repositories.metrics_driver)()
@ -281,7 +285,7 @@ class MetricsNames(metrics_api_v2.MetricsNamesV2API):
ex.message)
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
dimensions = helpers.get_query_dimensions(req)
helpers.validate_query_dimensions(dimensions)
@ -309,8 +313,9 @@ class DimensionValues(metrics_api_v2.DimensionValuesV2API):
try:
super(DimensionValues, self).__init__()
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_metrics_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._metrics_repo = simport.load(
cfg.CONF.repositories.metrics_driver)()
@ -320,7 +325,7 @@ class DimensionValues(metrics_api_v2.DimensionValuesV2API):
ex.message)
def on_get(self, req, res):
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
metric_name = helpers.get_query_param(req, 'metric_name')
dimension_name = helpers.get_query_param(req, 'dimension_name', required=True)

View File

@ -37,6 +37,9 @@ class Notifications(notifications_api_v2.NotificationsV2API):
self._region = cfg.CONF.region
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._get_notifications_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.read_only_authorized_roles)
self._notifications_repo = simport.load(
cfg.CONF.repositories.notifications_driver)()
self._notification_method_type_repo = simport.load(
@ -206,7 +209,8 @@ class Notifications(notifications_api_v2.NotificationsV2API):
def on_get(self, req, res, notification_method_id=None):
if notification_method_id is None:
helpers.validate_authorization(req, self._default_authorized_roles)
helpers.validate_authorization(req,
self._get_notifications_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
sort_by = helpers.get_query_param(req, 'sort_by', default_val=None)
if sort_by is not None:
@ -226,7 +230,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
res.status = falcon.HTTP_200
else:
helpers.validate_authorization(req,
self._default_authorized_roles)
self._get_notifications_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
result = self._list_notification(tenant_id,
notification_method_id,

View File

@ -0,0 +1,174 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import six.moves.urllib.parse as urlparse
from monasca_tempest_tests.tests.api import base
from monasca_tempest_tests.tests.api import helpers
from tempest import test
from tempest.lib import exceptions
from monasca_tempest_tests import clients
class TestReadOnlyRole(base.BaseMonascaTest):
@classmethod
def resource_setup(cls):
super(TestReadOnlyRole, cls).resource_setup()
credentials = cls.cred_provider.get_creds_by_roles(
['monasca-read-only-user']).credentials
cls.os = clients.Manager(credentials=credentials)
cls.monasca_client = cls.os.monasca_client
@classmethod
def resource_cleanup(cls):
super(TestReadOnlyRole, cls).resource_cleanup()
@test.attr(type="gate")
def test_list_alarms_success(self):
resp, response_body = self.monasca_client.list_alarms()
#
# Validate the call succeeds with empty result (we didn't
# create any alarms)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms'))
@test.attr(type="gate")
def test_list_metrics_success(self):
resp, response_body = self.monasca_client.list_metrics()
#
# Validate the call succeeds with empty result (we didn't
# create any metrics)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/metrics'))
@test.attr(type="gate")
def test_list_alarm_definition_success(self):
resp, response_body = self.monasca_client.list_alarm_definitions()
#
# Validate the call succeeds with empty result (we didn't
# create any alarm definitions)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarm-definitions'))
@test.attr(type="gate")
def test_list_notification_methods_success(self):
resp, response_body = self.monasca_client.list_notification_methods()
#
# Validate the call succeeds with empty result (we didn't
# create any notifications)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/notification-methods'))
@test.attr(type="gate")
def test_list_alarm_count_success(self):
resp, response_body = self.monasca_client.count_alarms()
#
# Validate the call succeeds with empty result (we didn't
# create any alarms to count)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, response_body['counts'][0][0])
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms/count'))
@test.attr(type="gate")
def test_list_alarm_state_history_success(self):
resp, response_body = self.monasca_client.list_alarms_state_history()
#
# Validate the call succeeds with empty result (we didn't
# create any alarms that have history)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms/state-history'))
@test.attr(type="gate")
def test_list_dimension_values_success(self):
parms = '?dimension_name=foo'
resp, response_body = self.monasca_client.list_dimension_values(parms)
#
# Validate the call succeeds with empty result (we didn't
# create any metrics/dimensions)
#
url = '/v2.0/metrics/dimensions/names/values?dimension_name=foo'
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements'][0]['values']))
self.assertTrue(response_body['links'][0]['href'].endswith(url))
@test.attr(type="gate")
def test_list_measurements_success(self):
start_timestamp = int(time.time() * 1000)
start_time = str(helpers.timestamp_to_iso(start_timestamp))
parms = '?name=foo&start_time=' + start_time
resp, response_body = self.monasca_client.list_measurements(parms)
#
# Validate the call succeeds with empty result (we didn't
# create any metrics to get measurements for)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue('/v2.0/metrics/measurements' in response_body['links'][0]['href'])
@test.attr(type="gate")
def test_list_statistics_success(self):
start_timestamp = int(time.time() * 1000)
start_time = str(helpers.timestamp_to_iso(start_timestamp))
query_parms = '?name=foo&statistics=avg&start_time=' + start_time
resp, response_body = self.monasca_client.list_statistics(
query_parms)
#
# Validate the call succeeds with empty result (we didn't
# create any metrics to get statistics for)
#
self.assertEqual(200, resp.status)
self.assertEqual(0, len(response_body['elements']))
self.assertTrue('/v2.0/metrics/statistics' in response_body['links'][0]['href'])
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_delete_alarms_fails(self):
self.assertRaises(exceptions.Unauthorized,
self.monasca_client.delete_alarm, "foo")
@test.attr(type='gate')
@test.attr(type=['negative'])
def test_create_metric_fails(self):
self.assertRaises(exceptions.Unauthorized,
self.monasca_client.create_metrics,
None)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_create_alarm_definition_fails(self):
self.assertRaises(exceptions.Unauthorized,
self.monasca_client.create_alarm_definitions,
None)
@test.attr(type="gate")
@test.attr(type=['negative'])
def test_create_notification_fails(self):
notif = helpers.create_notification()
self.assertRaises(exceptions.Unauthorized,
self.monasca_client.create_notifications,
notif)