56b4e61f0c
The same information is now available from the deployment's derived config deploy_signal_id input value. Other deployment signal transports might not have a single ID to put in this field so it is better at this stage to remove it from the deployments model and do anything that is required using derived config inputs. partial blueprint hot-software-config Change-Id: I90c85d0ceb9fd67eed640c84348ef4175a6194b6
339 lines
12 KiB
Python
339 lines
12 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.
|
|
|
|
from heat.common import template_format
|
|
from heat.rpc import api
|
|
from heat.openstack.common import timeutils
|
|
from heat.engine import constraints as constr
|
|
|
|
from heat.openstack.common import log as logging
|
|
from heat.openstack.common.gettextutils import _
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def extract_args(params):
|
|
'''
|
|
Extract any arguments passed as parameters through the API and return them
|
|
as a dictionary. This allows us to filter the passed args and do type
|
|
conversion where appropriate
|
|
'''
|
|
kwargs = {}
|
|
try:
|
|
timeout_mins = int(params.get(api.PARAM_TIMEOUT, 0))
|
|
except (ValueError, TypeError):
|
|
logger.exception(_('create timeout conversion'))
|
|
else:
|
|
if timeout_mins > 0:
|
|
kwargs[api.PARAM_TIMEOUT] = timeout_mins
|
|
|
|
if api.PARAM_DISABLE_ROLLBACK in params:
|
|
disable_rollback = params.get(api.PARAM_DISABLE_ROLLBACK)
|
|
if str(disable_rollback).lower() == 'true':
|
|
kwargs[api.PARAM_DISABLE_ROLLBACK] = True
|
|
elif str(disable_rollback).lower() == 'false':
|
|
kwargs[api.PARAM_DISABLE_ROLLBACK] = False
|
|
else:
|
|
raise ValueError(_('Unexpected value for parameter'
|
|
' %(name)s : %(value)s') %
|
|
dict(name=api.PARAM_DISABLE_ROLLBACK,
|
|
value=disable_rollback))
|
|
|
|
adopt_data = params.get(api.PARAM_ADOPT_STACK_DATA)
|
|
if adopt_data:
|
|
adopt_data = template_format.simple_parse(adopt_data)
|
|
if not isinstance(adopt_data, dict):
|
|
raise ValueError(
|
|
_('Unexpected adopt data "%s". Adopt data must be a dict.')
|
|
% adopt_data)
|
|
kwargs[api.PARAM_ADOPT_STACK_DATA] = adopt_data
|
|
|
|
return kwargs
|
|
|
|
|
|
def format_stack_outputs(stack, outputs):
|
|
'''
|
|
Return a representation of the given output template for the given stack
|
|
that matches the API output expectations.
|
|
'''
|
|
def format_stack_output(k):
|
|
return {api.OUTPUT_DESCRIPTION: outputs[k].get('Description',
|
|
'No description given'),
|
|
api.OUTPUT_KEY: k,
|
|
api.OUTPUT_VALUE: stack.output(k)}
|
|
|
|
return [format_stack_output(key) for key in outputs]
|
|
|
|
|
|
def format_stack(stack):
|
|
'''
|
|
Return a representation of the given stack that matches the API output
|
|
expectations.
|
|
'''
|
|
updated_time = stack.updated_time and timeutils.isotime(stack.updated_time)
|
|
info = {
|
|
api.STACK_NAME: stack.name,
|
|
api.STACK_ID: dict(stack.identifier()),
|
|
api.STACK_CREATION_TIME: timeutils.isotime(stack.created_time),
|
|
api.STACK_UPDATED_TIME: updated_time,
|
|
api.STACK_NOTIFICATION_TOPICS: [], # TODO Not implemented yet
|
|
api.STACK_PARAMETERS: stack.parameters.map(str),
|
|
api.STACK_DESCRIPTION: stack.t[stack.t.DESCRIPTION],
|
|
api.STACK_TMPL_DESCRIPTION: stack.t[stack.t.DESCRIPTION],
|
|
api.STACK_ACTION: stack.action or '',
|
|
api.STACK_STATUS: stack.status or '',
|
|
api.STACK_STATUS_DATA: stack.status_reason,
|
|
api.STACK_CAPABILITIES: [], # TODO Not implemented yet
|
|
api.STACK_DISABLE_ROLLBACK: stack.disable_rollback,
|
|
api.STACK_TIMEOUT: stack.timeout_mins,
|
|
}
|
|
|
|
# only show the outputs on a completely created or updated stack
|
|
if (stack.action != stack.DELETE and stack.status == stack.COMPLETE):
|
|
info[api.STACK_OUTPUTS] = format_stack_outputs(stack, stack.outputs)
|
|
|
|
return info
|
|
|
|
|
|
def format_stack_resource(resource, detail=True):
|
|
'''
|
|
Return a representation of the given resource that matches the API output
|
|
expectations.
|
|
'''
|
|
last_updated_time = resource.updated_time or resource.created_time
|
|
res = {
|
|
api.RES_UPDATED_TIME: timeutils.isotime(last_updated_time),
|
|
api.RES_NAME: resource.name,
|
|
api.RES_PHYSICAL_ID: resource.resource_id or '',
|
|
api.RES_METADATA: resource.metadata,
|
|
api.RES_ACTION: resource.action,
|
|
api.RES_STATUS: resource.status,
|
|
api.RES_STATUS_DATA: resource.status_reason,
|
|
api.RES_TYPE: resource.t['Type'],
|
|
api.RES_ID: dict(resource.identifier()),
|
|
api.RES_STACK_ID: dict(resource.stack.identifier()),
|
|
api.RES_STACK_NAME: resource.stack.name,
|
|
api.RES_REQUIRED_BY: resource.required_by(),
|
|
}
|
|
|
|
if detail:
|
|
res[api.RES_DESCRIPTION] = resource.parsed_template('Description', '')
|
|
res[api.RES_METADATA] = resource.metadata
|
|
|
|
return res
|
|
|
|
|
|
def format_stack_preview(stack):
|
|
def format_resource(res):
|
|
if isinstance(res, list):
|
|
return map(format_resource, res)
|
|
return format_stack_resource(res)
|
|
|
|
fmt_stack = format_stack(stack)
|
|
fmt_resources = map(format_resource, stack.preview_resources())
|
|
fmt_stack['resources'] = fmt_resources
|
|
|
|
return fmt_stack
|
|
|
|
|
|
def format_event(event):
|
|
stack_identifier = event.stack.identifier()
|
|
|
|
result = {
|
|
api.EVENT_ID: dict(event.identifier()),
|
|
api.EVENT_STACK_ID: dict(stack_identifier),
|
|
api.EVENT_STACK_NAME: stack_identifier.stack_name,
|
|
api.EVENT_TIMESTAMP: timeutils.isotime(event.timestamp),
|
|
api.EVENT_RES_NAME: event.resource_name,
|
|
api.EVENT_RES_PHYSICAL_ID: event.physical_resource_id,
|
|
api.EVENT_RES_ACTION: event.action,
|
|
api.EVENT_RES_STATUS: event.status,
|
|
api.EVENT_RES_STATUS_DATA: event.reason,
|
|
api.EVENT_RES_TYPE: event.resource_type,
|
|
api.EVENT_RES_PROPERTIES: event.resource_properties,
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
def format_notification_body(stack):
|
|
# some other posibilities here are:
|
|
# - template name
|
|
# - template size
|
|
# - resource count
|
|
if stack.status is not None and stack.action is not None:
|
|
state = '_'.join(stack.state)
|
|
else:
|
|
state = 'Unknown'
|
|
result = {
|
|
api.NOTIFY_TENANT_ID: stack.context.tenant_id,
|
|
api.NOTIFY_USER_ID: stack.context.user,
|
|
api.NOTIFY_STACK_ID: stack.identifier().arn(),
|
|
api.NOTIFY_STACK_NAME: stack.name,
|
|
api.NOTIFY_STATE: state,
|
|
api.NOTIFY_STATE_REASON: stack.status_reason,
|
|
api.NOTIFY_CREATE_AT: timeutils.isotime(stack.created_time),
|
|
}
|
|
return result
|
|
|
|
|
|
def format_watch(watch):
|
|
|
|
result = {
|
|
api.WATCH_ACTIONS_ENABLED: watch.rule.get(api.RULE_ACTIONS_ENABLED),
|
|
api.WATCH_ALARM_ACTIONS: watch.rule.get(api.RULE_ALARM_ACTIONS),
|
|
api.WATCH_TOPIC: watch.rule.get(api.RULE_TOPIC),
|
|
api.WATCH_UPDATED_TIME: timeutils.isotime(watch.updated_at),
|
|
api.WATCH_DESCRIPTION: watch.rule.get(api.RULE_DESCRIPTION),
|
|
api.WATCH_NAME: watch.name,
|
|
api.WATCH_COMPARISON: watch.rule.get(api.RULE_COMPARISON),
|
|
api.WATCH_DIMENSIONS: watch.rule.get(api.RULE_DIMENSIONS) or [],
|
|
api.WATCH_PERIODS: watch.rule.get(api.RULE_PERIODS),
|
|
api.WATCH_INSUFFICIENT_ACTIONS:
|
|
watch.rule.get(api.RULE_INSUFFICIENT_ACTIONS),
|
|
api.WATCH_METRIC_NAME: watch.rule.get(api.RULE_METRIC_NAME),
|
|
api.WATCH_NAMESPACE: watch.rule.get(api.RULE_NAMESPACE),
|
|
api.WATCH_OK_ACTIONS: watch.rule.get(api.RULE_OK_ACTIONS),
|
|
api.WATCH_PERIOD: watch.rule.get(api.RULE_PERIOD),
|
|
api.WATCH_STATE_REASON: watch.rule.get(api.RULE_STATE_REASON),
|
|
api.WATCH_STATE_REASON_DATA:
|
|
watch.rule.get(api.RULE_STATE_REASON_DATA),
|
|
api.WATCH_STATE_UPDATED_TIME: timeutils.isotime(
|
|
watch.rule.get(api.RULE_STATE_UPDATED_TIME)),
|
|
api.WATCH_STATE_VALUE: watch.state,
|
|
api.WATCH_STATISTIC: watch.rule.get(api.RULE_STATISTIC),
|
|
api.WATCH_THRESHOLD: watch.rule.get(api.RULE_THRESHOLD),
|
|
api.WATCH_UNIT: watch.rule.get(api.RULE_UNIT),
|
|
api.WATCH_STACK_ID: watch.stack_id
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
def format_watch_data(wd):
|
|
|
|
# Demangle DB format data into something more easily used in the API
|
|
# We are expecting a dict with exactly two items, Namespace and
|
|
# a metric key
|
|
namespace = wd.data['Namespace']
|
|
metric = [(k, v) for k, v in wd.data.items() if k != 'Namespace']
|
|
if len(metric) == 1:
|
|
metric_name, metric_data = metric[0]
|
|
else:
|
|
logger.error(_("Unexpected number of keys in watch_data.data!"))
|
|
return
|
|
|
|
result = {
|
|
api.WATCH_DATA_ALARM: wd.watch_rule.name,
|
|
api.WATCH_DATA_METRIC: metric_name,
|
|
api.WATCH_DATA_TIME: timeutils.isotime(wd.created_at),
|
|
api.WATCH_DATA_NAMESPACE: namespace,
|
|
api.WATCH_DATA: metric_data
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
def format_validate_parameter(param):
|
|
"""
|
|
Format a template parameter for validate template API call
|
|
|
|
Formats a template parameter and its schema information from the engine's
|
|
internal representation (i.e. a Parameter object and its associated
|
|
Schema object) to a representation expected by the current API (for example
|
|
to be compatible to CFN syntax).
|
|
"""
|
|
|
|
# map of Schema object types to API expected types
|
|
schema_to_api_types = {
|
|
param.schema.STRING: api.PARAM_TYPE_STRING,
|
|
param.schema.NUMBER: api.PARAM_TYPE_NUMBER,
|
|
param.schema.LIST: api.PARAM_TYPE_COMMA_DELIMITED_LIST,
|
|
param.schema.MAP: api.PARAM_TYPE_JSON
|
|
}
|
|
|
|
res = {
|
|
api.PARAM_TYPE: schema_to_api_types.get(param.schema.type,
|
|
param.schema.type),
|
|
api.PARAM_DESCRIPTION: param.description(),
|
|
api.PARAM_NO_ECHO: 'true' if param.hidden() else 'false',
|
|
api.PARAM_LABEL: param.label()
|
|
}
|
|
|
|
if param.has_default():
|
|
res[api.PARAM_DEFAULT] = param.default()
|
|
|
|
constraint_description = []
|
|
|
|
# build constraints
|
|
for c in param.schema.constraints:
|
|
if isinstance(c, constr.Length):
|
|
if c.min is not None:
|
|
res[api.PARAM_MIN_LENGTH] = c.min
|
|
|
|
if c.max is not None:
|
|
res[api.PARAM_MAX_LENGTH] = c.max
|
|
|
|
elif isinstance(c, constr.Range):
|
|
if c.min is not None:
|
|
res[api.PARAM_MIN_VALUE] = c.min
|
|
|
|
if c.max is not None:
|
|
res[api.PARAM_MAX_VALUE] = c.max
|
|
|
|
elif isinstance(c, constr.AllowedValues):
|
|
res[api.PARAM_ALLOWED_VALUES] = list(c.allowed)
|
|
|
|
elif isinstance(c, constr.AllowedPattern):
|
|
res[api.PARAM_ALLOWED_PATTERN] = c.pattern
|
|
|
|
if c.description:
|
|
constraint_description.append(c.description)
|
|
|
|
if constraint_description:
|
|
res[api.PARAM_CONSTRAINT_DESCRIPTION] = " ".join(
|
|
constraint_description)
|
|
|
|
return res
|
|
|
|
|
|
def format_software_config(sc):
|
|
if sc is None:
|
|
return
|
|
result = {
|
|
api.SOFTWARE_CONFIG_ID: sc.id,
|
|
api.SOFTWARE_CONFIG_NAME: sc.name,
|
|
api.SOFTWARE_CONFIG_GROUP: sc.group,
|
|
api.SOFTWARE_CONFIG_CONFIG: sc.config['config'],
|
|
api.SOFTWARE_CONFIG_INPUTS: sc.config['inputs'],
|
|
api.SOFTWARE_CONFIG_OUTPUTS: sc.config['outputs'],
|
|
api.SOFTWARE_CONFIG_OPTIONS: sc.config['options']
|
|
}
|
|
return result
|
|
|
|
|
|
def format_software_deployment(sd):
|
|
if sd is None:
|
|
return
|
|
result = {
|
|
api.SOFTWARE_DEPLOYMENT_ID: sd.id,
|
|
api.SOFTWARE_DEPLOYMENT_SERVER_ID: sd.server_id,
|
|
api.SOFTWARE_DEPLOYMENT_INPUT_VALUES: sd.input_values,
|
|
api.SOFTWARE_DEPLOYMENT_OUTPUT_VALUES: sd.output_values,
|
|
api.SOFTWARE_DEPLOYMENT_ACTION: sd.action,
|
|
api.SOFTWARE_DEPLOYMENT_STATUS: sd.status,
|
|
api.SOFTWARE_DEPLOYMENT_STATUS_REASON: sd.status_reason,
|
|
api.SOFTWARE_DEPLOYMENT_CONFIG_ID: sd.config.id,
|
|
}
|
|
return result
|