Merge "Removing left v1 related stuff (resources, DSL specs)"

This commit is contained in:
Jenkins 2015-04-09 11:22:16 +00:00 committed by Gerrit Code Review
commit feeac06704
42 changed files with 80 additions and 1584 deletions

View File

@ -21,7 +21,6 @@ from mistral.actions import action_factory
from mistral.actions import generator_factory
from mistral.db.v2 import api as db_api
from mistral import exceptions as exc
from mistral import expressions as expr
from mistral.openstack.common import log as logging
from mistral.services import actions
from mistral import utils
@ -168,42 +167,3 @@ def _has_argument(action, attributes, argument_name):
def has_action_context(action, attributes):
return _has_argument(action, attributes, _ACTION_CTX_PARAM)
def resolve_adhoc_action_name(workbook, action_name):
action_spec = workbook.get_action(action_name)
if not action_spec:
msg = ('Ad-hoc action class is not registered '
'[workbook=%s, action=%s, action_spec=%s]' %
(workbook, action_name, action_spec))
raise exc.ActionException(msg)
base_cls = get_action_class(action_spec.clazz)
if not base_cls:
msg = ('Ad-hoc action base class is not registered '
'[workbook=%s, action=%s, base_class=%s]' %
(workbook, action_name, base_cls))
raise exc.ActionException(msg)
return action_spec.clazz
def convert_adhoc_action_params(workbook, action_name, params):
base_params = workbook.get_action(action_name).base_parameters
if not base_params:
return {}
return expr.evaluate_recursively(base_params, params)
def convert_adhoc_action_result(workbook, action_name, result):
transformer = workbook.get_action(action_name).output
if not transformer:
return result
# Use base action result as a context for evaluating expressions.
return expr.evaluate_recursively(transformer, result)

View File

@ -1,53 +0,0 @@
Namespaces:
MyRest:
class: std.mistral_http
base-parameters:
method: GET
headers:
X-Auth-Token: <% $.auth_token %>
actions:
async-action:
base-parameters:
url: http://some_host/service/action/execute
headers:
Content-Type: 'application/json'
parameters:
- input1
- input2
output:
Workflow:
tasks:
start-task:
action: MyRest.async-action
parameters:
input1: foo
input2: bar
on-success:
task-one
on-error:
- task-three
- task-four
on-finish:
task-two
task-one:
action: MyRest.async-action
on-success: task-two
task-two:
action: MyRest.async-action
task-three:
action: MyRest.async-action
task-four:
action: MyRest.async-action
Triggers:
my-cron:
type: periodic
tasks: start-task
parameters:
cron-pattern: "* * * * *"

View File

@ -1,7 +0,0 @@
Workflow:
tasks:
task1:
action: std.http
parameters:
method: GET
url: http://some_url

View File

@ -1,26 +0,0 @@
Namespaces:
MyRest:
class: std.mistral_http
base-parameters:
method: GET
headers:
X-Auth-Token: <% $.auth_token %>
actions:
create-vm:
base-parameters:
url: http://some_host/service/action/execute
headers:
Content-Type: 'application/json'
parameters:
- image_id
- flavor_id
output:
Workflow:
tasks:
create-vms:
action: MyRest.create-vm
parameters:
image_id: 1234
flavor_id: 42

View File

@ -1,17 +0,0 @@
Namespaces:
Nova:
actions:
create-vm:
class: std.http
base-parameters:
url: http://path_to_nova/url_for_create
output:
vm_id: <% $.base_output.server_id %>
Workflow:
tasks:
std_http_task:
action: std.http
parameters:
method: GET
url: http://some_url

View File

@ -1,20 +0,0 @@
Namespaces:
MyActions:
actions:
concat:
class: std.echo
base-parameters:
output: '<% $.left %> <% $.right %>'
parameters:
- left
- right
output:
string: <% $.output %>
Workflow:
tasks:
build_name:
action: MyActions.concat
parameters:
left: Stormin
right: Stanley

View File

@ -1,34 +0,0 @@
Namespaces:
MyActions:
actions:
concat:
class: std.echo
base-parameters:
output: '<% $.left %> <% $.right %>'
parameters:
- left
- right
output:
string: <% $.output %>
start:
class: std.echo
base-parameters:
output: 'Starting...'
output:
info: <% $.output %>
Workflow:
tasks:
startup:
action: MyActions.start
build_name:
action: MyActions.concat
parameters:
left: Stormin
right: Stanley
greet:
requires: [startup, build_name]
action: MyActions.concat
parameters:
left: Greetings
right: <% $.string %>

View File

