In order to rework some of the persistence layer I would like to move around some of the files first, keeping job specifics in a jobs folder. Having some of the items which are root level taskflow items (flow, task) be at the root of the hiearchy. Also for now until the celery work is commited move that, since it doesn't make sense in backends anyway. Change-Id: If6c149710b40f70d4ec69ee8e523defe8f5e766d
454 lines
14 KiB
Python
454 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
|
|
# Copyright (C) 2013 Rackspace Hosting All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""Implementation of in-memory backend."""
|
|
|
|
import logging
|
|
|
|
from taskflow import exceptions as exception
|
|
from taskflow.persistence.backends.memory import memory
|
|
from taskflow.persistence import flowdetail
|
|
from taskflow.persistence import logbook
|
|
from taskflow.persistence import taskdetail
|
|
from taskflow.utils import LockingDict
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
logbooks = LockingDict()
|
|
flowdetails = LockingDict()
|
|
taskdetails = LockingDict()
|
|
|
|
|
|
"""
|
|
LOGBOOK
|
|
"""
|
|
|
|
|
|
def logbook_create(name, lb_id=None):
|
|
"""Creates a new LogBook model with matching lb_id"""
|
|
# Create the LogBook model
|
|
lb = memory.MemoryLogBook(name, lb_id)
|
|
|
|
# Store it in the LockingDict for LogBooks
|
|
logbooks[lb_id] = lb
|
|
|
|
|
|
def logbook_destroy(lb_id):
|
|
"""Deletes the LogBook model with matching lb_id"""
|
|
# Try deleting the LogBook
|
|
try:
|
|
del logbooks[lb_id]
|
|
# Raise a NotFound error if the LogBook doesn't exist
|
|
except KeyError:
|
|
raise exception.NotFound("No Logbook found with id "
|
|
"%s." % (lb_id,))
|
|
|
|
|
|
def logbook_save(lb):
|
|
"""Saves a generic LogBook object to the db"""
|
|
# Create a LogBook model if one doesn't exist
|
|
if not _logbook_exists(lb.uuid):
|
|
logbook_create(lb.name, lb.uuid)
|
|
|
|
# Get a copy of the LogBook model in the LockingDict
|
|
ld_lb = logbook_get(lb.uuid)
|
|
|
|
for fd in lb:
|
|
# Save each FlowDetail the LogBook to save has
|
|
fd.save()
|
|
|
|
# Add the FlowDetail to the LogBook model if not already there
|
|
if fd not in ld_lb:
|
|
logbook_add_flow_detail(lb.uuid, fd.uuid)
|
|
|
|
|
|
def logbook_delete(lb):
|
|
"""Deletes a LogBook from db based on a generic type"""
|
|
# Try to get the LogBook
|
|
try:
|
|
ld_lb = logbooks[lb.uuid]
|
|
# Raise a NotFound exception if the LogBook cannot be found
|
|
except KeyError:
|
|
raise exception.NotFound("No Logbook found with id "
|
|
"%s." % (lb.uuid,))
|
|
|
|
# Raise an error if the LogBook still has FlowDetails
|
|
if len(ld_lb):
|
|
raise exception.Error("Logbook <%s> still has "
|
|
"dependents." % (lb.uuid,))
|
|
# Destroy the LogBook model if it is safe
|
|
else:
|
|
logbook_destroy(lb.uuid)
|
|
|
|
|
|
def logbook_get(lb_id):
|
|
"""Gets a LogBook with matching lb_id, if it exists"""
|
|
# Try to get the LogBook
|
|
try:
|
|
ld_lb = logbooks[lb_id]
|
|
# Raise a NotFound exception if the LogBook is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No Logbook found with id "
|
|
"%s." % (lb_id,))
|
|
|
|
# Acquire a read lock on the LogBook
|
|
with ld_lb.acquire_lock(read=True):
|
|
# Create a generic type LogBook to return
|
|
retVal = logbook.LogBook(ld_lb.name, ld_lb.uuid)
|
|
|
|
# Attach the appropriate generic FlowDetails to the generic LogBook
|
|
for fd in ld_lb:
|
|
retVal.add_flow_detail(flowdetail_get(fd.uuid))
|
|
|
|
return retVal
|
|
|
|
|
|
def logbook_add_flow_detail(lb_id, fd_id):
|
|
"""Adds a FlowDetail with id fd_id to a LogBook with id lb_id"""
|
|
# Try to get the LogBook
|
|
try:
|
|
ld_lb = logbooks[lb_id]
|
|
# Raise a NotFound exception if the LogBook is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No Logbook found with id "
|
|
"%s." % (lb_id,))
|
|
|
|
# Try to get the FlowDetail to add
|
|
try:
|
|
ld_fd = flowdetails[fd_id]
|
|
# Raise a NotFound exception if the FlowDetail is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
# Acquire a write lock on the LogBook
|
|
with ld_lb.acquire_lock(read=False):
|
|
# Add the FlowDetail model to the LogBook model
|
|
ld_lb.add_flow_detail(ld_fd)
|
|
|
|
|
|
def logbook_remove_flowdetail(lb_id, fd_id):
|
|
"""Removes a FlowDetail with id fd_id from a LogBook with id lb_id"""
|
|
# Try to get the LogBook
|
|
try:
|
|
ld_lb = logbooks[lb_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No Logbook found with id "
|
|
"%s." % (lb_id,))
|
|
|
|
# Try to get the FlowDetail to remove
|
|
try:
|
|
ld_fd = flowdetails[fd_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
# Acquire a write lock on the LogBook model
|
|
with ld_lb.acquire_lock(read=False):
|
|
# Remove the FlowDetail from the LogBook model
|
|
ld_lb.remove_flow_detail(ld_fd)
|
|
|
|
|
|
def logbook_get_ids_names():
|
|
"""Returns all LogBook ids and names"""
|
|
lb_ids = []
|
|
lb_names = []
|
|
|
|
# Iterate through the LockingDict and append to the Lists
|
|
for (k, v) in logbooks.items():
|
|
lb_ids.append(k)
|
|
lb_names.append(v.name)
|
|
|
|
# Return a dict of the ids and names
|
|
return dict(zip(lb_ids, lb_names))
|
|
|
|
|
|
def _logbook_exists(lb_id, session=None):
|
|
"""Check if a LogBook with lb_id exists"""
|
|
return lb_id in logbooks.keys()
|
|
|
|
|
|
"""
|
|
FLOWDETAIL
|
|
"""
|
|
|
|
|
|
def flowdetail_create(name, wf, fd_id=None):
|
|
"""Create a new FlowDetail model with matching fd_id"""
|
|
# Create a FlowDetail model to save
|
|
fd = memory.MemoryFlowDetail(name, wf, fd_id)
|
|
|
|
#Save the FlowDetail model to the LockingDict
|
|
flowdetails[fd_id] = fd
|
|
|
|
|
|
def flowdetail_destroy(fd_id):
|
|
"""Deletes the FlowDetail model with matching fd_id"""
|
|
# Try to delete the FlowDetail model
|
|
try:
|
|
del flowdetails[fd_id]
|
|
# Raise a NotFound exception if the FlowDetail is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
|
|
def flowdetail_save(fd):
|
|
"""Saves a generic FlowDetail object to the db"""
|
|
# Create a FlowDetail model if one does not exist
|
|
if not _flowdetail_exists(fd.uuid):
|
|
flowdetail_create(fd.name, fd.flow, fd.uuid)
|
|
|
|
# Get a copy of the FlowDetail model in the LockingDict
|
|
ld_fd = flowdetail_get(fd.uuid)
|
|
|
|
for td in fd:
|
|
# Save the TaskDetails in the FlowDetail to save
|
|
td.save()
|
|
|
|
# Add the TaskDetail model to the FlowDetail model if it is not there
|
|
if td not in ld_fd:
|
|
flowdetail_add_task_detail(fd.uuid, td.uuid)
|
|
|
|
|
|
def flowdetail_delete(fd):
|
|
"""Deletes a FlowDetail from db based on a generic type"""
|
|
# Try to get the FlowDetails
|
|
try:
|
|
ld_fd = flowdetails[fd.uuid]
|
|
# Raise a NotFound exception if the FlowDetail is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd.uuid,))
|
|
|
|
# Raise an error if the FlowDetail still has TaskDetails
|
|
if len(ld_fd):
|
|
raise exception.Error("FlowDetail <%s> still has "
|
|
"dependents." % (fd.uuid,))
|
|
# If it is safe, delete the FlowDetail model
|
|
else:
|
|
flowdetail_destroy(fd.uuid)
|
|
|
|
|
|
def flowdetail_get(fd_id):
|
|
"""Gets a FlowDetail with matching fd_id, if it exists"""
|
|
# Try to get the FlowDetail
|
|
try:
|
|
fd = flowdetails[fd_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
# Acquire a read lock on the FlowDetail
|
|
with fd.acquire_lock(read=True):
|
|
# Get the Flow this FlowDetail represents
|
|
wf = fd.flow
|
|
|
|
# Create a FlowDetail to return
|
|
retVal = flowdetail.FlowDetail(fd.name, wf, fd.uuid)
|
|
|
|
# Change updated_at to reflect the current data
|
|
retVal.updated_at = fd.updated_at
|
|
|
|
# Add the generic TaskDetails to the FlowDetail to return
|
|
for td in fd:
|
|
retVal.add_task_detail(taskdetail_get(td.uuid))
|
|
|
|
return retVal
|
|
|
|
|
|
def flowdetail_add_task_detail(fd_id, td_id):
|
|
"""Adds a TaskDetail with id td_id to a Flowdetail with id fd_id"""
|
|
# Try to get the FlowDetail
|
|
try:
|
|
fd = flowdetails[fd_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
# Try to get the TaskDetail to add
|
|
try:
|
|
td = taskdetails[td_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No TaskDetail found with id "
|
|
"%s." % (td_id,))
|
|
|
|
# Acquire a write lock on the FlowDetail model
|
|
with fd.acquire_lock(read=False):
|
|
# Add the TaskDetail to the FlowDetail model
|
|
fd.add_task_detail(td)
|
|
|
|
|
|
def flowdetail_remove_taskdetail(fd_id, td_id):
|
|
"""Removes a TaskDetail with id td_id from a FlowDetail with id fd_id"""
|
|
# Try to get the FlowDetail
|
|
try:
|
|
fd = flowdetails[fd_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No FlowDetail found with id "
|
|
"%s." % (fd_id,))
|
|
|
|
# Try to get the TaskDetail to remove
|
|
try:
|
|
td = taskdetails[td_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No TaskDetail found with id "
|
|
"%s." % (td_id,))
|
|
|
|
# Acquire a write lock on the FlowDetail
|
|
with fd.acquire_lock(read=False):
|
|
# Remove the TaskDetail model from the FlowDetail model
|
|
fd.remove_task_detail(td)
|
|
|
|
|
|
def flowdetail_get_ids_names():
|
|
"""Returns all FlowDetail ids and names"""
|
|
fd_ids = []
|
|
fd_names = []
|
|
|
|
# Iterate through the LockingDict and append to lists
|
|
for (k, v) in flowdetails.items():
|
|
fd_ids.append(k)
|
|
fd_names.append(v.name)
|
|
|
|
# Return a dict of the uuids and names
|
|
return dict(zip(fd_ids, fd_names))
|
|
|
|
|
|
def _flowdetail_exists(fd_id):
|
|
"""Checks if a FlowDetail with fd_id exists"""
|
|
return fd_id in flowdetails.keys()
|
|
|
|
|
|
"""
|
|
TASKDETAIL
|
|
"""
|
|
|
|
|
|
def taskdetail_create(name, tsk, td_id=None):
|
|
"""Create a new TaskDetail model with matching td_id"""
|
|
# Create a TaskDetail model to save
|
|
td = memory.MemoryTaskDetail(name, tsk, td_id)
|
|
# Save the TaskDetail model to the LockingDict
|
|
taskdetails[td_id] = td
|
|
|
|
|
|
def taskdetail_destroy(td_id):
|
|
"""Deletes the TaskDetail model with matching td_id"""
|
|
# Try to delete the TaskDetails
|
|
try:
|
|
del taskdetails[td_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No TaskDetail found with id "
|
|
"%s." % (td_id,))
|
|
|
|
|
|
def taskdetail_save(td):
|
|
"""Saves a generic TaskDetail object to the db"""
|
|
# Create a TaskDetail model if it doesn't exist
|
|
if not _taskdetail_exists(td.uuid):
|
|
taskdetail_create(td.name, td.task, td.uuid)
|
|
|
|
# Prepare values for updating
|
|
values = dict(state=td.state,
|
|
results=td.results,
|
|
exception=td.exception,
|
|
stacktrace=td.stacktrace,
|
|
meta=td.meta)
|
|
|
|
# Update the values of the TaskDetail model
|
|
taskdetail_update(td.uuid, values)
|
|
|
|
|
|
def taskdetail_delete(td):
|
|
"""Deletes a TaskDetail from db based on a generic type"""
|
|
# Destroy the TaskDetail model
|
|
taskdetail_destroy(td.uuid)
|
|
|
|
|
|
def taskdetail_get(td_id):
|
|
"""Gets a TaskDetail with matching td_id, if it exists"""
|
|
# Try to get the TaskDetail
|
|
try:
|
|
ld_td = taskdetails[td_id]
|
|
# Raise NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No TaskDetail found with id "
|
|
"%s." % (td_id,))
|
|
|
|
# Acquire a read lock
|
|
with ld_td.acquire_lock(read=True):
|
|
# Get the Task this TaskDetail represents
|
|
tsk = ld_td.task
|
|
|
|
# Update TaskDetail to return
|
|
retVal = taskdetail.TaskDetail(ld_td.name, tsk, ld_td.uuid)
|
|
retVal.updated_at = ld_td.updated_at
|
|
retVal.state = ld_td.state
|
|
retVal.results = ld_td.results
|
|
retVal.exception = ld_td.exception
|
|
retVal.stacktrace = ld_td.stacktrace
|
|
retVal.meta = ld_td.meta
|
|
|
|
return retVal
|
|
|
|
|
|
def taskdetail_update(td_id, values):
|
|
"""Updates a TaskDetail with matching td_id"""
|
|
# Try to get the TaskDetail
|
|
try:
|
|
ld_td = taskdetails[td_id]
|
|
# Raise a NotFound exception if it is not there
|
|
except KeyError:
|
|
raise exception.NotFound("No TaskDetail found with id "
|
|
"%s." % (td_id,))
|
|
|
|
# Acquire a write lock for the TaskDetail
|
|
with ld_td.acquire_lock(read=False):
|
|
# Write the values to the TaskDetail
|
|
for k, v in values.iteritems():
|
|
setattr(ld_td, k, v)
|
|
|
|
|
|
def taskdetail_get_ids_names():
|
|
"""Returns all TaskDetail ids and names"""
|
|
td_ids = []
|
|
td_names = []
|
|
|
|
# Iterate through the LockingDict and append to Lists
|
|
for (k, v) in taskdetails.items():
|
|
td_ids.append(k)
|
|
td_names.append(v.name)
|
|
|
|
# Return a dict of uuids and names
|
|
return dict(zip(td_ids, td_names))
|
|
|
|
|
|
def _taskdetail_exists(td_id, session=None):
|
|
"""Check if a TaskDetail with td_id exists"""
|
|
return td_id in taskdetails.keys()
|