Add Celery task list output
This commit is contained in:
parent
ed84df5237
commit
2057859b9f
|
@ -35,6 +35,7 @@ from functools import wraps
|
||||||
from flask_restful import Resource, Api, reqparse, abort
|
from flask_restful import Resource, Api, reqparse, abort
|
||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
from celery.task.control import inspect
|
||||||
|
|
||||||
import api_lib.pvcapi_helper as api_helper
|
import api_lib.pvcapi_helper as api_helper
|
||||||
import api_lib.pvcapi_provisioner as api_provisioner
|
import api_lib.pvcapi_provisioner as api_provisioner
|
||||||
|
@ -5439,6 +5440,40 @@ class API_Provisioner_Create_Root(Resource):
|
||||||
return { "task_id": task.id }, 202, { 'Location': Api.url_for(api, API_Provisioner_Status_Element, task_id=task.id) }
|
return { "task_id": task.id }, 202, { 'Location': Api.url_for(api, API_Provisioner_Status_Element, task_id=task.id) }
|
||||||
api.add_resource(API_Provisioner_Create_Root, '/provisioner/create')
|
api.add_resource(API_Provisioner_Create_Root, '/provisioner/create')
|
||||||
|
|
||||||
|
# /provisioner/status
|
||||||
|
class API_Provisioner_Status_Root(Resource):
|
||||||
|
@Authenticator
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
View status of provisioner Celery queue
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- provisioner
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
active:
|
||||||
|
type: object
|
||||||
|
description: Celery app.control.inspect active tasks
|
||||||
|
reserved:
|
||||||
|
type: object
|
||||||
|
description: Celery app.control.inspect reserved tasks
|
||||||
|
scheduled:
|
||||||
|
type: object
|
||||||
|
description: Celery app.control.inspect scheduled tasks
|
||||||
|
"""
|
||||||
|
queue = celery.control.inspect(timeout=0.1)
|
||||||
|
response = {
|
||||||
|
'scheduled': queue.scheduled(),
|
||||||
|
'active': queue.active(),
|
||||||
|
'reserved': queue.reserved()
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
api.add_resource(API_Provisioner_Status_Root, '/provisioner/status')
|
||||||
|
|
||||||
# /provisioner/status/<task_id>
|
# /provisioner/status/<task_id>
|
||||||
class API_Provisioner_Status_Element(Resource):
|
class API_Provisioner_Status_Element(Resource):
|
||||||
@Authenticator
|
@Authenticator
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import ast
|
||||||
|
|
||||||
import cli_lib.ansiprint as ansiprint
|
import cli_lib.ansiprint as ansiprint
|
||||||
from cli_lib.common import call_api
|
from cli_lib.common import call_api
|
||||||
|
@ -453,16 +454,20 @@ def vm_create(config, name, profile, wait_flag, define_flag, start_flag):
|
||||||
|
|
||||||
return retvalue, retdata
|
return retvalue, retdata
|
||||||
|
|
||||||
def task_status(config, task_id, is_watching=False):
|
def task_status(config, task_id=None, is_watching=False):
|
||||||
"""
|
"""
|
||||||
Get information about provisioner job {task_id}
|
Get information about provisioner job {task_id} or all tasks if None
|
||||||
|
|
||||||
API endpoint: GET /api/v1/provisioner/status
|
API endpoint: GET /api/v1/provisioner/status
|
||||||
API arguments:
|
API arguments:
|
||||||
API schema: {json_data_object}
|
API schema: {json_data_object}
|
||||||
"""
|
"""
|
||||||
|
if task_id is not None:
|
||||||
response = call_api(config, 'get', '/provisioner/status/{task_id}'.format(task_id=task_id))
|
response = call_api(config, 'get', '/provisioner/status/{task_id}'.format(task_id=task_id))
|
||||||
|
else:
|
||||||
|
response = call_api(config, 'get', '/provisioner/status')
|
||||||
|
|
||||||
|
if task_id is not None:
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
retvalue = True
|
retvalue = True
|
||||||
respjson = response.json()
|
respjson = response.json()
|
||||||
|
@ -494,6 +499,10 @@ def task_status(config, task_id, is_watching=False):
|
||||||
else:
|
else:
|
||||||
retvalue = False
|
retvalue = False
|
||||||
retdata = response.json()['message']
|
retdata = response.json()['message']
|
||||||
|
else:
|
||||||
|
retvalue = True
|
||||||
|
respjson = response.json()
|
||||||
|
retdata = format_list_task(respjson)
|
||||||
|
|
||||||
return retvalue, retdata
|
return retvalue, retdata
|
||||||
|
|
||||||
|
@ -1142,3 +1151,123 @@ Data: {profile_userdata: <{profile_userdata_length}} \
|
||||||
)
|
)
|
||||||
|
|
||||||
return '\n'.join([profile_list_output_header] + profile_list_output)
|
return '\n'.join([profile_list_output_header] + profile_list_output)
|
||||||
|
|
||||||
|
def format_list_task(task_data_raw):
|
||||||
|
# Format the Celery data into a more useful data structure
|
||||||
|
task_data = list()
|
||||||
|
for task_type in ['active', 'reserved', 'scheduled']:
|
||||||
|
type_data = task_data_raw[task_type]
|
||||||
|
if not type_data:
|
||||||
|
type_data = dict()
|
||||||
|
for task_host in type_data:
|
||||||
|
for task_job in task_data_raw[task_type][task_host]:
|
||||||
|
task = dict()
|
||||||
|
if task_type == 'reserved':
|
||||||
|
task['type'] = 'pending'
|
||||||
|
else:
|
||||||
|
task['type'] = task_type
|
||||||
|
task['worker'] = task_host
|
||||||
|
task['id'] = task_job.get('id')
|
||||||
|
task_args = ast.literal_eval(task_job.get('args'))
|
||||||
|
task['vm_name'] = task_args[0]
|
||||||
|
task['vm_profile'] = task_args[1]
|
||||||
|
task_kwargs = ast.literal_eval(task_job.get('kwargs'))
|
||||||
|
task['vm_define'] = str(bool(task_kwargs['define_vm']))
|
||||||
|
task['vm_start'] = str(bool(task_kwargs['start_vm']))
|
||||||
|
task_data.append(task)
|
||||||
|
|
||||||
|
task_list_output = []
|
||||||
|
|
||||||
|
# Determine optimal column widths
|
||||||
|
task_id_length = 3
|
||||||
|
task_type_length = 7
|
||||||
|
task_vm_name_length = 5
|
||||||
|
task_vm_profile_length = 8
|
||||||
|
task_vm_define_length = 8
|
||||||
|
task_vm_start_length = 7
|
||||||
|
task_worker_length = 8
|
||||||
|
|
||||||
|
for task in task_data:
|
||||||
|
# task_id column
|
||||||
|
_task_id_length = len(str(task['id'])) + 1
|
||||||
|
if _task_id_length > task_id_length:
|
||||||
|
task_id_length = _task_id_length
|
||||||
|
# task_type column
|
||||||
|
_task_type_length = len(str(task['type'])) + 1
|
||||||
|
if _task_type_length > task_type_length:
|
||||||
|
task_type_length = _task_type_length
|
||||||
|
# task_vm_name column
|
||||||
|
_task_vm_name_length = len(str(task['vm_name'])) + 1
|
||||||
|
if _task_vm_name_length > task_vm_name_length:
|
||||||
|
task_vm_name_length = _task_vm_name_length
|
||||||
|
# task_vm_profile column
|
||||||
|
_task_vm_profile_length = len(str(task['vm_profile'])) + 1
|
||||||
|
if _task_vm_profile_length > task_vm_profile_length:
|
||||||
|
task_vm_profile_length = _task_vm_profile_length
|
||||||
|
# task_vm_define column
|
||||||
|
_task_vm_define_length = len(str(task['vm_define'])) + 1
|
||||||
|
if _task_vm_define_length > task_vm_define_length:
|
||||||
|
task_vm_define_length = _task_vm_define_length
|
||||||
|
# task_vm_start column
|
||||||
|
_task_vm_start_length = len(str(task['vm_start'])) + 1
|
||||||
|
if _task_vm_start_length > task_vm_start_length:
|
||||||
|
task_vm_start_length = _task_vm_start_length
|
||||||
|
# task_worker column
|
||||||
|
_task_worker_length = len(str(task['worker'])) + 1
|
||||||
|
if _task_worker_length > task_worker_length:
|
||||||
|
task_worker_length = _task_worker_length
|
||||||
|
|
||||||
|
# Format the string (header)
|
||||||
|
task_list_output_header = '{bold}{task_id: <{task_id_length}} {task_type: <{task_type_length}} \
|
||||||
|
{task_worker: <{task_worker_length}} \
|
||||||
|
VM: {task_vm_name: <{task_vm_name_length}} \
|
||||||
|
{task_vm_profile: <{task_vm_profile_length}} \
|
||||||
|
{task_vm_define: <{task_vm_define_length}} \
|
||||||
|
{task_vm_start: <{task_vm_start_length}}{end_bold}'.format(
|
||||||
|
task_id_length=task_id_length,
|
||||||
|
task_type_length=task_type_length,
|
||||||
|
task_worker_length=task_worker_length,
|
||||||
|
task_vm_name_length=task_vm_name_length,
|
||||||
|
task_vm_profile_length=task_vm_profile_length,
|
||||||
|
task_vm_define_length=task_vm_define_length,
|
||||||
|
task_vm_start_length=task_vm_start_length,
|
||||||
|
bold=ansiprint.bold(),
|
||||||
|
end_bold=ansiprint.end(),
|
||||||
|
task_id='ID',
|
||||||
|
task_type='Status',
|
||||||
|
task_worker='Worker',
|
||||||
|
task_vm_name='Name',
|
||||||
|
task_vm_profile='Profile',
|
||||||
|
task_vm_define='Define?',
|
||||||
|
task_vm_start='Start?'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format the string (elements)
|
||||||
|
for task in sorted(task_data, key=lambda i: i.get('type', None)):
|
||||||
|
task_list_output.append(
|
||||||
|
'{bold}{task_id: <{task_id_length}} {task_type: <{task_type_length}} \
|
||||||
|
{task_worker: <{task_worker_length}} \
|
||||||
|
{task_vm_name: <{task_vm_name_length}} \
|
||||||
|
{task_vm_profile: <{task_vm_profile_length}} \
|
||||||
|
{task_vm_define: <{task_vm_define_length}} \
|
||||||
|
{task_vm_start: <{task_vm_start_length}}{end_bold}'.format(
|
||||||
|
task_id_length=task_id_length,
|
||||||
|
task_type_length=task_type_length,
|
||||||
|
task_worker_length=task_worker_length,
|
||||||
|
task_vm_name_length=task_vm_name_length,
|
||||||
|
task_vm_profile_length=task_vm_profile_length,
|
||||||
|
task_vm_define_length=task_vm_define_length,
|
||||||
|
task_vm_start_length=task_vm_start_length,
|
||||||
|
bold='',
|
||||||
|
end_bold='',
|
||||||
|
task_id=task['id'],
|
||||||
|
task_type=task['type'],
|
||||||
|
task_worker=task['worker'],
|
||||||
|
task_vm_name=task['vm_name'],
|
||||||
|
task_vm_profile=task['vm_profile'],
|
||||||
|
task_vm_define=task['vm_define'],
|
||||||
|
task_vm_start=task['vm_start']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return '\n'.join([task_list_output_header] + task_list_output)
|
||||||
|
|
|
@ -3029,11 +3029,11 @@ def provisioner_create(name, profile, wait_flag, define_flag, start_flag):
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@click.command(name='status', short_help='Show status of provisioner job.')
|
@click.command(name='status', short_help='Show status of provisioner job.')
|
||||||
@click.argument(
|
@click.argument(
|
||||||
'job'
|
'job', required=False, default=None
|
||||||
)
|
)
|
||||||
def provisioner_status(job):
|
def provisioner_status(job):
|
||||||
"""
|
"""
|
||||||
Show status of provisioner job JOB.
|
Show status of provisioner job JOB or a list of jobs.
|
||||||
"""
|
"""
|
||||||
retcode, retdata = pvc_provisioner.task_status(config, job)
|
retcode, retdata = pvc_provisioner.task_status(config, job)
|
||||||
cleanup(retcode, retdata)
|
cleanup(retcode, retdata)
|
||||||
|
|
|
@ -2568,6 +2568,37 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/provisioner/status": {
|
||||||
|
"get": {
|
||||||
|
"description": "",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"active": {
|
||||||
|
"description": "Celery app.control.inspect active tasks",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"reserved": {
|
||||||
|
"description": "Celery app.control.inspect reserved tasks",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"scheduled": {
|
||||||
|
"description": "Celery app.control.inspect scheduled tasks",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"summary": "View status of provisioner Celery queue",
|
||||||
|
"tags": [
|
||||||
|
"provisioner"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/provisioner/status/{task_id}": {
|
"/api/v1/provisioner/status/{task_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
Loading…
Reference in New Issue