dcos node

This commit is contained in:
Michael Gummelt
2015-06-29 18:36:31 -07:00
parent aa88ed2c2e
commit aff8afae2e
13 changed files with 244 additions and 17 deletions

View File

92
cli/dcoscli/node/main.py Normal file
View File

@@ -0,0 +1,92 @@
"""Manage DCOS nodes
Usage:
dcos node --info
dcos node [--json]
Options:
-h, --help Show this screen
--info Show a short description of this subcommand
--json Print json-formatted nodes
--version Show version
"""
import dcoscli
import docopt
from dcos import cmds, emitting, errors, mesos, util
from dcos.errors import DCOSException
from dcoscli import 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-node version {}".format(dcoscli.version))
return cmds.execute(_cmds(), args)
def _cmds():
"""
:returns: All of the supported commands
:rtype: [Command]
"""
return [
cmds.Command(
hierarchy=['node', '--info'],
arg_keys=[],
function=_info),
cmds.Command(
hierarchy=['node'],
arg_keys=['--json'],
function=_list),
]
def _info():
"""Print node cli information.
:returns: process return code
:rtype: int
"""
emitter.publish(__doc__.split('\n')[0])
return 0
def _list(json_):
"""List dcos nodes
:param json_: If true, output json.
Otherwise, output a human readable table.
:type json_: bool
:returns: process return code
:rtype: int
"""
client = mesos.MesosClient()
slaves = client.get_state_summary()['slaves']
if json_:
emitter.publish(slaves)
else:
table = tables.slave_table(slaves)
output = str(table)
if output:
emitter.publish(output)
else:
emitter.publish(errors.DefaultError('No slaves found.'))

View File

@@ -91,7 +91,7 @@ def _service(inactive, is_json):
"""List dcos services
:param inactive: If True, include completed tasks
:type completed: bool
:type inactive: bool
:param is_json: If true, output json.
Otherwise, output a human readable table.
:type is_json: bool

View File

@@ -1,7 +1,7 @@
import copy
from collections import OrderedDict
from dcos import util
from dcos import mesos, util
def task_table(tasks):
@@ -276,3 +276,21 @@ def package_search_table(search_results):
tb.align['DESCRIPTION'] = 'l'
return tb
def slave_table(slaves):
"""Returns a PrettyTable representation of the provided DCOS slaves
:param slaves: slaves to render. dicts from /mesos/state-summary
:type slaves: [dict]
:rtype: PrettyTable
"""
fields = OrderedDict([
('HOSTNAME', lambda s: s['hostname']),
('IP', lambda s: mesos.parse_pid(s['pid'])[1]),
('ID', lambda s: s['id'])
])
tb = util.table(fields, slaves, sortby="HOSTNAME")
return tb

View File

@@ -97,6 +97,7 @@ setup(
'dcos-package=dcoscli.package.main:main',
'dcos-service=dcoscli.service.main:main',
'dcos-task=dcoscli.task.main:main',
'dcos-node=dcoscli.node.main:main'
],
},

40
cli/tests/fixtures/node.py vendored Normal file
View File

@@ -0,0 +1,40 @@
def slave_fixture():
""" Slave node fixture.
:rtype: dict
"""
return {
"TASK_ERROR": 0,
"TASK_FAILED": 0,
"TASK_FINISHED": 0,
"TASK_KILLED": 0,
"TASK_LOST": 0,
"TASK_RUNNING": 0,
"TASK_STAGING": 0,
"TASK_STARTING": 0,
"active": True,
"attributes": {},
"framework_ids": [],
"hostname": "dcos-01",
"id": "20150630-004309-1695027628-5050-1649-S0",
"offered_resources": {
"cpus": 0,
"disk": 0,
"mem": 0
},
"pid": "slave(1)@172.17.8.101:5051",
"registered_time": 1435625024.42234,
"resources": {
"cpus": 4,
"disk": 10823,
"mem": 2933,
"ports": ("[1025-2180, 2182-3887, 3889-5049, 5052-8079, " +
"8082-8180, 8182-65535]")
},
"used_resources": {
"cpus": 0,
"disk": 0,
"mem": 0
}
}

View File

@@ -20,6 +20,7 @@ Available DCOS commands:
\tconfig \tGet and set DCOS CLI configuration properties
\thelp \tDisplay command line usage information
\tmarathon \tDeploy and manage applications on the DCOS
\tnode \tManage DCOS nodes
\tpackage \tInstall and manage DCOS software packages
\tservice \tManage DCOS services
\ttask \tManage DCOS tasks

View File

@@ -39,6 +39,7 @@ Available DCOS commands:
\tconfig \tGet and set DCOS CLI configuration properties
\thelp \tDisplay command line usage information
\tmarathon \tDeploy and manage applications on the DCOS
\tnode \tManage DCOS nodes
\tpackage \tInstall and manage DCOS software packages
\tservice \tManage DCOS services
\ttask \tManage DCOS tasks

View File

@@ -0,0 +1,44 @@
import json
import dcos.util as util
from dcos.util import create_schema
from ..fixtures.node import slave_fixture
from .common import assert_command, assert_lines, exec_command
def test_help():
stdout = b"""Manage DCOS nodes
Usage:
dcos node --info
dcos node [--json]
Options:
-h, --help Show this screen
--info Show a short description of this subcommand
--json Print json-formatted nodes
--version Show version
"""
assert_command(['dcos', 'node', '--help'], stdout=stdout)
def test_info():
stdout = b"Manage DCOS nodes\n"
assert_command(['dcos', 'node', '--info'], stdout=stdout)
def test_node():
returncode, stdout, stderr = exec_command(['dcos', 'node', '--json'])
assert returncode == 0
assert stderr == b''
nodes = json.loads(stdout.decode('utf-8'))
schema = create_schema(slave_fixture())
for node in nodes:
assert not util.validate_json(node, schema)
def test_node_table():
assert_lines(['dcos', 'node'], 2)

View File

@@ -7,8 +7,7 @@ import pytest
from ..fixtures.service import framework_fixture
from .common import (assert_command, assert_lines, delete_zk_nodes,
exec_command, get_services, service_shutdown,
watch_all_deployments)
get_services, service_shutdown, watch_all_deployments)
@pytest.fixture(scope="module")
@@ -50,8 +49,6 @@ def test_info():
def test_service():
returncode, stdout, stderr = exec_command(['dcos', 'service', '--json'])
services = get_services(1)
schema = _get_schema(framework_fixture())
@@ -63,17 +60,6 @@ def test_service_table():
assert_lines(['dcos', 'service'], 2)
def _get_schema(service):
schema = create_schema(service.dict())
schema['required'].remove('reregistered_time')
schema['required'].remove('pid')
schema['properties']['offered_resources']['required'].remove('ports')
schema['properties']['resources']['required'].remove('ports')
schema['properties']['used_resources']['required'].remove('ports')
return schema
def test_service_inactive(zk_znode):
# install cassandra
stdout = b"""The Apache Cassandra DCOS Service implementation is alpha \
@@ -122,3 +108,14 @@ Thank you for installing the Apache Cassandra DCOS Service.
# assert marathon is only listed with --inactive
get_services(1, ['--inactive'])
def _get_schema(service):
schema = create_schema(service.dict())
schema['required'].remove('reregistered_time')
schema['required'].remove('pid')
schema['properties']['offered_resources']['required'].remove('ports')
schema['properties']['resources']['required'].remove('ports')
schema['properties']['used_resources']['required'].remove('ports')
return schema

View File

@@ -0,0 +1,2 @@
HOSTNAME IP ID
dcos-01 172.17.8.101 20150630-004309-1695027628-5050-1649-S0

View File

@@ -2,6 +2,7 @@ from dcoscli import tables
from ..fixtures.marathon import (app_fixture, app_task_fixture,
deployment_fixture, group_fixture)
from ..fixtures.node import slave_fixture
from ..fixtures.package import package_fixture, search_result_fixture
from ..fixtures.service import framework_fixture
from ..fixtures.task import task_fixture
@@ -55,6 +56,12 @@ def test_package_search_table():
'tests/unit/data/package_search.txt')
def test_node_table():
_test_table(tables.slave_table,
slave_fixture,
'tests/unit/data/node.txt')
def _test_table(table_fn, fixture_fn, path):
table = table_fn([fixture_fn()])
with open(path) as f:

View File

@@ -90,6 +90,16 @@ class MesosClient:
url = self.slave_url(slave_id, 'state.json')
return http.get(url).json()
def get_state_summary(self):
"""Get the Mesos master state summary json object
:returns: Mesos' master state summary json object
:rtype: dict
"""
url = self.master_url('master/state-summary')
return http.get(url).json()
def shutdown_framework(self, framework_id):
"""Shuts down a Mesos framework
@@ -696,6 +706,20 @@ class MesosFile(object):
return "{0}:{1}".format(self._task['id'], self._path)
def parse_pid(pid):
""" Parse the mesos pid string,
:param pid: pid of the form "id@ip:port"
:type pid: str
:returns: parsed pid
:rtype: (str, str, str)
"""
id_, second = pid.split('@')
ip, port = second.split(':')
return id_, ip, port
def _merge(d, keys):
""" Merge multiple lists from a dictionary into one iterator.
e.g. _merge({'a': [1, 2], 'b': [3]}, ['a', 'b']) ->