@ -1,62 +0,0 @@
Namespaces:
MyService:
# These ad-hoc actions based on std.echo have parameters only for test
# purposes. In practice, it's more convenient just to use std.echo and
# specify parameter 'output'.
actions:
concat:
class: std.echo
base-parameters:
output: '<% $.left %> <% $.right %>'
parameters:
- left
- right
output:
string: <% $ %>
Workflow:
# context = {
# 'person': {
# 'first_name': 'John',
# 'last_name': 'Doe',
# 'address': {
# 'street': '124352 Broadway Street',
# 'city': 'Gloomington',
# 'country': 'USA'
# }
# }
# }
tasks:
build_full_name:
action: MyService.concat
parameters:
left: <% $.person.first_name %>
right: <% $.person.last_name %>
publish:
full_name: <% $.string %>
build_greeting:
requires: [build_full_name]
action: MyService.concat
parameters:
left: Dear
right: <% $.full_name %>
publish:
greeting: <% $.string %>
build_address:
requires: [build_full_name]
action: MyService.concat
parameters:
left: To
right: <% $.full_name %>
publish:
address: <% $.string %>
send_greeting:
requires: [build_address, build_greeting]
action: MyService.concat
parameters:
left: '<% $.address %>.'
right: '<% $.greeting %>,..'

View File

@ -1,67 +0,0 @@
Namespaces:
MyService:
# These ad-hoc actions based on std.echo have parameters only for test
# purposes. In practice, it's more convenient just to use std.echo and
# specify parameter 'output'.
actions:
build_full_name:
class: std.echo
base-parameters:
output: '<% $.first_name %> <% $.last_name %>'
parameters:
- first_name
- last_name
output:
full_name: <% $ %>
build_greeting:
class: std.echo
base-parameters:
output: Cheers!
output:
greeting: <% $ %>
send_greeting:
class: std.echo
base-parameters:
output: True
parameters:
- f_name
- greet_msg
output:
greeting_sent: <% $ %>
Workflow:
# context = {
# 'person': {
# 'first_name': 'John',
# 'last_name': 'Doe',
# 'address': {
# 'street': '124352 Broadway Street',
# 'city': 'Gloomington',
# 'country': 'USA'
# }
# }
# }
tasks:
build_full_name:
action: MyService.build_full_name
parameters:
first_name: <% $.person.first_name %>
last_name: <% $.person.last_name %>
publish:
f_name: <% $.full_name %>
build_greeting:
requires: [build_full_name]
action: MyService.build_greeting
publish:
greet_msg: <% $.greeting %>
send_greeting:
requires: [build_full_name, build_greeting]
action: MyService.send_greeting
parameters:
f_name: <% $.f_name %>
greet_msg: <% $.greet_msg %>

View File

@ -1,72 +0,0 @@
Namespaces:
MyService:
# These ad-hoc actions based on std.echo have parameters only for test
# purposes. In practice, it's more convenient just to use std.echo and
# specify parameter 'output'.
actions:
build_full_name:
class: std.echo
base-parameters:
output: '<% $.first_name %> <% $.last_name %>'
parameters:
- first_name
- last_name
output:
full_name: <% $ %>
build_greeting:
class: std.echo
base-parameters:
output: 'Hello, <% $.full_name %>!'
parameters:
- full_name
output:
greeting: <% $ %>
send_greeting:
class: std.echo
base-parameters:
output: True
parameters:
- greeting
output:
greeting_sent: <% $ %>
Workflow:
# context = {
# 'person': {
# 'first_name': 'John',
# 'last_name': 'Doe',
# 'address': {
# 'street': '124352 Broadway Street',
# 'city': 'Gloomington',
# 'country': 'USA'
# }
# }
# }
tasks:
build_full_name:
action: MyService.build_full_name
parameters:
first_name: <% $.person.first_name %>
last_name: <% $.person.last_name %>
publish:
f_name: <% $.full_name %>
on-success: build_greeting
build_greeting:
action: MyService.build_greeting
parameters:
full_name: <% $.f_name %>
publish:
greet_msg: <% $.greeting %>
on-success: send_greeting
send_greeting:
action: MyService.send_greeting
parameters:
greeting: <% $.task.build_greeting.greeting %>
publish:
sent: <% $.greeting_sent %>

View File

