
Nested key can be specified with '.' separator: key1.key2.key3. Nested keys handled in the API and in the fuel2. Tests for keys operations moved from the test for resource values to the test for resource key operations. Change-Id: I73b5fd9a4a4720a96af351d7e6a7cea14d816f75 Partial-Bug: #1642330
169 lines
6.8 KiB
Python
169 lines
6.8 KiB
Python
# 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 flask
|
|
from flask import current_app as app
|
|
import flask_restful
|
|
import six
|
|
from sqlalchemy import or_
|
|
|
|
from tuning_box import db
|
|
from tuning_box import errors
|
|
from tuning_box import library
|
|
from tuning_box.library import hierarchy_levels
|
|
from tuning_box.library import resource_keys_operation
|
|
from tuning_box.library.resource_keys_operation import KeysOperationMixin
|
|
|
|
|
|
class ResourceValues(flask_restful.Resource, KeysOperationMixin):
|
|
|
|
KEYS_PATH_DELIMITER = '.'
|
|
|
|
@db.with_transaction
|
|
def put(self, environment_id, levels, resource_id_or_name):
|
|
environment = db.Environment.query.get_or_404(environment_id)
|
|
res_def = library.get_resource_definition(
|
|
resource_id_or_name, environment_id)
|
|
|
|
level_value = hierarchy_levels.get_environment_level_value(
|
|
environment, levels)
|
|
esv = db.get_or_create(
|
|
db.ResourceValues,
|
|
environment=environment,
|
|
resource_definition=res_def,
|
|
level_value=level_value,
|
|
)
|
|
esv.values = flask.request.json
|
|
return None, 204
|
|
|
|
def _calculate_effective_values(self, result, level_value,
|
|
resource_values_idx, show_lookup,
|
|
lookup_path):
|
|
level_value_id = getattr(level_value, 'id', None)
|
|
if level_value_id in resource_values_idx:
|
|
resource_value = resource_values_idx[level_value_id]
|
|
if show_lookup:
|
|
values = ((k, (v, lookup_path)) for k, v in
|
|
six.iteritems(resource_value.values))
|
|
overrides = ((k, (v, lookup_path)) for k, v in
|
|
six.iteritems(resource_value.overrides))
|
|
else:
|
|
values = resource_value.values
|
|
overrides = resource_value.overrides
|
|
result.update(values)
|
|
result.update(overrides)
|
|
|
|
@db.with_transaction
|
|
def get(self, environment_id, resource_id_or_name, levels):
|
|
app.logger.debug("Getting resource value. Env: %s, "
|
|
"resource: %s, levels: %s", environment_id,
|
|
resource_id_or_name, levels)
|
|
|
|
effective = 'effective' in flask.request.args
|
|
show_lookup = 'show_lookup' in flask.request.args
|
|
|
|
if show_lookup and not effective:
|
|
raise errors.RequestValidationError(
|
|
"Lookup path tracing can be done only for effective values")
|
|
|
|
environment = db.Environment.query.get_or_404(environment_id)
|
|
res_def = library.get_resource_definition(
|
|
resource_id_or_name, environment_id)
|
|
|
|
level_values = list(hierarchy_levels.iter_environment_level_values(
|
|
environment, levels))
|
|
|
|
level_values_ids = [l.id for l in level_values]
|
|
app.logger.debug("Got level values ids: %s", level_values_ids)
|
|
|
|
if effective:
|
|
app.logger.debug("Getting effective resource value. Env: %s, "
|
|
"resource: %s, levels: %s", environment_id,
|
|
resource_id_or_name, levels)
|
|
resource_values = db.ResourceValues.query.filter(
|
|
or_(
|
|
db.ResourceValues.level_value_id.in_(level_values_ids),
|
|
db.ResourceValues.level_value_id.is_(None)
|
|
),
|
|
db.ResourceValues.resource_definition == res_def,
|
|
db.ResourceValues.environment == environment
|
|
).all()
|
|
app.logger.debug("Processing values for resource: %s, env: %s. "
|
|
"Loaded resource values: %s",
|
|
res_def.id, environment.id, len(resource_values))
|
|
# Creating index of resource_values by level_value_id
|
|
resource_values_idx = {r.level_value_id: r
|
|
for r in resource_values}
|
|
app.logger.debug("Resource values index size: %s",
|
|
len(resource_values_idx))
|
|
|
|
result = {}
|
|
lookup_path = '/'
|
|
self._calculate_effective_values(
|
|
result, None, resource_values_idx, show_lookup,
|
|
lookup_path)
|
|
|
|
for level_value in level_values:
|
|
name = level_value.level.name
|
|
value = level_value.value
|
|
lookup_path += name + '/' + value + '/'
|
|
|
|
self._calculate_effective_values(
|
|
result, level_value, resource_values_idx, show_lookup,
|
|
lookup_path)
|
|
|
|
app.logger.debug("Effective values got for resource: "
|
|
"%s, env: %s", res_def.id, environment.id)
|
|
else:
|
|
if not level_values:
|
|
level_value = None
|
|
else:
|
|
level_value = level_values[-1]
|
|
resource_values = db.ResourceValues.query.filter_by(
|
|
resource_definition=res_def,
|
|
environment=environment,
|
|
level_value=level_value,
|
|
).one_or_none()
|
|
app.logger.debug("Values got for resource: "
|
|
"%s, env: %s", res_def.id, environment.id)
|
|
if resource_values:
|
|
result = resource_values.values
|
|
else:
|
|
result = {}
|
|
return self._extract_keys_paths(result)
|
|
|
|
def _extract_keys_paths(self, data):
|
|
if 'key' not in flask.request.args:
|
|
return data
|
|
keys_path = flask.request.args['key'].split(self.KEYS_PATH_DELIMITER)
|
|
app.logger.debug("Extracting data by keys paths: %s", keys_path)
|
|
result = self.do_get(data, [keys_path])
|
|
# Single keys path is passed as GET request parameter, so we need
|
|
# only first result
|
|
result = result[0]
|
|
app.logger.debug("Extracted data by keys paths: %s is: %s",
|
|
keys_path, result)
|
|
return result
|
|
|
|
|
|
class ResourceValuesKeys(flask_restful.Resource,
|
|
resource_keys_operation.ResourceKeysMixin):
|
|
|
|
def put(self, environment_id, levels, resource_id_or_name, operation):
|
|
return self.patch(environment_id, levels,
|
|
resource_id_or_name, operation)
|
|
|
|
def patch(self, environment_id, levels, resource_id_or_name, operation):
|
|
self._do_update(environment_id, levels, resource_id_or_name,
|
|
operation, 'values')
|
|
return None, 204
|