gnocchi: fix alarms for unpriviledged user
When an unprivileged user want to access to Gnocchi resources created by Ceilometer, that doesn't work because the filter scope the Gnocchi query to resource owner to the user. To fix we introduce a new configuration option "gnocchi_external_project_owner" set by default to "service". The new filter now allow two kind of Gnocchi resources: * owned by the user project * owned by "gnocchi_external_project_owner" and the orignal project_id of the resource is the user project. Change-Id: I0c86736a902a21520da18550aea0a7d1549bb24e
This commit is contained in:
parent
132de83ed3
commit
b0bdd43209
@ -18,6 +18,8 @@ import threading
|
||||
import cachetools
|
||||
from gnocchiclient import client
|
||||
from gnocchiclient import exceptions
|
||||
from keystoneauth1 import exceptions as ka_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
import pecan
|
||||
import wsme
|
||||
@ -28,6 +30,14 @@ from aodh.api.controllers.v2 import utils as v2_utils
|
||||
from aodh import keystone_client
|
||||
|
||||
|
||||
GNOCCHI_OPTS = [
|
||||
cfg.StrOpt('gnocchi_external_project_owner',
|
||||
default="service",
|
||||
help='Project name of resources creator in Gnocchi. '
|
||||
'(For example the Ceilometer project name'),
|
||||
]
|
||||
|
||||
|
||||
class GnocchiUnavailable(Exception):
|
||||
code = 503
|
||||
|
||||
@ -122,6 +132,20 @@ class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
||||
'resource_type'])
|
||||
return rule
|
||||
|
||||
cache = cachetools.TTLCache(maxsize=1, ttl=3600)
|
||||
lock = threading.RLock()
|
||||
|
||||
@staticmethod
|
||||
@cachetools.cached(cache, lock=lock)
|
||||
def get_external_project_owner():
|
||||
kc = keystone_client.get_client(pecan.request.cfg)
|
||||
project_name = pecan.request.cfg.api.gnocchi_external_project_owner
|
||||
try:
|
||||
project = kc.projects.find(name=project_name)
|
||||
return project.id
|
||||
except ka_exceptions.NotFound:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def validate_alarm(cls, alarm):
|
||||
super(AggregationMetricByResourcesLookupRule,
|
||||
@ -135,14 +159,27 @@ class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
|
||||
except ValueError:
|
||||
raise wsme.exc.InvalidInput('rule/query', rule.query)
|
||||
|
||||
conf = pecan.request.cfg
|
||||
|
||||
# Scope the alarm to the project id if needed
|
||||
auth_project = v2_utils.get_auth_project(alarm.project_id)
|
||||
if auth_project:
|
||||
query = {"and": [{"=": {"created_by_project_id": auth_project}},
|
||||
query]}
|
||||
|
||||
perms_filter = {"=": {"created_by_project_id": auth_project}}
|
||||
|
||||
external_project_owner = cls.get_external_project_owner()
|
||||
if external_project_owner:
|
||||
perms_filter = {"or": [
|
||||
perms_filter,
|
||||
{"and": [
|
||||
{"=": {"created_by_project_id":
|
||||
external_project_owner}},
|
||||
{"=": {"project_id": auth_project}}]}
|
||||
]}
|
||||
|
||||
query = {"and": [perms_filter, query]}
|
||||
rule.query = jsonutils.dumps(query)
|
||||
|
||||
conf = pecan.request.cfg
|
||||
gnocchi_client = client.Client(
|
||||
'1', keystone_client.get_session(conf),
|
||||
interface=conf.service_credentials.interface,
|
||||
|
@ -16,6 +16,7 @@ import itertools
|
||||
from keystoneauth1 import loading
|
||||
|
||||
import aodh.api
|
||||
import aodh.api.controllers.v2.alarm_rules.gnocchi
|
||||
import aodh.api.controllers.v2.alarms
|
||||
import aodh.coordination
|
||||
import aodh.evaluator
|
||||
@ -42,6 +43,7 @@ def list_opts():
|
||||
('api',
|
||||
itertools.chain(
|
||||
aodh.api.OPTS,
|
||||
aodh.api.controllers.v2.alarm_rules.gnocchi.GNOCCHI_OPTS,
|
||||
aodh.api.controllers.v2.alarms.ALARM_API_OPTS)),
|
||||
('coordination', aodh.coordination.OPTS),
|
||||
('database', aodh.storage.OPTS),
|
||||
|
@ -2607,7 +2607,9 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
||||
self.assertEqual(1, len(alarms))
|
||||
self._verify_alarm(json, alarms[0])
|
||||
|
||||
def test_post_gnocchi_aggregation_alarm_project_constraint(self):
|
||||
@mock.patch('aodh.keystone_client.get_client')
|
||||
def test_post_gnocchi_aggregation_alarm_project_constraint(self,
|
||||
get_client):
|
||||
json = {
|
||||
'enabled': False,
|
||||
'name': 'project_constraint',
|
||||
@ -2630,10 +2632,21 @@ class TestAlarmsRuleGnocchi(TestAlarmsBase):
|
||||
}
|
||||
}
|
||||
|
||||
expected_query = {"and": [{"=": {"created_by_project_id":
|
||||
self.auth_headers['X-Project-Id']}},
|
||||
{"=": {"server_group":
|
||||
"my_autoscaling_group"}}]}
|
||||
expected_query = {"and": [
|
||||
{"or": [
|
||||
{"=": {"created_by_project_id":
|
||||
self.auth_headers['X-Project-Id']}},
|
||||
{"and": [
|
||||
{"=": {"created_by_project_id": "<my-uuid>"}},
|
||||
{"=": {"project_id": self.auth_headers['X-Project-Id']}}
|
||||
]},
|
||||
]},
|
||||
{"=": {"server_group": "my_autoscaling_group"}},
|
||||
]}
|
||||
|
||||
ks_client = mock.Mock()
|
||||
ks_client.projects.find.return_value = mock.Mock(id='<my-uuid>')
|
||||
get_client.return_value = ks_client
|
||||
|
||||
with mock.patch('aodh.api.controllers.v2.alarm_rules.'
|
||||
'gnocchi.client') as clientlib:
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
When an unprivileged user want to access to Gnocchi resources created by
|
||||
Ceilometer, that doesn't work because the filter scope the Gnocchi query to
|
||||
resource owner to the user. To fix we introduce a new configuration option
|
||||
"gnocchi_external_project_owner" set by default to "service". The new
|
||||
filter now allow two kind of Gnocchi resources:
|
||||
* owned by the user project
|
||||
* owned by "gnocchi_external_project_owner" and the orignal project_id of
|
||||
the resource is the user project.
|
Loading…
Reference in New Issue
Block a user