@ -1,53 +0,0 @@
Namespaces:
MyService:
actions:
# These ad-hoc actions based on std.echo have parameters only for test
# purposes. In practice, it's more convenient just to use std.echo and
# specify parameter 'output'.
build_full_name:
class: std.echo
base-parameters:
output: '<% $.first_name %> <% $.last_name %>'
parameters:
- first_name
- last_name
output:
full_name: <% $ %>
build_greeting:
class: std.echo
base-parameters:
output: 'Hello, <% $.full_name %>!'
parameters:
- full_name
output:
greeting: <% $ %>
Workflow:
# context = {
# 'person': {
# 'first_name': 'John',
# 'last_name': 'Doe',
# 'address': {
# 'street': '124352 Broadway Street',
# 'city': 'Gloomington',
# 'country': 'USA'
# }
# }
# }
tasks:
build_full_name:
action: MyService.build_full_name
parameters:
first_name: <% $.person.first_name %>
last_name: <% $.person.last_name %>
publish:
f_name: <% $.full_name %>
build_greeting:
requires: [build_full_name]
action: MyService.build_greeting
parameters:
full_name: <% $.f_name %>

View File

@ -1,56 +0,0 @@
Namespaces:
MyService:
# These ad-hoc actions based on std.echo have parameters only for test
# purposes. In practice, it's more convenient just to use std.echo and
# specify parameter 'output'.
actions:
build_full_name:
class: std.echo
base-parameters:
output: '<% $.first_name %> <% $.last_name %>'
parameters:
- first_name
- last_name
output:
full_name: <% $ %>
build_greeting:
class: std.echo
base-parameters:
output: 'Hello, <% $.full_name %>!'
parameters:
- full_name
output:
greeting:
greet_message: <% $ %>
Workflow:
# context = {
# 'person': {
# 'first_name': 'John',
# 'last_name': 'Doe',
# 'address': {
# 'street': '124352 Broadway Street',
# 'city': 'Gloomington',
# 'country': 'USA'
# }
# }
# }
tasks:
build_full_name:
action: MyService.build_full_name
parameters:
first_name: <% $.person.first_name %>
last_name: <% $.person.last_name %>
publish:
f_name: <% $.full_name %>
on-success: build_greeting
build_greeting:
action: MyService.build_greeting
parameters:
full_name: <% $.f_name %>
publish:
greet_msg: <% $.greeting %>

View File

@ -1,4 +0,0 @@
Workflow:
tasks:
glance_image_list:
action: glance.images_list

View File

@ -1,15 +0,0 @@
Workflow:
tasks:
image_list:
action: glance.images_list
publish:
image_id: <% $[0].id %>
on-success: image_get
image_get:
action: glance.images_get
parameters:
image_id: <% $.image_id %>
publish:
image_id: <% $.id %>
image_name: <% $.name %>

View File

@ -1,4 +0,0 @@
Workflow:
tasks:
heat_stack_list:
action: heat.stacks_list

View File

@ -1,4 +0,0 @@
Workflow:
tasks:
keystone_user_list:
action: keystone.users_list

View File

@ -1,6 +0,0 @@
Workflow:
tasks:
get_some_endpoint:
action: keystone.service_catalog_get_data
publish:
endpoint_url: <% $[0].endpoints[0].url %>

View File

@ -1,7 +0,0 @@
Workflow:
tasks:
nova_server_findall:
action: nova.servers_findall
parameters:
status: ACTIVE
tenant_id: 8e44eb2ce32

View File

@ -1,31 +0,0 @@
Workflow:
tasks:
server_create:
action: nova.servers_create
parameters:
name: <% $.server_name %>
image: <% $.image_ref %>
flavor: <% $.flavor_ref %>
publish:
server_id: <% $.id %>
on-success: check_server_exists
check_server_exists:
action: nova.servers_get
parameters:
server: <% $.server_id %>
publish:
server_exists: True
on-success:
wait_instance
wait_instance:
action: nova.servers_find
retry:
delay: 5
count: 15
parameters:
id: <% $.server_id %>
status: 'ACTIVE'
publish:
instance_id: <% $.id %>

View File

@ -1,4 +0,0 @@
Workflow:
tasks:
neutron_list_networks:
action: neutron.list_networks

View File

@ -1,20 +0,0 @@
Namespaces:
MyService:
actions:
some-action:
class: std.mistral_http
base-parameters:
url: http://path_to_service/action_url
method: GET
output:
output: <% $ %>
Workflow:
tasks:
delay_retry_task:
action: MyService.some-action
retry:
count: 2
delay: 0.1
publish:
rt_output: output

View File

@ -1,19 +0,0 @@
Namespaces:
MyService:
actions:
some-action:
class: std.mistral_http
base-parameters:
url: http://path_to_service/action_url
method: GET
output:
output: <% $ %>
Workflow:
tasks:
retry_task:
action: MyService.some-action
retry:
count: 5
publish:
rt_output: <% $.output %>

