Files
Pedro Henrique 3961dcddb8 Adapt tests to new rating API
With the introduction of start and end dates in the rating rules, the
update of rating rules for both mappings and pyscripts was
disabled as well as the deletion. In the new versions the
rating rules can be disabled instead of deleted so it is possible
to track all changes in the rating rules.

We are accepting both results for the tests, the old behavior and
the new one. We had to do it to keep stable branches pipeline
working, otherwise old stable versions pipeline would fail for
this new validation. As soon as the support for release 2024.1
ends, we can remove the old versions validation and keep only the
new one.

Change-Id: Idf632f229e2bc2a52eb24c138477f1b70f7fe936
Depends-On: https://review.opendev.org/c/openstack/cloudkitty-tempest-plugin/+/925736
Signed-off-by: Pedro Henrique <phpm13@gmail.com>
2025-08-18 15:03:41 -03:00

480 lines
18 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2017 Objectif Libre
#
# 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.
#
from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient import client
from oslo_serialization import jsonutils as json
from tempest import clients as tempest_clients
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib.services import clients
CONF = config.CONF
class BaseRatingClient(rest_client.RestClient):
"""Base class for cloudkittyclient implementations"""
@staticmethod
def deserialize(body):
return json.loads(body.decode().replace("\n", ""))
@staticmethod
def serialize(body):
return json.dumps(body)
def _do_request(self, method, uri, body=None, expected_code=200):
resp, body = self.request(method, uri, body=body)
self.expected_success(expected_code, resp.status)
body = self.deserialize(body) if body else dict()
if not isinstance(body, dict) and not isinstance(body, list):
body = dict()
# ResponseBody inherits from dict, so lists must be converted
body = dict(body=body) if isinstance(body, list) else body
return rest_client.ResponseBody(resp, body)
class RatingClientV1(BaseRatingClient):
"""Implementation of cloudkittyclient for v1 endpoints"""
api_version = 'v1'
def get_collector_mappings(self, service=None):
uri = '/collector/mappings/'
if service:
uri += service + '/'
return self._do_request('GET', uri)
def create_collector_mapping(self, collector='gnocchi', service='compute'):
uri = '/collector/mappings/'
request_body = {
'collector': collector,
'service': service,
}
return self._do_request('POST', uri, body=self.serialize(request_body))
def delete_collector_mapping(self, service='compute'):
uri = '/collector/mappings'
request_body = {
'service': service,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def get_collector_state(self, collector="gnocchi"):
uri = '/collector/states/'
request_body = {
'name': collector,
}
return self._do_request('GET', uri, body=self.serialize(request_body))
def set_collector_state(self, collector="gnocchi", enabled=True):
uri = '/collector/states/'
request_body = {
'name': collector,
'enabled': enabled,
}
return self._do_request('PUT', uri, body=self.serialize(request_body))
def get_config(self):
uri = '/info/config/'
return self._do_request('GET', uri)
def get_metric(self, metric_name=None):
uri = '/info/metrics/'
if metric_name:
uri += metric_name + '/'
return self._do_request('GET', uri)
def get_rating_module(self, module_name=None):
uri = '/rating/modules/'
if module_name:
uri += module_name + '/'
return self._do_request('GET', uri)
def update_rating_module(self, module_name, description='',
enabled=False, hot_config=True, priority=1):
uri = '/rating/modules/' + module_name + '/'
request_body = {
'module_id': module_name,
'description': description,
'enabled': enabled,
'hot-config': hot_config,
'priority': priority,
}
return self._do_request('PUT', uri, body=self.serialize(request_body),
expected_code=302)
def reload_rating_modules(self):
uri = '/rating/reload_modules'
return self._do_request('GET', uri, expected_code=204)
def get_report_summary(self):
uri = '/report/summary/'
return self._do_request('GET', uri)
def get_rated_tenants(self):
uri = '/report/tenants/'
return self._do_request('GET', uri)
def get_report_total(self):
uri = '/report/total/'
return self._do_request('GET', uri)
def get_storage_dataframes(self):
uri = '/storage/dataframes/'
return self._do_request('GET', uri)
def get_hashmap_mapping_types(self):
uri = '/rating/module_config/hashmap/types/'
return self._do_request('GET', uri)
def get_hashmap_service(self, service_id=None):
uri = '/rating/module_config/hashmap/services/'
if service_id:
uri += service_id + '/'
return self._do_request('GET', uri)
def create_hashmap_service(self, name, service_id=None):
uri = '/rating/module_config/hashmap/services/'
request_body = {
'name': name,
}
if service_id:
request_body['service_id'] = service_id
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def delete_hashmap_service(self, service_id):
uri = '/rating/module_config/hashmap/services/'
request_body = {
'service_id': service_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def get_hashmap_fields(self, service_id):
uri = '/rating/module_config/hashmap/fields/'
request_body = {
'service_id': service_id,
}
return self._do_request('GET', uri,
body=self.serialize(request_body))
def get_hashmap_field(self, field_id):
uri = '/rating/module_config/hashmap/fields/' + field_id + '/'
return self._do_request('GET', uri)
def create_hashmap_field(self, field_name, service_id, field_id=None):
uri = '/rating/module_config/hashmap/fields/'
request_body = {
'name': field_name,
'service_id': service_id,
}
if field_id:
request_body['field_id'] = field_id
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def delete_hashmap_field(self, field_id):
uri = '/rating/module_config/hashmap/fields/'
request_body = {
'field_id': field_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def get_hashmap_mappings(self, service_id=None, field_id=None,
group_id=None, no_group=False, tenant_id=None,
filter_tenant=False):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/mappings/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('GET', uri,
body=self.serialize(request_body))
def get_hashmap_mapping(self, mapping_id):
uri = '/rating/module_config/hashmap/mappings/' + mapping_id + '/'
return self._do_request('GET', uri)
def create_hashmap_mapping(self, cost=0, field_id=None, group_id=None,
map_type=None, mapping_id=None, service_id=None,
tenant_id=None, value=None):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/mappings/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def delete_hashmap_mapping(self, mapping_id):
uri = '/rating/module_config/hashmap/mappings/'
request_body = {
'mapping_id': mapping_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def update_hashmap_mapping(self, mapping_id, cost=0,
field_id=None, group_id=None, map_type=None,
service_id=None, tenant_id=None, value=None,
end=None):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/mappings/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('PUT', uri,
body=self.serialize(request_body),
expected_code=302)
def get_hashmap_mapping_group(self, mapping_id):
uri = '/rating/module_config/hashmap/mappings/group/'
request_body = {
'mapping_id': mapping_id,
}
return self._do_request('GET', uri,
body=self.serialize(request_body))
def get_hashmap_group(self, group_id=None):
uri = '/rating/module_config/hashmap/groups/'
if group_id:
uri += group_id + '/'
return self._do_request('GET', uri)
def create_hashmap_group(self, group_name, group_id=None):
uri = '/rating/module_config/hashmap/groups'
request_body = {
'name': group_name,
}
if group_id:
request_body['group_id'] = group_id
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def delete_hashmap_group(self, group_id):
uri = '/rating/module_config/hashmap/groups'
request_body = {
'group_id': group_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def get_hashmap_group_mappings(self, group_id):
uri = '/rating/module_config/hashmap/groups/mappings/'
request_body = {
'group_id': group_id,
}
return self._do_request('GET', uri,
body=self.serialize(request_body))
def get_hashmap_group_threshold(self, group_id):
uri = '/rating/module_config/hashmap/groups/thresholds/'
request_body = {
'group_id': group_id,
}
return self._do_request('GET', uri,
body=self.serialize(request_body))
def get_hashmap_threshold(self, threshold_id):
uri = '/rating/module_config/hashmap/thresholds/' + threshold_id + '/'
return self._do_request('GET', uri)
def get_hashmap_thresholds(self, service_id=None,
field_id=None, group_id=None):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/thresholds/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('GET', uri,
body=self.serialize(request_body))
def create_hashmap_threshold(self, field_id=None, group_id=None,
threshold_id=None, map_type=None, cost=None,
service_id=None, tenant_id=None, level=None):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/thresholds/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def update_hashmap_threshold(self, threshold_id, field_id=None,
group_id=None, map_type=None, cost=None,
service_id=None, tenant_id=None, level=None):
args = locals()
args.pop('self')
uri = '/rating/module_config/hashmap/thresholds/'
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return self._do_request('PUT', uri,
body=self.serialize(request_body),
expected_code=302)
def delete_hashmap_threshold(self, threshold_id):
uri = '/rating/module_config/hashmap/thresholds/'
request_body = {
'threshold_id': threshold_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
def get_pyscripts(self, no_data=False):
uri = '/rating/module_config/pyscripts/scripts/'
request_body = {
'no_data': no_data,
}
return self._do_request('GET', uri, body=self.serialize(request_body))
def get_pyscript(self, script_id):
uri = '/rating/module_config/pyscripts/scripts/' + script_id + '/'
return self._do_request('GET', uri)
@staticmethod
def _get_pyscript_request_body(name, data, checksum, script_id, end=None):
args = locals()
request_body = dict((k, v)
for k, v in args.items() if v is not None)
return request_body
def create_pyscript(self, name, data, checksum=None, script_id=None):
uri = '/rating/module_config/pyscripts/scripts/'
request_body = self._get_pyscript_request_body(name, data,
checksum, script_id)
return self._do_request('POST', uri,
body=self.serialize(request_body),
expected_code=201)
def update_pyscript(self, script_id, name=None, data=None, checksum=None,
end=None):
uri = '/rating/module_config/pyscripts/scripts/'
request_body = self._get_pyscript_request_body(name, data,
checksum, script_id,
end)
return self._do_request('PUT', uri,
body=self.serialize(request_body),
expected_code=201)
def delete_pyscript(self, script_id):
uri = '/rating/module_config/pyscripts/scripts/'
request_body = {
'script_id': script_id,
}
return self._do_request('DELETE', uri,
body=self.serialize(request_body),
expected_code=204)
class RatingClientV2(RatingClientV1):
"""Implementation of cloudkittyclient for v2 endpoints"""
api_version = 'v2'
class CustomIdentityClient(object):
"""Custom Keystone client
Class used by the CK tempest plugin to add the 'rating' role to
the dynamically allocated test tenant
"""
def __init__(self):
self.admin_auth = v3.Password(
auth_url=CONF.identity.uri_v3,
username=CONF.auth.admin_username,
password=CONF.auth.admin_password,
project_name=CONF.auth.admin_project_name,
project_domain_name=CONF.auth.admin_domain_name,
user_domain_name=CONF.auth.admin_domain_name,
)
self.admin_session = session.Session(auth=self.admin_auth)
self.admin_client = client.Client(session=self.admin_session)
self.ck_user_id = self._get_ck_user_id()
self.rating_role_id = self._get_rating_role_id()
def enable_rating(self, project_id):
"""Assigns the 'rating' role to ck user on the given project"""
self.admin_client.roles.grant(self.rating_role_id,
user=self.ck_user_id,
project=project_id)
@staticmethod
def _find_item(iterable, key, value):
item = None
for elem in iterable:
if getattr(elem, key, None) == value:
item = elem
return item
def _get_ck_user_id(self):
users = self.admin_client.users.list()
return getattr(
self._find_item(users, 'name', CONF.rating_plugin.user_name),
'id', None,
)
def _get_rating_role_id(self):
roles = self.admin_client.roles.list()
return getattr(
self._find_item(roles, 'name', 'rating'), 'id', None,
)
class Manager(clients.ServiceClients):
rating_params = {
'service': CONF.rating_plugin.service_name,
'region': CONF.identity.region,
'endpoint_type': CONF.rating_plugin.endpoint_type,
}
def __init__(self, credentials=None, service=None):
dscv = CONF.identity.disable_ssl_certificate_validation
_, uri = tempest_clients.get_auth_provider_class(credentials)
super(Manager, self).__init__(
credentials=credentials,
identity_uri=uri,
scope='project',
disable_ssl_certificate_validation=dscv,
ca_certs=CONF.identity.ca_certificates_file,
trace_requests=CONF.debug.trace_requests)
self.rating_clients = {
'v1': RatingClientV1(self.auth_provider, **self.rating_params),
'v2': RatingClientV2(self.auth_provider, **self.rating_params),
}
def get_rating_client(self, api_version='v2'):
if api_version not in self.rating_clients:
raise ValueError('API version must be one of the following: {}',
list(self.rating_client.keys()))
return self.rating_clients[api_version]