Files
deb-python-dcos/cli/dcoscli/task/main.py
Michael Gummelt ac41782ef1 dcos node ssh
2015-07-13 13:01:43 -07:00

212 lines
5.3 KiB
Python

"""Manage DCOS tasks
Usage:
dcos task --info
dcos task [--completed --json <task>]
dcos task log [--completed --follow --lines=N] <task> [<file>]
Options:
-h, --help Show this screen
--info Show a short description of this subcommand
--completed Include completed tasks as well
--follow Output data as the file grows
--json Print json-formatted tasks
--lines=N Output the last N lines [default: 10]
--version Show version
Positional Arguments:
<task> Only match tasks whose ID matches <task>. <task> may be
a substring of the ID, or a unix glob pattern.
<file> Output this file. [default: stdout]
"""
import dcoscli
import docopt
from dcos import cmds, emitting, mesos, util
from dcos.errors import DCOSException, DefaultError
from dcoscli import log, tables
logger = util.get_logger(__name__)
emitter = emitting.FlatEmitter()
def main():
try:
return _main()
except DCOSException as e:
emitter.publish(e)
return 1
def _main():
util.configure_logger_from_environ()
args = docopt.docopt(
__doc__,
version="dcos-task version {}".format(dcoscli.version))
return cmds.execute(_cmds(), args)
def _cmds():
"""
:returns: All of the supported commands
:rtype: [Command]
"""
return [
cmds.Command(
hierarchy=['task', '--info'],
arg_keys=[],
function=_info),
cmds.Command(
hierarchy=['task', 'log'],
arg_keys=['--follow', '--completed', '--lines', '<task>',
'<file>'],
function=_log),
cmds.Command(
hierarchy=['task'],
arg_keys=['<task>', '--completed', '--json'],
function=_task),
]
def _info():
"""Print task cli information.
:returns: process return code
:rtype: int
"""
emitter.publish(__doc__.split('\n')[0])
return 0
def _task(fltr, completed, json_):
"""List DCOS tasks
:param fltr: task id filter
:type fltr: str
:param completed: If True, include completed tasks
:type completed: bool
:param json_: If True, output json. Otherwise, output a human
readable table.
:type json_: bool
:returns: process return code
"""
if fltr is None:
fltr = ""
tasks = sorted(mesos.get_master().tasks(completed=completed, fltr=fltr),
key=lambda task: task['name'])
if json_:
emitter.publish([task.dict() for task in tasks])
else:
table = tables.task_table(tasks)
output = str(table)
if output:
emitter.publish(output)
return 0
def _log(follow, completed, lines, task, path):
""" Tail a file in the task's sandbox.
:param follow: same as unix tail's -f
:type follow: bool
:param completed: whether to include completed tasks
:type completed: bool
:param lines: number of lines to print
:type lines: int
:param task: task pattern to match
:type task: str
:param path: file path to read
:type path: str
:returns: process return code
:rtype: int
"""
if task is None:
fltr = ""
else:
fltr = task
if path is None:
path = 'stdout'
lines = util.parse_int(lines)
mesos_files = _mesos_files(completed, fltr, path)
if not mesos_files:
raise DCOSException('No matching tasks. Exiting.')
log.log_files(mesos_files, follow, lines)
return 0
def _mesos_files(completed, fltr, path):
"""Return MesosFile objects for the specified files. Only include
files that satisfy all of the following:
a) belong to an available slave
b) have an executor entry on the slave
:param completed: whether to include completed tasks
:type completed: bool
:param fltr: task pattern to match
:type fltr: str
:param path: file path to read
:type path: str
:returns: MesosFile objects
:rtype: [MesosFile]
"""
# get tasks
client = mesos.DCOSClient()
master = mesos.Master(client.get_master_state())
tasks = master.tasks(completed=completed, fltr=fltr)
# load slave state in parallel
slaves = _load_slaves_state([task.slave() for task in tasks])
# some completed tasks may have entries on the master, but none on
# the slave. since we need the slave entry to get the executor
# sandbox, we only include files with an executor entry.
available_tasks = [task for task in tasks
if task.slave() in slaves and task.executor()]
# create files.
return [mesos.MesosFile(path, task=task, mesos_client=client)
for task in available_tasks]
def _load_slaves_state(slaves):
"""Fetch each slave's state.json in parallel, and return the reachable
slaves.
:param slaves: slaves to fetch
:type slaves: [MesosSlave]
:returns: MesosSlave objects that were successfully reached
:rtype: [MesosSlave]
"""
reachable_slaves = []
for job, slave in util.stream(lambda slave: slave.state(), slaves):
try:
job.result()
reachable_slaves.append(slave)
except DCOSException as e:
emitter.publish(
DefaultError('Error accessing slave: {0}'.format(e)))
return reachable_slaves