203 lines
6.7 KiB
Python
203 lines
6.7 KiB
Python
"""
|
|
Copyright 2015 Hewlett-Packard
|
|
|
|
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 falcon
|
|
from freezer_api.common import exceptions as freezer_api_exc
|
|
from freezer_api.api.common import resource
|
|
|
|
|
|
class JobsCollectionResource(resource.BaseResource):
|
|
"""
|
|
Handler for endpoint: /v1/jobs
|
|
"""
|
|
def __init__(self, storage_driver):
|
|
self.db = storage_driver
|
|
|
|
def on_get(self, req, resp):
|
|
# GET /v1/jobs(?limit,offset) Lists jobs
|
|
user_id = req.get_header('X-User-ID')
|
|
offset = req.get_param_as_int('offset') or 0
|
|
limit = req.get_param_as_int('limit') or 10
|
|
search = self.json_body(req)
|
|
obj_list = self.db.search_job(user_id=user_id, offset=offset,
|
|
limit=limit, search=search)
|
|
resp.body = {'jobs': obj_list}
|
|
|
|
def on_post(self, req, resp):
|
|
# POST /v1/jobs Creates job entry
|
|
try:
|
|
doc = self.json_body(req)
|
|
except KeyError:
|
|
raise freezer_api_exc.BadDataFormat(
|
|
message='Missing request body')
|
|
|
|
user_id = req.get_header('X-User-ID')
|
|
job_id = self.db.add_job(user_id=user_id, doc=doc)
|
|
resp.status = falcon.HTTP_201
|
|
resp.body = {'job_id': job_id}
|
|
|
|
|
|
class JobsResource(resource.BaseResource):
|
|
"""
|
|
Handler for endpoint: /v1/jobs/{job_id}
|
|
"""
|
|
|
|
def __init__(self, storage_driver):
|
|
self.db = storage_driver
|
|
|
|
def on_get(self, req, resp, job_id):
|
|
# GET /v1/jobs/{job_id} retrieves the specified job
|
|
# search in body
|
|
user_id = req.get_header('X-User-ID') or ''
|
|
obj = self.db.get_job(user_id=user_id, job_id=job_id)
|
|
if obj:
|
|
resp.body = obj
|
|
else:
|
|
resp.status = falcon.HTTP_404
|
|
|
|
def on_delete(self, req, resp, job_id):
|
|
# DELETE /v1/jobs/{job_id} Deletes the specified job
|
|
user_id = req.get_header('X-User-ID')
|
|
self.db.delete_job(user_id=user_id, job_id=job_id)
|
|
resp.body = {'job_id': job_id}
|
|
resp.status = falcon.HTTP_204
|
|
|
|
def on_patch(self, req, resp, job_id):
|
|
# PATCH /v1/jobs/{job_id} updates the specified job
|
|
user_id = req.get_header('X-User-ID') or ''
|
|
doc = self.json_body(req)
|
|
new_version = self.db.update_job(user_id=user_id,
|
|
job_id=job_id,
|
|
patch_doc=doc)
|
|
resp.body = {'job_id': job_id, 'version': new_version}
|
|
|
|
def on_post(self, req, resp, job_id):
|
|
# PUT /v1/jobs/{job_id} creates/replaces the specified job
|
|
user_id = req.get_header('X-User-ID') or ''
|
|
doc = self.json_body(req)
|
|
new_version = self.db.replace_job(user_id=user_id,
|
|
job_id=job_id,
|
|
doc=doc)
|
|
resp.status = falcon.HTTP_201
|
|
resp.body = {'job_id': job_id, 'version': new_version}
|
|
|
|
|
|
class JobsEvent(resource.BaseResource):
|
|
"""
|
|
Handler for endpoint: /v1/jobs/{job_id}/event
|
|
|
|
Actions are passed in the body, for example:
|
|
{
|
|
"start": null
|
|
}
|
|
"""
|
|
def __init__(self, storage_driver):
|
|
self.db = storage_driver
|
|
|
|
def on_post(self, req, resp, job_id):
|
|
# POST /v1/jobs/{job_id}/event
|
|
# requests an event on the specified job
|
|
|
|
user_id = req.get_header('X-User-ID') or ''
|
|
doc = self.json_body(req)
|
|
|
|
try:
|
|
event, params = next(doc.iteritems())
|
|
except:
|
|
raise freezer_api_exc.BadDataFormat("Bad event request format")
|
|
|
|
job_doc = self.db.get_job(user_id=user_id,
|
|
job_id=job_id)
|
|
job = Job(job_doc)
|
|
result = job.execute_event(event, params)
|
|
|
|
if job.need_update:
|
|
self.db.replace_job(user_id=user_id,
|
|
job_id=job_id,
|
|
doc=job.doc)
|
|
resp.status = falcon.HTTP_202
|
|
resp.body = {'result': result}
|
|
|
|
|
|
class Job(resource.BaseResource):
|
|
"""
|
|
A class to manage the events that can be sent upon a
|
|
Job data structure.
|
|
It modifies information contained in its document
|
|
in accordance to the requested event
|
|
"""
|
|
def __init__(self, doc):
|
|
self.doc = doc
|
|
self.event_result = ''
|
|
self.need_update = False
|
|
if 'job_schedule' not in doc:
|
|
doc['job_schedule'] = {}
|
|
self.job_schedule = doc['job_schedule']
|
|
self.event_handlers = {'start': self.start,
|
|
'stop': self.stop,
|
|
'abort': self.abort}
|
|
|
|
def execute_event(self, event, params):
|
|
handler = self.event_handlers.get(event, None)
|
|
if not handler:
|
|
raise freezer_api_exc.BadDataFormat("Bad Action Method")
|
|
try:
|
|
self.event_result = handler(params)
|
|
except freezer_api_exc.BadDataFormat:
|
|
raise
|
|
except Exception as e:
|
|
raise freezer_api_exc.FreezerAPIException(e)
|
|
return self.event_result
|
|
|
|
@property
|
|
def job_status(self):
|
|
return self.job_schedule.get('status', '')
|
|
|
|
@job_status.setter
|
|
def job_status(self, value):
|
|
self.job_schedule['status'] = value
|
|
|
|
def start(self, params=None):
|
|
if self.job_status in ["scheduled", "running"]:
|
|
return 'already active'
|
|
if self.job_status in ["completed", "stop", ""]:
|
|
# completed jobs are not acquired by the scheduler
|
|
self.job_status = 'stop'
|
|
self.job_schedule['event'] = 'start'
|
|
self.job_schedule['result'] = ''
|
|
self.need_update = True
|
|
return 'success'
|
|
else:
|
|
raise freezer_api_exc.BadDataFormat("unable to start a {0} job"
|
|
.format(self.job_status))
|
|
|
|
def stop(self, params=None):
|
|
if self.job_status in ["scheduled", "running", ""]:
|
|
self.job_schedule['event'] = 'stop'
|
|
self.need_update = True
|
|
return 'success'
|
|
else:
|
|
return 'already stopped'
|
|
|
|
def abort(self, params=None):
|
|
if self.job_status in ["scheduled", "running", ""]:
|
|
self.job_schedule['event'] = 'abort'
|
|
self.need_update = True
|
|
return 'success'
|
|
else:
|
|
return 'already stopped'
|