363 lines
12 KiB
Python
363 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2014 - Mirantis, Inc.
|
|
#
|
|
# 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 copy
|
|
import json
|
|
|
|
from mistral.actions import action_factory as a_f
|
|
from mistral.actions import std_actions as std
|
|
from mistral.db.sqlalchemy import models
|
|
from mistral.engine import data_flow
|
|
from mistral import exceptions
|
|
from mistral.openstack.common import log as logging
|
|
from mistral.tests import base
|
|
from mistral.workbook import parser as spec_parser
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
DB_TASK = {
|
|
'task_spec': {
|
|
'name': 'my_task',
|
|
'action': 'std.http'
|
|
},
|
|
'name': 'my_task',
|
|
'workbook_name': 'my_workbook',
|
|
'execution_id': '123',
|
|
'id': '123',
|
|
'in_context': {},
|
|
'tags': ['deployment', 'test'],
|
|
'parameters': {
|
|
'url': 'http://some.url',
|
|
'params': {
|
|
'param1': 'val1',
|
|
'param2': 'val2'
|
|
},
|
|
'method': 'POST',
|
|
'headers': {
|
|
'content-type': 'text/json'
|
|
},
|
|
'body': {
|
|
'my_object': {
|
|
'prop1': 'val1',
|
|
'prop2': 'val2'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DB_TASK_ADHOC = {
|
|
'task_spec': {
|
|
'name': 'my_task',
|
|
'action': 'my_namespace.my_action'
|
|
},
|
|
'action_spec': {
|
|
'name': 'my_action',
|
|
'namespace': 'my_namespace',
|
|
'class': 'std.echo',
|
|
'base-parameters': {
|
|
'output': '{$.first} and {$.second}'
|
|
},
|
|
'parameters': ['first', 'second'],
|
|
'output': {
|
|
'res': '{$}'
|
|
}
|
|
},
|
|
'name': 'my_task',
|
|
'workbook_name': 'my_workbook',
|
|
'execution_id': '123',
|
|
'id': '123',
|
|
'in_context': {},
|
|
'tags': ['deployment', 'test'],
|
|
'parameters': {
|
|
'first': 'Tango',
|
|
'second': 'Cash'
|
|
}
|
|
}
|
|
|
|
|
|
class ActionFactoryTest(base.BaseTest):
|
|
def test_register_standard_actions(self):
|
|
namespaces = a_f.get_registered_namespaces()
|
|
|
|
self.assertIn("nova", namespaces)
|
|
self.assertIn("glance", namespaces)
|
|
self.assertIn("keystone", namespaces)
|
|
self.assertIn("std", namespaces)
|
|
|
|
std_ns = namespaces["std"]
|
|
nova_ns = namespaces["nova"]
|
|
keystone_ns = namespaces["keystone"]
|
|
glance_ns = namespaces["glance"]
|
|
|
|
self.assertEqual(5, len(std_ns))
|
|
|
|
self.assertTrue(nova_ns.contains_action_name("servers_get"))
|
|
self.assertTrue(nova_ns.contains_action_name("volumes_delete"))
|
|
|
|
self.assertTrue(keystone_ns.contains_action_name("users_list"))
|
|
self.assertTrue(keystone_ns.contains_action_name("trusts_create"))
|
|
|
|
self.assertTrue(glance_ns.contains_action_name("images_list"))
|
|
self.assertTrue(glance_ns.contains_action_name("images_delete"))
|
|
|
|
self.assertTrue(std_ns.contains_action_name("echo"))
|
|
self.assertTrue(std_ns.contains_action_name("http"))
|
|
self.assertTrue(std_ns.contains_action_name("mistral_http"))
|
|
self.assertTrue(std_ns.contains_action_name("email"))
|
|
|
|
self.assertEqual(std.EchoAction, std_ns.get_action_class("echo"))
|
|
self.assertEqual(std.HTTPAction, std_ns.get_action_class("http"))
|
|
self.assertEqual(std.MistralHTTPAction,
|
|
std_ns.get_action_class("mistral_http"))
|
|
self.assertEqual(std.SendEmailAction,
|
|
std_ns.get_action_class("email"))
|
|
|
|
def test_get_action_class(self):
|
|
self.assertEqual(std.EchoAction, a_f.get_action_class("std.echo"))
|
|
self.assertEqual(std.HTTPAction, a_f.get_action_class("std.http"))
|
|
self.assertEqual(std.SendEmailAction,
|
|
a_f.get_action_class("std.email"))
|
|
|
|
def test_get_action_class_failure(self):
|
|
exc = self.assertRaises(exceptions.ActionException,
|
|
a_f.get_action_class, 'echo')
|
|
self.assertIn('Invalid action name', exc.message)
|
|
|
|
def test_create_http_action(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK))
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertIsNotNone(action)
|
|
|
|
task_params = DB_TASK['parameters']
|
|
|
|
self.assertEqual(task_params['url'], action.url)
|
|
self.assertDictEqual(task_params['params'], action.params)
|
|
self.assertEqual(task_params['method'], action.method)
|
|
self.assertEqual(task_params['headers'], action.headers)
|
|
self.assertDictEqual(task_params['body'], json.loads(action.body))
|
|
|
|
def test_create_mistral_http_action(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK))
|
|
|
|
db_task.task_spec['action'] = 'std.mistral_http'
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertIsNotNone(action)
|
|
|
|
task_params = db_task.parameters
|
|
|
|
self.assertEqual(task_params['url'], action.url)
|
|
self.assertDictEqual(task_params['params'], action.params)
|
|
self.assertEqual(task_params['method'], action.method)
|
|
self.assertEqual(task_params['headers'], action.headers)
|
|
self.assertDictEqual(task_params['body'], json.loads(action.body))
|
|
|
|
self.assertEqual(db_task.workbook_name,
|
|
action.headers['Mistral-Workbook-Name'])
|
|
self.assertEqual(db_task.execution_id,
|
|
action.headers['Mistral-Execution-Id'])
|
|
self.assertEqual(db_task.id,
|
|
action.headers['Mistral-Task-Id'])
|
|
|
|
def test_create_mistral_http_action_with_no_headers(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK))
|
|
|
|
db_task.task_spec['action'] = 'std.mistral_http'
|
|
|
|
del db_task.parameters['headers']
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertIsNotNone(action)
|
|
|
|
task_params = db_task.parameters
|
|
|
|
self.assertEqual(task_params['url'], action.url)
|
|
self.assertDictEqual(task_params['params'], action.params)
|
|
self.assertEqual(task_params['method'], action.method)
|
|
self.assertDictEqual(task_params['body'], json.loads(action.body))
|
|
|
|
self.assertIsNotNone(action.headers)
|
|
|
|
self.assertEqual(db_task.workbook_name,
|
|
action.headers['Mistral-Workbook-Name'])
|
|
self.assertEqual(db_task.execution_id,
|
|
action.headers['Mistral-Execution-Id'])
|
|
self.assertEqual(db_task.id,
|
|
action.headers['Mistral-Task-Id'])
|
|
|
|
def test_create_adhoc_action_with_openstack_context(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK_ADHOC))
|
|
|
|
db_task.action_spec['output'] = {'res': '{$}'}
|
|
db_task.in_context.update({
|
|
'openstack': {
|
|
'auth_token': '123',
|
|
'project_id': '321'
|
|
}
|
|
})
|
|
base_parameters = db_task.action_spec['base-parameters']
|
|
base_parameters['output'] = ("{$.openstack.auth_token}"
|
|
"{$.openstack.project_id}")
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertEqual({'res': "123321"}, action.run())
|
|
|
|
def test_create_adhoc_action_no_openstack_context(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK_ADHOC))
|
|
|
|
db_task.action_spec['output'] = {'res': '{$}'}
|
|
db_task.in_context.update({
|
|
'openstack': None
|
|
})
|
|
|
|
base_parameters = db_task.action_spec['base-parameters']
|
|
base_parameters['output'] = "$.openstack.auth_token"
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertEqual({'res': "$.openstack.auth_token"}, action.run())
|
|
|
|
def test_create_no_adhoc_action_with_openstack_context(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK))
|
|
|
|
db_task.task_spec['action'] = 'std.http'
|
|
db_task.in_context.update({
|
|
'openstack': {
|
|
'auth_token': '123',
|
|
'project_id': '321'
|
|
}
|
|
})
|
|
|
|
# In case of no-adhoc action we should evaluate task parameters
|
|
# to see what we need.
|
|
task_spec = db_task.task_spec
|
|
task_spec['parameters'] = {
|
|
'url': "http://some/{$.openstack.project_id}/servers",
|
|
}
|
|
|
|
db_task.parameters = data_flow.evaluate_task_parameters(
|
|
db_task, db_task.in_context)
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertEqual("http://some/321/servers", action.url)
|
|
|
|
def test_get_ssh_action(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK))
|
|
|
|
db_task['task_spec'] = {
|
|
'name': 'my_task',
|
|
'action': 'std.ssh',
|
|
'parameters': {
|
|
'host': '10.0.0.1',
|
|
'cmd': 'ls -l',
|
|
'username': '$.ssh_username',
|
|
'password': '$.ssh_password'
|
|
}
|
|
}
|
|
|
|
db_task['parameters'] = {
|
|
'host': '10.0.0.1',
|
|
'cmd': 'ls -l',
|
|
'username': 'ubuntu',
|
|
'password': 'ubuntu_password'
|
|
}
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertEqual("ubuntu", action.username)
|
|
self.assertEqual("ubuntu_password", action.password)
|
|
self.assertEqual("ls -l", action.cmd)
|
|
self.assertEqual("10.0.0.1", action.host)
|
|
|
|
def test_adhoc_echo_action(self):
|
|
db_task = models.Task()
|
|
db_task.update(copy.copy(DB_TASK_ADHOC))
|
|
|
|
action_spec = db_task.action_spec
|
|
|
|
# With dic-like output formatter.
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertDictEqual({'res': 'Tango and Cash'}, action.run())
|
|
|
|
# With list-like output formatter.
|
|
action_spec['output'] = ['$', '$']
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertListEqual(['Tango and Cash', 'Tango and Cash'],
|
|
action.run())
|
|
|
|
# With single-object output formatter.
|
|
action_spec['output'] = "'{$}' is a cool movie!"
|
|
|
|
action = a_f.create_action(db_task)
|
|
|
|
self.assertEqual("'Tango and Cash' is a cool movie!", action.run())
|
|
|
|
def test_resolve_adhoc_action_name(self):
|
|
wb = spec_parser.get_workbook_spec_from_yaml(
|
|
base.get_resource('control_flow/one_sync_task.yaml'))
|
|
|
|
action_name = 'MyActions.concat'
|
|
|
|
action = a_f.resolve_adhoc_action_name(wb, action_name)
|
|
|
|
self.assertEqual('std.echo', action)
|
|
|
|
def test_convert_adhoc_action_params(self):
|
|
wb = spec_parser.get_workbook_spec_from_yaml(
|
|
base.get_resource('control_flow/one_sync_task.yaml'))
|
|
|
|
action_name = 'MyActions.concat'
|
|
params = {
|
|
'left': 'Stormin',
|
|
'right': 'Stanley'
|
|
}
|
|
|
|
parameters = a_f.convert_adhoc_action_params(wb,
|
|
action_name,
|
|
params)
|
|
|
|
self.assertEqual({'output': 'Stormin Stanley'}, parameters)
|
|
|
|
def test_convert_adhoc_action_result(self):
|
|
wb = spec_parser.get_workbook_spec_from_yaml(
|
|
base.get_resource('control_flow/one_sync_task.yaml'))
|
|
|
|
action_name = 'MyActions.concat'
|
|
result = {'output': 'Stormin Stanley'}
|
|
|
|
parameters = a_f.convert_adhoc_action_result(wb,
|
|
action_name,
|
|
result)
|
|
|
|
self.assertEqual({'string': 'Stormin Stanley'}, parameters)
|