3 '''main python API for make-magic
5 This is where most of the magic happens. Any APIs you write (e.g. for HTTP)
6 may very well just call this much of the time
11 from core.store import Store
12 from core.marshal import ItemConverter,TaskConverter
13 from lib.loaders import JSONItemLoader
14 from core.deptools import DigraphDependencyStrategy
15 from core.bits import Item
18 def __init__(self,store_factory=Store,dependency_strategy=DigraphDependencyStrategy):
21 self.store = store_factory()
22 self.dependency_strategy = dependency_strategy
25 itemsf = open(config.items_file)
26 self.taskfactory = JSONItemLoader.load_item_classes_from_file(itemsf)
29 reload_items = load_items
32 # This stuff is pretty easy. Get information about existing tasks
33 # and update them: Just pass it off to the storage module
36 return self.store.get_tasks()
37 def get_task(self, uuid):
38 metadata = self.store.metadata(uuid)
39 items = self.store.items(uuid)
40 if not metadata and not items:
41 raise KeyError('uuid '+str(uuid)+' not found')
42 return {'items': items, 'metadata': metadata}
43 def get_item(self, uuid, itemname):
44 item = self.store.item(uuid, itemname)
46 raise KeyError(uuid+'/'+itemname)
48 def get_metadata(self, uuid):
49 metadata = self.store.metadata(uuid)
51 raise KeyError('uuid '+str(uuid)+' not found')
53 def update_item(self, uuid, name, updatedict, onlyif={}):
54 cannot_update = set(('name','depends','if'))
56 for k,v in updatedict.items():
57 if k in cannot_update:
58 raise ValueError('cannot modify item attribute "%s"' %(k,))
59 if 'onlyif' in updatedict:
60 if not getattr(updatedict['onlyif'], 'items', None):
61 raise ValueError('can only set "onlyif" to a dictionary')
62 onlyif.update(updatedict['onlyif'])
63 updatedict.pop('onlyif')
64 if 'state' in updatedict:
65 if updatedict['state'] not in Item.allowed_states:
66 raise ValueError('can only change state to '+','.join(Item.allowed_states))
67 return self.store.update_item(uuid,name,updatedict,onlyif)
68 def update_item_state(self, uuid, name, oldstate, newstate):
69 '''helper to update a state with a guard against the old one
70 WARNING: This actually sucks quite a bit. It doesn't guard against race conditions
71 from multiple agents. This is deliberately not exposed to the HTTP interface for
74 return self.update_item(uuid, name, {'state': newstate}, {'state': oldstate})
75 def update_task_metadata(self, uuid, updatedict, onlyif={}):
76 if 'uuid' in updatedict and uuid != updatedict['uuid']:
77 raise ValueError('cannot change uuid for a task')
78 return self.store.update_metadata(uuid,updatedict,onlyif)
79 def delete_task(self, uuid):
80 self.store.delete_task(uuid)
83 # Creating a new task is almost as easy!
85 def create_task(self, task_data):
86 if 'requirements' not in task_data:
87 raise ValueError('No requirements supplied to create task')
88 task = self.taskfactory.task_from_requirements(task_data['requirements'])
90 # FIXME: Should be in core.marshal
91 # This is also an awful hack
92 task.data.update(task_data)
94 items = [ic.item_to_itemdict(item) for item in task.items]
97 self.store.new_task(task.uuid, items, metadata=task.data)
98 return self.get_task(task.uuid)
103 def ready_to_run(self, uuid):
104 '''return all the items that we can run'''
105 task = self.get_task(uuid)
106 converter = TaskConverter()
107 task = converter.taskdict_to_task(task)
108 ready = self.dependency_strategy.ready_to_run(task.items)
109 # FIXME: Evil, Evil hack
110 if 'TaskComplete' in (r.name for r in ready):
111 self.update_item(uuid, 'TaskComplete', {'data': {'state': 'COMPLETED'}})
112 return self.ready_to_run(uuid)
113 ready = self.dependency_strategy.ready_to_run(task.items)
114 return [converter.item_to_itemdict(item) for item in ready]