View File

@ -1,18 +0,0 @@
Namespaces:
MyService:
actions:
sync-action:
class: std.echo
base-parameters:
output: Cheers!
output:
greeting: <% $ %>
Workflow:
tasks:
sync-task:
action: MyService.sync-action
retry:
count: 5
publish:
st_output: <% $.greeting %>

View File

@ -1,26 +0,0 @@
Namespaces:
MyService:
actions:
some-action:
class: std.mistral_http
base-parameters:
url: http://path_to_service/action_url
method: GET
output:
output: <% $ %>
Workflow:
tasks:
no_retry_task:
action: MyService.some-action
publish:
n_rt_output: <% $.output %>
on-success: delay_retry_task
delay_retry_task:
action: MyService.some-action
retry:
count: 2
delay: 0.1
publish:
rt_output: output

View File

@ -1,63 +0,0 @@
Namespaces:
Nova:
class: std.mistral_http
base-parameters:
method: GET
headers:
X-Auth-Token: <% $.auth_token %>
actions:
create-vm:
base-parameters:
url: http://some_host/service/action/execute
headers:
Content-Type: 'application/json'
parameters:
- image_id
- flavor_id
Echo:
actions:
concat:
class: std.echo
base-parameters:
output: '<% $.left %> <% $.right %>'
parameters:
- left
- right
output:
string: <% $ %>
Workflow:
tasks:
task1:
action: Nova.create-vm
parameters:
image_id: 1234
flavor_id: 42
on-error: task2
task2:
action: Echo.concat
parameters:
left: workflow
right: is
publish:
result2: <% $.string %>
on-finish: task3
task3:
action: Nova.create-vm
parameters:
image_id: 1234
flavor_id: 42
on-finish: task4
task4:
action: Echo.concat
parameters:
left: <% $.result2 %>
right: complete!
publish:
result4: <% $.string %>

View File

@ -1,40 +0,0 @@
Namespaces:
MyService:
actions:
concat:
class: std.echo
base-parameters:
output: '<% $.left %> <% $.right %>'
parameters:
- left
- right
output:
string: <% $ %>
Workflow:
tasks:
task1:
action: MyService.concat
parameters:
left: workflow
right: is
publish:
result1: <% $.string %>
task2:
requires: [task1]
action: MyService.concat
parameters:
left: <% $.result1 %>
right: complete
publish:
result2: <% $.string %>
on-success: task3
task3:
action: MyService.concat
parameters:
left: <% $.result2 %>
right: '!'
publish:
result3: <% $.string %>

View File

@ -1,29 +0,0 @@
Workflow:
tasks:
task:
requires: [atask, pretask]
action: std.echo
parameters:
output: some
btask:
requires: [ztask]
action: std.echo
parameters:
output: some
ztask:
action: std.echo
parameters:
output: some
atask:
action: std.echo
parameters:
output: some
ctask:
action: std.echo
parameters:
output: some
pretask:
requires: [btask, ztask]
action: std.echo
parameters:
output: some

View File

@ -1,101 +0,0 @@
Namespaces:
MyRest:
class: std.mistral_http
base-parameters:
method: GET
headers:
X-Auth-Token: $.auth_token
actions:
create-vm:
base-parameters:
url: http://some_host/service/action/execute
headers:
Content-Type: 'application/json'
parameters:
- image_id
- flavor_id
output:
backup-vm:
base-parameters:
url: http://some_host/url_for_backup
parameters:
- server_id
attach-volume:
base-parameters:
url: /url_for_attach
parameters:
- size
- mnt_path
format-volume:
base-parameters:
url: /url_for_format
parameters:
- server_id
Nova:
actions:
create-vm:
class: std.http
base-parameters:
url: http://path_to_nova/url_for_create
parameters:
- image_id
- flavor_id
output:
vm_id: $.base_output.server_id
Workflow:
tasks:
create-vms:
action: MyRest.create-vm
parameters:
image_id: 1234
flavor_id: 42
attach-volumes:
requires: [create-vms]
action: MyRest.attach-volume
parameters:
size:
mnt_path:
format-volumes:
requires: [attach-volumes]
action: MyRest.format-volume
parameters:
server_id:
backup-vms:
requires: [create-vms]
action: MyRest.backup-vm
parameters:
server_id:
create-vm-nova:
action: Nova.create-vm
parameters:
image_id: 1234
flavor_id: 2
test_subsequent:
action: MyRest.backup-vm
parameters:
server_id: 1
on-success:
attach-volumes
on-error:
- backup-vms: $.status != 'OK'
- attach-volumes
on-finish:
create-vms
Triggers:
create-vms:
type: periodic
tasks: create-vms
parameters:
cron-pattern: "* * * * *"

View File

@ -4,8 +4,9 @@ Namespaces:
hello:
class: std.echo
base-parameters:
output: Hello!
output: Hello!
Workflow:
tasks:
hello:
action: Greetings.hello
action: Greetings.hello

View File

@ -1,154 +0,0 @@
# -*- 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.
from mistral.actions import std_actions as std
from mistral.openstack.common import log as logging
from mistral.services import action_manager as a_m
from mistral.tests import base
from mistral.workbook import parser as spec_parser
# TODO(rakhmerov): Deprecated. Remove it once engine v1 is gone.
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.DbTestCase):
def test_register_standard_actions(self):
action_list = a_m.get_registered_actions()
self._assert_single_item(action_list, name="std.echo")
self._assert_single_item(action_list, name="std.email")
self._assert_single_item(action_list, name="std.http")
self._assert_single_item(action_list, name="std.mistral_http")
self._assert_single_item(action_list, name="std.ssh")
self._assert_single_item(action_list, name="std.javascript")
self._assert_single_item(action_list, name="nova.servers_get")
self._assert_single_item(action_list, name="nova.volumes_delete")
self._assert_single_item(action_list, name="keystone.users_list")
self._assert_single_item(action_list, name="keystone.trusts_create")
self._assert_single_item(action_list, name="glance.images_list")
self._assert_single_item(action_list, name="glance.images_delete")
def test_get_action_class(self):
self.assertEqual(std.EchoAction, a_m.get_action_class("std.echo"))
self.assertEqual(std.HTTPAction, a_m.get_action_class("std.http"))
self.assertEqual(std.MistralHTTPAction,
a_m.get_action_class("std.mistral_http"))
self.assertEqual(std.SendEmailAction,
a_m.get_action_class("std.email"))
self.assertEqual(std.JavaScriptAction,
a_m.get_action_class("std.javascript"))
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_m.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_m.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_m.convert_adhoc_action_result(wb,
action_name,
result)
self.assertEqual({'string': 'Stormin Stanley'}, parameters)

View File

@ -0,0 +1,60 @@
# -*- 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.
from mistral.actions import std_actions as std
from mistral.openstack.common import log as logging
from mistral.services import action_manager as a_m
from mistral.tests import base
LOG = logging.getLogger(__name__)
class ActionFactoryTest(base.DbTestCase):
def test_register_standard_actions(self):
action_list = a_m.get_registered_actions()
self._assert_single_item(action_list, name="std.echo")
self._assert_single_item(action_list, name="std.email")
self._assert_single_item(action_list, name="std.http")
self._assert_single_item(action_list, name="std.mistral_http")
self._assert_single_item(action_list, name="std.ssh")
self._assert_single_item(action_list, name="std.javascript")
self._assert_single_item(action_list, name="nova.servers_get")
self._assert_single_item(action_list, name="nova.volumes_delete")
self._assert_single_item(action_list, name="keystone.users_list")
self._assert_single_item(action_list, name="keystone.trusts_create")
self._assert_single_item(action_list, name="glance.images_list")
self._assert_single_item(action_list, name="glance.images_delete")
def test_get_action_class(self):
self.assertEqual(std.EchoAction, a_m.get_action_class("std.echo"))
self.assertEqual(std.HTTPAction, a_m.get_action_class("std.http"))
self.assertEqual(
std.MistralHTTPAction,
a_m.get_action_class("std.mistral_http")
)
self.assertEqual(
std.SendEmailAction,
a_m.get_action_class("std.email")
)
self.assertEqual(
std.JavaScriptAction,
a_m.get_action_class("std.javascript")
)

View File

