Support workflow creation
Partial-Implements: blueprint workflow Change-Id: I19f4a82b373cbc6f4c4367648ec1c66a35ce4e2e
This commit is contained in:
parent
ed1295154d
commit
bf30391841
|
@ -13,6 +13,7 @@
|
|||
import pecan
|
||||
|
||||
from evoque.api.controllers.v1 import ticket
|
||||
from evoque.api.controllers.v1 import workflow
|
||||
|
||||
|
||||
class Controller(object):
|
||||
|
@ -20,6 +21,7 @@ class Controller(object):
|
|||
def __init__(self):
|
||||
self.sub_controllers = {
|
||||
"ticket": ticket.Controller(),
|
||||
"workflow": workflow.Controller(),
|
||||
}
|
||||
|
||||
for name, ctrl in self.sub_controllers.items():
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# 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 pecan
|
||||
from pecan import rest
|
||||
|
||||
|
||||
class Controller(rest.RestController):
|
||||
|
||||
@pecan.expose('json')
|
||||
def get(self):
|
||||
return {"version": "1.0.0"}
|
||||
|
||||
@pecan.expose('json')
|
||||
def post(self, **kwargs):
|
||||
workflow = pecan.request.workflow_api.workflow_create(
|
||||
name=kwargs['name'], wf_spec=kwargs['wf_spec'])
|
||||
|
||||
return workflow
|
|
@ -16,6 +16,7 @@ from oslo_config import cfg
|
|||
|
||||
from evoque.common import context
|
||||
from evoque.engine.ticket import api as ticket_api
|
||||
from evoque.engine.workflow import api as workflow_api
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('auth_uri', 'keystonemiddleware.auth_token',
|
||||
|
@ -79,3 +80,5 @@ class RPCHook(hooks.PecanHook):
|
|||
def before(self, state):
|
||||
state.request.ticket_api = ticket_api.API(
|
||||
context=state.request.context)
|
||||
state.request.workflow_api = workflow_api.API(
|
||||
context=state.request.context)
|
||||
|
|
|
@ -23,6 +23,7 @@ from evoque.common.i18n import _LI
|
|||
from evoque.common import rpc_service
|
||||
from evoque.common import service as evoque_service
|
||||
from evoque.engine.ticket import endpoint as ticket_endpoint
|
||||
from evoque.engine.workflow import endpoint as workflow_endpoint
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -36,6 +37,7 @@ def main():
|
|||
|
||||
endpoints = [
|
||||
ticket_endpoint.Handler(),
|
||||
workflow_endpoint.Handler(),
|
||||
]
|
||||
|
||||
server = rpc_service.Service.create("evoque-engine",
|
||||
|
|
|
@ -45,6 +45,11 @@ def ticket_get_all(context, filters=None, limit=None, marker=None,
|
|||
sort_key=None, sort_dir=None)
|
||||
|
||||
|
||||
# Workflows
|
||||
def workflow_create(context, values):
|
||||
return IMPL.workflow_create(context, values)
|
||||
|
||||
|
||||
# Utils
|
||||
def db_sync(engine, version=None):
|
||||
"""Migrate the database to `version` or the most recent version."""
|
||||
|
|
|
@ -89,6 +89,14 @@ def ticket_get_all(context, filters=None, limit=None, marker=None,
|
|||
sort_key, sort_dir, query)
|
||||
|
||||
|
||||
# Workflows
|
||||
def workflow_create(context, values):
|
||||
workflow_ref = models.Workflow()
|
||||
workflow_ref.update(values)
|
||||
workflow_ref.save(_session())
|
||||
return workflow_ref
|
||||
|
||||
|
||||
# Utils
|
||||
def db_sync(engine, version=None):
|
||||
"""Migrate the database to `version` or the most recent version."""
|
||||
|
|
|
@ -40,8 +40,29 @@ def upgrade(migrate_engine):
|
|||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
workflow = sqlalchemy.Table(
|
||||
'workflow', meta,
|
||||
sqlalchemy.Column('id', sqlalchemy.String(36),
|
||||
primary_key=True, nullable=False),
|
||||
sqlalchemy.Column('name', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('spec', types.MediumText()),
|
||||
sqlalchemy.Column('user', sqlalchemy.String(32)),
|
||||
sqlalchemy.Column('project', sqlalchemy.String(32)),
|
||||
sqlalchemy.Column('domain', sqlalchemy.String(32)),
|
||||
sqlalchemy.Column('user_id', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('project_id', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('domain_id', sqlalchemy.String(255)),
|
||||
sqlalchemy.Column('metadata', types.Dict),
|
||||
sqlalchemy.Column('created_at', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('updated_at', sqlalchemy.DateTime),
|
||||
sqlalchemy.Column('deleted_at', sqlalchemy.DateTime),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
tables = (
|
||||
ticket,
|
||||
workflow
|
||||
)
|
||||
|
||||
for index, table in enumerate(tables):
|
||||
|
|
|
@ -51,3 +51,21 @@ class Ticket(BASE, EvoqueBase):
|
|||
user_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
project_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
domain_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
|
||||
|
||||
class Workflow(BASE, EvoqueBase):
|
||||
"""Represents a workflow created by the Evoque engine."""
|
||||
|
||||
__tablename__ = 'workflow'
|
||||
|
||||
id = sqlalchemy.Column('id', sqlalchemy.String(36), primary_key=True,
|
||||
default=lambda: str(uuid.uuid4()))
|
||||
name = sqlalchemy.Column('name', sqlalchemy.String(255))
|
||||
spec = sqlalchemy.Column('spec', types.MediumText())
|
||||
|
||||
user = sqlalchemy.Column(sqlalchemy.String(32))
|
||||
project = sqlalchemy.Column(sqlalchemy.String(32))
|
||||
domain = sqlalchemy.Column(sqlalchemy.String(32))
|
||||
user_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
project_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
domain_id = sqlalchemy.Column(sqlalchemy.String(255))
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import json
|
||||
|
||||
from sqlalchemy.dialects import mysql
|
||||
from sqlalchemy import Text
|
||||
from sqlalchemy import types
|
||||
|
||||
|
||||
|
@ -32,3 +33,7 @@ class Dict(types.TypeDecorator):
|
|||
if value is None:
|
||||
return None
|
||||
return json.loads(value)
|
||||
|
||||
|
||||
def MediumText():
|
||||
return Text().with_variant(mysql.MEDIUMTEXT(), 'mysql')
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# 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 abc
|
||||
import six
|
||||
|
||||
from oslo_config import cfg
|
||||
from stevedore import driver
|
||||
|
||||
from evoque.common.i18n import _
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('workflow_engine',
|
||||
default='spiff',
|
||||
help=_('The Evoque workflow engine driver.')),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(OPTS)
|
||||
|
||||
|
||||
def get_workflow(engine=CONF.workflow_engine, namespace='evoque.workflow'):
|
||||
"""Get workflow driver and load it.
|
||||
|
||||
:param engine: workflow engine
|
||||
:param namespace: Namespace to use to look for drivers.
|
||||
"""
|
||||
|
||||
loaded_driver = driver.DriverManager(namespace, engine)
|
||||
return loaded_driver.driver()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Base(object):
|
||||
"""Base class for workflow operations."""
|
||||
|
||||
def __init__(self):
|
||||
super(Base, self).__init__()
|
||||
|
||||
@abc.abstractmethod
|
||||
def workflow_create(self, wf_spec):
|
||||
"""Create a workflow"""
|
|
@ -0,0 +1,22 @@
|
|||
# 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 evoque.common import rpc_service
|
||||
|
||||
|
||||
class API(rpc_service.API):
|
||||
def __init__(self, transport=None, context=None, topic=None):
|
||||
super(API, self).__init__(transport, context,
|
||||
topic="evoque-engine")
|
||||
|
||||
def workflow_create(self, name, wf_spec):
|
||||
return self._call('workflow_create', name=name, wf_spec=wf_spec)
|
|
@ -0,0 +1,25 @@
|
|||
# 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 evoque.engine import workflow
|
||||
|
||||
|
||||
class Handler(object):
|
||||
|
||||
def __init__(self):
|
||||
super(Handler, self).__init__()
|
||||
self.workflow_driver = workflow.get_workflow()
|
||||
|
||||
def workflow_create(self, context, name, wf_spec):
|
||||
workflow = self.workflow_driver.workflow_create(
|
||||
context, name, wf_spec)
|
||||
return {'workflow': workflow}
|
|
@ -0,0 +1,33 @@
|
|||
# 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 evoque.db import api as db_api
|
||||
from evoque.engine import workflow
|
||||
|
||||
|
||||
class Workflow(workflow.Base):
|
||||
"""Workflow engine powered by `SpiffWorkflow`
|
||||
|
||||
https://github.com/knipknap/SpiffWorkflow
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Workflow, self).__init__()
|
||||
|
||||
def workflow_create(self, context, name, wf_spec):
|
||||
"""Create a workflow"""
|
||||
values = {
|
||||
'name': name,
|
||||
'spec': wf_spec
|
||||
}
|
||||
workflow = db_api.workflow_create(context, values)
|
||||
return workflow
|
|
@ -52,5 +52,8 @@ def list_opts():
|
|||
cfg.StrOpt('host',
|
||||
default=socket.getfqdn(),
|
||||
help=_('The listen IP for the Evoque engine server.')),
|
||||
cfg.StrOpt('workflow_engine',
|
||||
default='spiff',
|
||||
help=_('The Evoque workflow engine driver.')),
|
||||
)),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue