def test_node_decommission(): agents = mesos.DCOSClient().get_state_summary()['slaves'] agents_count = len(agents) assert agents_count > 0 agent_id = agents[0]['id'] returncode, stdout, stderr = exec_command( ['dcos', 'node', 'decommission', agent_id]) exp_stdout = "Agent {} has been marked as gone.\n".format(agent_id) assert returncode == 0 assert stdout.decode('utf-8') == exp_stdout assert stderr == b'' new_agents = mesos.DCOSClient().get_state_summary()['slaves'] assert (agents_count - 1) == len(new_agents)
def test_log_master_unavailable(config_mock): config_mock.return_value = {'core.dcos_url': 'foo'} """ Test master's state.json being unavailable """ client = mesos.DCOSClient() client.get_master_state = _mock_exception() with patch('dcos.mesos.DCOSClient', return_value=client): args = ['task', 'log', '_'] assert_mock(main, args, returncode=1, stderr=(b"exception\n"))
def __init__(self, ssh_client, r_addroute, r_delroute, disablevips, disableoverlay): self.dcos_client = mesos.DCOSClient() self.dns_client = mesos.MesosDNSClient() self.ssh_client = ssh_client self.addroute = marshal_networks(r_addroute) self.delroute = marshal_networks(r_delroute) self.disablevips = disablevips self.disableoverlay = disableoverlay
def get_host(host): if not host: dcos_client = mesos.DCOSClient() host = dcos_client.metadata().get('PUBLIC_IPV4') if not host: host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip'] if not host: raise DCOSException("*** No host detected. Please set one manually.") return host
def _log(follow, completed, lines, task, file_): """ 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 file_: file path to read :type file_: str :returns: process return code :rtype: int """ if task is None: fltr = "" else: fltr = task if file_ is None: file_ = 'stdout' if lines is None: lines = 10 lines = util.parse_int(lines) # get tasks client = mesos.DCOSClient() master = mesos.Master(client.get_master_state()) tasks = master.tasks(completed=completed, fltr=fltr) if not tasks: if not completed: completed_tasks = master.tasks(completed=True, fltr=fltr) if completed_tasks: msg = 'No running tasks match ID [{}]; however, there '.format( fltr) if len(completed_tasks) > 1: msg += 'are {} matching completed tasks. '.format( len(completed_tasks)) else: msg += 'is 1 matching completed task. ' msg += 'Run with --completed to see these logs.' raise DCOSException(msg) raise DCOSException('No matching tasks. Exiting.') mesos_files = _mesos_files(tasks, file_, client) if not mesos_files: raise DCOSException('No matching tasks. Exiting.') log.log_files(mesos_files, follow, lines) return 0
def _ls(task, path, all_, long_, completed): """ List files in a task's sandbox. :param task: task pattern to match :type task: str :param path: file path to read :type path: str :param long_: whether to use a long listing format :type long_: bool :param all_: If True, include all tasks :type all_: bool :param completed: If True, include completed tasks :type completed: bool :returns: process return code :rtype: int """ if path is None: path = '.' if path.startswith('/'): path = path[1:] dcos_client = mesos.DCOSClient() task_objects = mesos.get_master(dcos_client).tasks( fltr=task, completed=completed, all_=all_) if len(task_objects) == 0: if task is None: raise DCOSException("No tasks found") else: raise DCOSException( 'Cannot find a task with ID containing "{}"'.format(task)) try: all_files = [] for task_obj in task_objects: dir_ = posixpath.join(task_obj.directory(), path) all_files += [ (task_obj['id'], dcos_client.browse(task_obj.slave(), dir_))] except DCOSHTTPException as e: if e.response.status_code == 404: raise DCOSException( 'Cannot access [{}]: No such file or directory'.format(path)) else: raise add_header = len(all_files) > 1 for (task_id, files) in all_files: if add_header: emitter.publish('===> {} <==='.format(task_id)) if long_: emitter.publish(tables.ls_long_table(files)) else: emitter.publish( ' '.join(posixpath.basename(file_['path']) for file_ in files))
def test_log_slave_unavailable(): """ Test slave's state.json being unavailable """ client = mesos.DCOSClient() client.get_slave_state = _mock_exception() with patch('dcos.mesos.DCOSClient', return_value=client): args = ['task', 'log', 'test-app1'] stderr = (b"""Error accessing slave: exception\n""" b"""No matching tasks. Exiting.\n""") assert_mock(main, args, returncode=1, stderr=stderr)
def _decommission(mesos_id): try: mesos.DCOSClient().mark_agent_gone(mesos_id) except errors.DCOSException as e: emitter.publish( DefaultError("Couldn't mark agent {} as gone :\n\n{}".format( mesos_id, e))) return 1 emitter.publish("Agent {} has been marked as gone.".format(mesos_id))
def _shutdown(service_id): """Shuts down a service :param service_id: the id for the service :type service_id: str :returns: process return code :rtype: int """ mesos.DCOSClient().shutdown_framework(service_id) return 0
def log_job(args): dcos_client = mesos.DCOSClient() task = mesos.get_master(dcos_client).task(args['<submissionId>']) log_file = args.get('--file', "stdout") if log_file is None: log_file = "stdout" mesos_file = mesos.MesosFile(log_file, task=task, dcos_client=dcos_client) lines_count = args.get('--lines_count', "10") if lines_count is None: lines_count = "10" return log.log_files([mesos_file], args['--follow'], int(lines_count))
def _list(json_, extra_field_names): """List DC/OS nodes :param json_: If true, output json. Otherwise, output a human readable table. :type json_: bool :param extra_field_names: List of additional field names to include in table output :type extra_field_names: [str] :returns: process return code :rtype: int """ client = mesos.DCOSClient() masters = mesos.MesosDNSClient().masters() master_state = client.get_master_state() slaves = client.get_state_summary()['slaves'] for master in masters: if master['ip'] == master_state['hostname']: master['type'] = 'master (leader)' region, zone = util.get_fault_domain(master_state) master['region'] = region master['zone'] = zone for key in ('id', 'pid', 'version'): master[key] = master_state.get(key) else: master['type'] = 'master' for slave in slaves: region, zone = util.get_fault_domain(slave) slave['type'] = 'agent' slave['region'] = region slave['zone'] = zone nodes = masters + slaves if json_: emitter.publish(nodes) else: for extra_field_name in extra_field_names: field_name = extra_field_name.split(':')[-1] if len(slaves) > 0: try: tables._dotted_itemgetter(field_name)(slaves[0]) except KeyError: emitter.publish( errors.DefaultError('Field "%s" is invalid.' % field_name)) return table = tables.node_table(nodes, extra_field_names) output = six.text_type(table) if output: emitter.publish(output) else: emitter.publish(errors.DefaultError('No agents found.'))
def _main(): signal.signal(signal.SIGINT, signal_handler) args = docopt.docopt(_doc(), version='dcos version {}'.format(dcoscli.version), options_first=True) log_level = args['--log-level'] if log_level and not _config_log_level_environ(log_level): return 1 if args['--debug']: os.environ[constants.DCOS_DEBUG_ENV] = 'true' util.configure_process_from_environ() if args['<command>'] != 'config' and \ not auth.check_if_user_authenticated(): auth.force_auth() config = util.get_config() set_ssl_info_env_vars(config) command = args['<command>'] http.silence_requests_warnings() if not command: command = "help" executable = subcommand.command_executables(command) cluster_id = None if dcoscli.version != 'SNAPSHOT' and command and \ command not in ["config", "help"]: try: cluster_id = mesos.DCOSClient().metadata().get('CLUSTER_ID') except DCOSAuthenticationException: raise except: msg = 'Unable to get the cluster_id of the cluster.' logger.exception(msg) # the call to retrieve cluster_id must happen before we run the subcommand # so that if you have auth enabled we don't ask for user/pass multiple # times (with the text being out of order) before we can cache the auth # token subproc = Popen([executable, command] + args['<args>'], stderr=PIPE) if dcoscli.version != 'SNAPSHOT': return analytics.wait_and_track(subproc, cluster_id) else: return analytics.wait_and_capture(subproc)[0]
def __init__(self, hosts=[]): self.dcos_client = mesos.DCOSClient() self.agents = [] self.scheduled = None self.maintenance_status = None self.machine_ids = [] self.full_maintenance_status = [] self.get_agents() self.get_scheduled() self.get_maintenance_status() self.get_machines_ids(hosts) self.get_full_maintenance_status()
def get_tasks(task_id='', completed=True): """ Get a list of tasks, optionally filtered by task name. :param task_id: task ID :type task_id: str :param completed: include completed tasks? :type completed: bool :return: a tuple of tasks :rtype: tuple """ client = mesos.DCOSClient() master = mesos.Master(client.get_master_state()) tasks = master.tasks(completed=completed, fltr=task_id) return tasks
def get_ssh_proxy_options(ssh_options, user_options='', proxy_ip=None, master_proxy=False): """Returns the SSH proxy arguments for the given parameters. :param ssh_options: SSH options :type ssh_options: str :param user_option: SSH user option string :type user_option: str :param proxy_ip: SSH proxy node :type proxy_ip: str | None :param master_proxy: Use master nodes as proxy :type master_proxy: boolean :rtype: str """ if not proxy_ip: dcos_config_proxy_ip = config.get_config_val("core.ssh_proxy_ip") if dcos_config_proxy_ip: proxy_ip = dcos_config_proxy_ip elif master_proxy: dcos_client = mesos.DCOSClient() master_public_ip = dcos_client.metadata().get('PUBLIC_IPV4') if not master_public_ip: raise DCOSException(("Cannot use --master-proxy. Failed to " "find 'PUBLIC_IPV4' at {}").format( dcos_client.get_dcos_url('metadata'))) proxy_ip = master_public_ip if not proxy_ip: return '' if proxy_ip and not os.environ.get('SSH_AUTH_SOCK') and not ssh_options: raise DCOSException( "There is no SSH_AUTH_SOCK env variable, which likely means " "you aren't running `ssh-agent`. `dcos node ssh " "--master-proxy/--proxy-ip` depends on `ssh-agent` to safely " "use your private key to hop between nodes in your cluster. " "Please run `ssh-agent`, then add your private key with " "`ssh-add`.") proxy_options = '-A -t {0} {1} {2} -- ssh'.format(ssh_options, user_options, proxy_ip) return proxy_options
def get_tasks(task_id='', completed=True): """ Get a list of tasks, optionally filtered by task id. The task_id can be the abbrevated. Example: If a task named 'sleep' is scaled to 3 in marathon, there will be be 3 tasks starting with 'sleep.' :param task_id: task ID :type task_id: str :param completed: include completed tasks? :type completed: bool :return: a list of tasks :rtype: [] """ client = mesos.DCOSClient() master = mesos.Master(client.get_master_state()) mesos_tasks = master.tasks(completed=completed, fltr=task_id) return [task.__dict__['_task'] for task in mesos_tasks]
def _log_task(task_id, follow, lines, file_): """Prints the contents of the logs for a given task ID. :param task_id: task ID :type task_id: str :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param file_: file path to read :type file_: str :returns: process return code :rtype: int """ dcos_client = mesos.DCOSClient() task = mesos.get_master(dcos_client).task(task_id) mesos_file = mesos.MesosFile(file_, task=task, dcos_client=dcos_client) return log.log_files([mesos_file], follow, lines)
def _log_marathon(follow, lines, ssh_config_file): """Prints the contents of the marathon logs. Proxy through the master because marathon only runs on the master. :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param ssh_config_file: SSH config file. :type ssh_config_file: str | None ;:returns: process return code :rtype: int """ ssh_options = util.get_ssh_options(ssh_config_file, []) journalctl_args = '' if follow: journalctl_args += '-f ' if lines: journalctl_args += '-n {} '.format(lines) leader_ip = marathon.create_client().get_leader().split(':')[0] user_string = 'core@' if ssh_config_file: user_string = '' dcos_client = mesos.DCOSClient() master_public_ip = dcos_client.metadata().get('PUBLIC_IPV4') service = 'dcos-marathon' cmd = "ssh -At {0}{1}{2} ssh -At {0}{1}{3} journalctl {4}-u {5}".format( ssh_options, user_string, master_public_ip, leader_ip, journalctl_args, service) emitter.publish(DefaultError("Running `{}`".format(cmd))) return subprocess.Subproc().call(cmd, shell=True)
def gen_hosts(ssh_client): dcos_client = mesos.DCOSClient() mesos_hosts = [] dns_hosts = [] for host in mesos.MesosDNSClient().hosts('master.mesos.'): mesos_hosts.append(host['ip']) summary = dcos_client.get_state_summary() for host in summary['slaves']: mesos_hosts.append(host['hostname']) scom = 'cat /etc/resolv.conf' _, query_stdout, _ = ssh_client.exec_command(scom, get_pty=True) for line in query_stdout.readlines(): if line.startswith('nameserver'): host = line.strip().split()[1] dns_hosts.append(host) return (mesos_hosts, dns_hosts)
def _base_properties(conf=None): """ These properties are sent with every analytics event. :param conf: dcos config file :type conf: Toml :rtype: dict """ if not conf: conf = util.get_config() if len(sys.argv) > 1: cmd = 'dcos ' + _command() full_cmd = 'dcos ' + ' '.join(sys.argv[1:]) else: cmd = 'dcos' full_cmd = 'dcos' try: dcos_hostname = six.moves.urllib.parse.urlparse( conf.get('core.dcos_url')).hostname except: logger.exception('Unable to find the hostname of the cluster.') dcos_hostname = None try: cluster_id = mesos.DCOSClient().metadata().get('CLUSTER_ID') except: logger.exception('Unable to get the cluster_id of the cluster.') cluster_id = None return { 'cmd': cmd, 'full_cmd': full_cmd, 'dcoscli.version': dcoscli.version, 'python_version': str(sys.version_info), 'config': json.dumps(list(conf.property_items())), 'DCOS_HOSTNAME': dcos_hostname, 'CLUSTER_ID': cluster_id }
def uninstall(package_name, remove_all, app_id, cli, app): """Uninstalls a package. :param package_name: The package to uninstall :type package_name: str :param remove_all: Whether to remove all instances of the named app :type remove_all: boolean :param app_id: App ID of the app instance to uninstall :type app_id: str :param init_client: The program to use to run the app :type init_client: object :rtype: None """ if cli is False and app is False: cli = app = True uninstalled = False if cli: if subcommand.uninstall(package_name): uninstalled = True if app: num_apps = uninstall_app( package_name, remove_all, app_id, marathon.create_client(), mesos.DCOSClient()) if num_apps > 0: uninstalled = True if uninstalled: return None else: msg = 'Package [{}]'.format(package_name) if app_id is not None: msg += " with id [{}]".format(app_id) msg += " is not installed." raise DCOSException(msg)
def _ssh(master, slave, option, config_file, user): """SSH into a DCOS node using the IP addresses found in master's state.json :param master: True if the user has opted to SSH into the leading master :type master: bool | None :param slave: The slave ID if the user has opted to SSH into a slave :type slave: str | None :param option: SSH option :type option: [str] :param config_file: SSH config file :type config_file: str | None :param user: SSH user :type user: str | None :rtype: int :returns: process return code """ ssh_options = util.get_ssh_options(config_file, option) if master: host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip'] else: summary = mesos.DCOSClient().get_state_summary() slave_obj = next((slave_ for slave_ in summary['slaves'] if slave_['id'] == slave), None) if slave_obj: host = mesos.parse_pid(slave_obj['pid'])[1] else: raise DCOSException('No slave found with ID [{}]'.format(slave)) cmd = "ssh -t {0}{1}@{2}".format( ssh_options, user, host) emitter.publish(DefaultError("Running `{}`".format(cmd))) return subprocess.call(cmd, shell=True)
def _list(json_): """List DC/OS nodes :param json_: If true, output json. Otherwise, output a human readable table. :type json_: bool :returns: process return code :rtype: int """ client = mesos.DCOSClient() slaves = client.get_state_summary()['slaves'] if json_: emitter.publish(slaves) else: table = tables.slave_table(slaves) output = six.text_type(table) if output: emitter.publish(output) else: emitter.publish(errors.DefaultError('No slaves found.'))
def _ls(task, path, long_, completed): """ List files in a task's sandbox. :param task: task pattern to match :type task: str :param path: file path to read :type path: str :param long_: whether to use a long listing format :type long_: bool :param completed: If True, include completed tasks :type completed: bool :returns: process return code :rtype: int """ if path is None: path = '.' if path.startswith('/'): path = path[1:] dcos_client = mesos.DCOSClient() task_obj = mesos.get_master(dcos_client).task(fltr=task, completed=completed) dir_ = posixpath.join(task_obj.directory(), path) try: files = dcos_client.browse(task_obj.slave(), dir_) except DCOSHTTPException as e: if e.response.status_code == 404: raise DCOSException( 'Cannot access [{}]: No such file or directory'.format(path)) else: raise if files: if long_: emitter.publish(tables.ls_long_table(files)) else: emitter.publish(' '.join( posixpath.basename(file_['path']) for file_ in files))
def __init__(self, ctx): self._master_url = None self._zk_url = None self._marathon_url = None self._framework_url = None self.ctx = ctx try: self.ctx.vlog('Attempting to create DCOSClient') dcos_client = mesos.DCOSClient() self.client = dcos_client dcos_url = self.client.get_dcos_url('') ssl_verify = dcos_config.get_config().get('core.ssl_verify') self.ctx.vlog('DCOS core.ssl_verify value = ' + ssl_verify) if ssl_verify is not None and (not ssl_verify or ssl_verify == 'false'): self.ctx.vlog('Setting insecure_ssl to True') self.ctx.insecure_ssl = True if dcos_url is None: raise Exception("Unable to find DCOS server URL") dcos_url.rstrip('/') self.dcos_url = dcos_url except dcos_errors.DCOSException: raise Exception("DCOS is not configured properly")
def test_node_ssh_slave_with_private_ip(): slave_ip = mesos.DCOSClient().get_state_summary()['slaves'][0]['hostname'] _node_ssh(['--private-ip={}'.format(slave_ip), '--master-proxy'])
def __get_all_agents(): """Provides all agent json in the cluster which can be used for filtering""" client = mesos.DCOSClient() agents = client.get_state_summary()['slaves'] return agents
def _log(all_, follow, completed, lines, task, file_): """ Tail a file in the task's sandbox. :param all_: If True, include all tasks :type all_: bool :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 file_: file path to read :type file_: str :returns: process return code :rtype: int """ fltr = task if file_ is None: file_ = 'stdout' if lines is None: lines = 10 lines = util.parse_int(lines) # get tasks client = mesos.DCOSClient() master = mesos.Master(client.get_master_state()) tasks = master.tasks(fltr=fltr, completed=completed, all_=all_) if not tasks: if not fltr: raise DCOSException("No tasks found. Exiting.") elif not completed: completed_tasks = master.tasks(completed=True, fltr=fltr) if completed_tasks: msg = 'No running tasks match ID [{}]; however, there '.format( fltr) if len(completed_tasks) > 1: msg += 'are {} matching completed tasks. '.format( len(completed_tasks)) else: msg += 'is 1 matching completed task. ' msg += 'Run with --completed to see these logs.' raise DCOSException(msg) raise DCOSException('No matching tasks. Exiting.') # if journald logging is disabled, read files API and exit. if not log.dcos_log_enabled(): mesos_files = _mesos_files(tasks, file_, client) if not mesos_files: if fltr is None: msg = "No tasks found. Exiting." else: msg = "No matching tasks. Exiting." raise DCOSException(msg) log.log_files(mesos_files, follow, lines) return 0 # otherwise if file_ in ('stdout', 'stderr'): _dcos_log(follow, tasks, lines, file_, completed) return 0 raise DCOSException('Invalid file {}. dcos-log only ' 'supports stdout/stderr'.format(file_)) return 1
def test_node_ssh_slave_with_command(): slave = mesos.DCOSClient().get_state_summary()['slaves'][0] _node_ssh([ '--mesos-id={}'.format(slave['id']), '--master-proxy', '/opt/mesosphere/bin/detect_ip' ], 0, slave['hostname'])
def test_node_ssh_with_command(): leader_hostname = mesos.DCOSClient().get_state_summary()['hostname'] _node_ssh(['--leader', '--master-proxy', '/opt/mesosphere/bin/detect_ip'], 0, leader_hostname)