@ -54,7 +54,7 @@ workflows:
RESUME_WORKBOOK_REVERSE = """
---
version: 2.0
version: '2.0'
name: resume_reverse

View File

@ -1,112 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 - 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.
from mistral.tests import base
from mistral.workbook import parser
SIMPLE_WORKBOOK = """
Workflow:
tasks:
create-vms:
action: MyRest.create-vm
"""
class DSLv1ModelTest(base.BaseTest):
def setUp(self):
super(DSLv1ModelTest, self).setUp()
# TODO(rakhmerov): Need to have a dedicated resource.
self.doc = base.get_resource("test_rest.yaml")
def test_load_dsl(self):
wb = parser.get_workbook_spec_from_yaml(self.doc)
self.assertEqual(wb.workflow.tasks.items, wb.tasks.items)
self.assertEqual(wb.tasks.get("create-vms").name, "create-vms")
self.assertEqual(4, len(wb.namespaces.get("MyRest").actions))
def test_tasks(self):
wb = parser.get_workbook_spec_from_yaml(self.doc)
self.assertEqual(len(wb.tasks), 6)
attach_volumes = wb.tasks.get("attach-volumes")
self.assertEqual(attach_volumes.get_action_namespace(), "MyRest")
t_parameters = {"image_id": 1234, "flavor_id": 2}
create_vm_nova = wb.tasks.get("create-vm-nova")
self.assertEqual(create_vm_nova.parameters, t_parameters)
attach_requires = {"create-vms": ''}
self.assertEqual(attach_volumes.requires, attach_requires)
subsequent = wb.tasks.get("test_subsequent")
subseq_success = subsequent.get_on_success()
subseq_error = subsequent.get_on_error()
subseq_finish = subsequent.get_on_finish()
self.assertEqual(subseq_success, {"attach-volumes": ''})
self.assertEqual(subseq_error, {"backup-vms": "$.status != 'OK'",
"attach-volumes": ''})
self.assertEqual(subseq_finish, {"create-vms": ''})
def test_actions(self):
wb = parser.get_workbook_spec_from_yaml(self.doc)
actions = wb.namespaces.get("MyRest").actions
self.assertEqual(len(actions), 4)
create_vm = actions.get("create-vm")
base_params = create_vm.base_parameters
self.assertEqual('std.mistral_http', create_vm.clazz)
self.assertIn('method', base_params)
self.assertIn('headers', base_params)
self.assertEqual('$.auth_token',
base_params['headers']['X-Auth-Token'])
self.assertEqual('application/json',
base_params['headers']['Content-Type'])
action = wb.get_action("MyRest.create-vm")
self.assertIsNotNone(action)
self.assertEqual("create-vm", action.name)
self.assertEqual("MyRest", action.namespace)
def test_namespaces(self):
wb = parser.get_workbook_spec_from_yaml(self.doc)
self.assertEqual(len(wb.namespaces), 2)
nova_namespace = wb.namespaces.get("Nova")
self.assertEqual(1, len(nova_namespace.actions))
def test_workbook_without_namespaces(self):
parser.get_workbook_spec_from_yaml(SIMPLE_WORKBOOK)
def test_triggers(self):
wb = parser.get_workbook_spec_from_yaml(self.doc)
self.assertEqual(len(wb.get_triggers()), 1)

View File

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013 - 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.
# TODO(rakhmerov): Can we just extend dsl_specs_v1.py and remove this one?.
SAMPLE_TASK_SPEC = {
'action': 'MyRest:create-vm',
'name': 'create-vms',
'on-success': ["format-volumes", {'task1': 'expression'}, {'task2': ''}],
'on-finish': "attach-volumes",
'on-error': ["task1", "task2"]
}
from mistral.tests import base
from mistral.workbook.v1 import tasks
class GetOnStateTest(base.BaseTest):
def setUp(self):
super(GetOnStateTest, self).setUp()
self.task = tasks.TaskSpec(SAMPLE_TASK_SPEC)
def test_state_finish(self):
on_finish = self.task.get_on_finish()
self.assertIsInstance(on_finish, dict)
self.assertIn("attach-volumes", on_finish)
def test_state_error(self):
on_error = self.task.get_on_error()
self.assertIsInstance(on_error, dict)
self.assertEqual(len(on_error), 2)
self.assertIn("task1", on_error)
def test_state_success(self):
on_success = self.task.get_on_success()
self.assertIsInstance(on_success, dict)
self.assertEqual(len(on_success), 3)
self.assertIn("task1", on_success)
self.assertIsNotNone(on_success["task1"])

View File

@ -17,20 +17,14 @@ import yaml
from yaml import error
from mistral import exceptions as exc
from mistral.workbook.v1 import actions as actions_v1
from mistral.workbook.v1 import namespaces as ns_v1
from mistral.workbook.v1 import tasks as tasks_v1
from mistral.workbook.v1 import workbook as wb_v1
from mistral.workbook.v1 import workflow as wf_v1
from mistral.workbook.v2 import actions as actions_v2
from mistral.workbook.v2 import tasks as tasks_v2
from mistral.workbook.v2 import workbook as wb_v2
from mistral.workbook.v2 import workflows as wf_v2
V1_0 = '1.0'
V2_0 = '2.0'
ALL_VERSIONS = [V1_0, V2_0]
ALL_VERSIONS = [V2_0]
def parse_yaml(text):
@ -49,8 +43,8 @@ def parse_yaml(text):
def _get_spec_version(spec_dict):
# If version is not specified it will '1.0' by default.
ver = V1_0
# If version is not specified it will '2.0' by default.
ver = V2_0
if 'version' in spec_dict:
ver = spec_dict['version']
@ -66,31 +60,22 @@ def _get_spec_version(spec_dict):
def get_workbook_spec(spec_dict):
if _get_spec_version(spec_dict) == V1_0:
return wb_v1.WorkbookSpec(spec_dict)
else:
if _get_spec_version(spec_dict) == V2_0:
return wb_v2.WorkbookSpec(spec_dict)
return None
def get_workbook_spec_from_yaml(text):
spec_dict = parse_yaml(text)
return get_workbook_spec(spec_dict)
def get_namespace_spec(spec_dict):
if _get_spec_version(spec_dict) == V1_0:
return ns_v1.NamespaceSpec(spec_dict)
else:
return None
return get_workbook_spec(parse_yaml(text))
def get_action_spec(spec_dict):
if _get_spec_version(spec_dict) == V1_0:
return actions_v1.ActionSpec(spec_dict)
else:
if _get_spec_version(spec_dict) == V2_0:
return actions_v2.ActionSpec(spec_dict)
return None
def get_action_spec_from_yaml(text, action_name):
spec_dict = parse_yaml(text)
@ -105,17 +90,15 @@ def get_action_list_spec(spec_dict):
def get_action_list_spec_from_yaml(text):
spec_dict = parse_yaml(text)
return get_action_list_spec(spec_dict)
return get_action_list_spec(parse_yaml(text))
def get_workflow_spec(spec_dict):
if _get_spec_version(spec_dict) == V1_0:
return wf_v1.WorkflowSpec(spec_dict)
else:
if _get_spec_version(spec_dict) == V2_0:
return wf_v2.WorkflowSpec(spec_dict)
return None
def get_workflow_list_spec(spec_dict):
return wf_v2.WorkflowListSpec(spec_dict)
@ -134,9 +117,7 @@ def get_workflow_list_spec_from_yaml(text):
def get_task_spec(spec_dict):
if _get_spec_version(spec_dict) == V1_0:
return tasks_v1.TaskSpec(spec_dict)
else:
if _get_spec_version(spec_dict) == V2_0:
workflow_type = spec_dict.get('type')
if workflow_type == 'direct':
@ -146,7 +127,4 @@ def get_task_spec(spec_dict):
else:
raise Exception('Unsupported workflow type "%s".' % workflow_type)
def get_trigger_spec(spec_dict):
# TODO(rakhmerov): Implement.
pass
return None

View File

@ -1,47 +0,0 @@
# 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.
from mistral.workbook import base
class ActionSpec(base.BaseSpec):
# See http://json-schema.org
_schema = {
"type": "object",
"properties": {
"version": {"type": "string"},
"name": {"type": "string"},
"class": {"type": "string"},
"namespace": {"type": "string"},
"base-parameters": {"type": "object"},
"parameters": {"type": "array"},
"output": {"type": ["string", "object", "array", "null"]},
},
"required": ["name", "class", "namespace"],
"additionalProperties": False
}
def __init__(self, data):
super(ActionSpec, self).__init__(data)
self.name = data['name']
self.clazz = data['class']
self.namespace = data['namespace']
self.base_parameters = data.get('base-parameters', {})
self.parameters = data.get('parameters', {})
self.output = data.get('output')
class ActionSpecList(base.BaseSpecList):
item_class = ActionSpec

View File

@ -1,68 +0,0 @@
# 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.
from mistral import utils
from mistral.workbook import base
from mistral.workbook.v1 import actions
def merge_base_parameters(action, ns_base_parameters):
if not ns_base_parameters:
return
if 'base-parameters' not in action:
action['base-parameters'] = ns_base_parameters
return
action_base_parameters = action['base-parameters']
utils.merge_dicts(action_base_parameters, ns_base_parameters)
class NamespaceSpec(base.BaseSpec):
# See http://json-schema.org
_schema = {
"type": "object",
"properties": {
"version": {"type": "string"},
"name": {"type": "string"},
"class": {"type": ["string", "null"]},
"base-parameters": {"type": ["object", "null"]},
"actions": {"type": "object"}
},
"required": ["name", "actions"],
"additionalProperties": False
}
def __init__(self, data):
super(NamespaceSpec, self).__init__(data)
self.name = data['name']
self.clazz = data.get('class')
self.base_parameters = data.get('base-parameters')
self.parameters = data.get('parameters')
for _, action in data['actions'].iteritems():
action['namespace'] = self.name
if 'class' not in action:
action['class'] = self.clazz
merge_base_parameters(action, self.base_parameters)
self.actions = actions.ActionSpecList(data['actions'])
class NamespaceSpecList(base.BaseSpecList):
item_class = NamespaceSpec

View File

@ -1,102 +0,0 @@
# 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.
from mistral.workbook import base
class TaskSpec(base.BaseSpec):
# See http://json-schema.org
_schema = {
"type": "object",
"properties": {
"version": {"type": "string"},
"name": {"type": "string"},
"action": {"type": ["string", "null"]},
"parameters": {"type": ["object", "null"]},
"publish": {"type": ["object", "null"]},
"retry": {"type": ["object", "null"]},
"requires": {"type": ["object", "string", "array", "null"]},
"on-finish": {"type": ["string", "array", "null"]},
"on-success": {"type": ["string", "array", "null"]},
"on-error": {"type": ["string", "array", "null"]}
},
"required": ["name", "action"],
"additionalProperties": False
}
def __init__(self, data):
super(TaskSpec, self).__init__(data)
self._prepare(data)
self.requires = data.get('requires')
self.action = data['action']
self.name = data['name']
self.parameters = data.get('parameters', {})
def _prepare(self, task):
if task:
req = task.get("requires", {})
if req and isinstance(req, list):
task["requires"] = dict(zip(req, [''] * len(req)))
elif isinstance(req, dict):
task['requires'] = req
def get_property(self, property_name, default=None):
return self._data.get(property_name, default)
def get_requires(self):
return self._as_dict('requires').keys()
def get_on_error(self):
return self._as_dict("on-error")
def get_on_success(self):
return self._as_dict("on-success")
def get_on_finish(self):
return self._as_dict("on-finish")
def get_action_namespace(self):
return self.action.split('.')[0]
def get_action_name(self):
return self.action.split('.')[1]
def get_full_action_name(self):
return self.action
def is_retry_task(self):
return self.get_property("retry") is not None
def get_retry_parameters(self):
iterations = 0
break_on = None
delay = 0
retry = self.get_property("retry")
if retry:
if "count" in retry:
iterations = retry["count"]
if "break-on" in retry:
break_on = retry["break-on"]
if "delay" in retry:
delay = retry["delay"]
return iterations, break_on, delay
class TaskSpecList(base.BaseSpecList):
item_class = TaskSpec

View File

@ -1,73 +0,0 @@
# 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.
from mistral.workbook import base
from mistral.workbook.v1 import namespaces
from mistral.workbook.v1 import workflow
class WorkbookSpec(base.BaseSpec):
# See http://json-schema.org
_schema = {
"type": "object",
"properties": {
"Namespaces": {"type": "object"},
"Workflow": {"type": "object"},
"Triggers": {"type": "object"}
},
"required": ["Workflow"],
"additionalProperties": False
}
def __init__(self, doc):
super(WorkbookSpec, self).__init__(doc)
self.namespaces = self._spec_property('Namespaces',
namespaces.NamespaceSpecList)
self.workflow = self._spec_property('Workflow', workflow.WorkflowSpec)
self.tasks = self.workflow.tasks
def get_triggers(self):
triggers_from_data = self._data.get("Triggers", None)
if not triggers_from_data:
return []
triggers = []
for name in triggers_from_data:
trigger_dict = {'name': name}
trigger_dict.update(triggers_from_data[name])
triggers.append(trigger_dict)
return triggers
def get_action(self, full_action_name):
if not self.namespaces:
return None
if full_action_name.find(".") == -1:
return {}
ns_name = full_action_name.split('.')[0]
action_name = full_action_name.split('.')[1]
if ns_name in self.namespaces.item_keys():
return self.namespaces[ns_name].actions.get(action_name)
def get_actions(self, namespace_name):
return self.namespaces.get(namespace_name).actions
def get_trigger_task_name(self, trigger_name):
trigger = self._data["Triggers"].get(trigger_name)
return trigger.get('tasks') if trigger else ""

View File

@ -1,33 +0,0 @@
# 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.
from mistral.workbook import base
from mistral.workbook.v1 import tasks
class WorkflowSpec(base.BaseSpec):
# See http://json-schema.org
_schema = {
"type": "object",
"properties": {
"tasks": {"type": "object"},
},
"required": ["tasks"],
"additionalProperties": False
}
def __init__(self, workflow):
super(WorkflowSpec, self).__init__(workflow)
self.tasks = tasks.TaskSpecList(workflow['tasks'])