def _dcos_log(follow, lines, leader, slave, component, filters): """ Print logs from dcos-log backend. :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param leader: whether to print the leading master's log :type leader: bool :param slave: the slave ID to print :type slave: str | None :param component: DC/OS component name :type component: string :param filters: a list of filters ["key:value", ...] :type filters: list """ if not log.dcos_log_enabled(): raise DCOSException('dcos-log is not supported') filter_query = '' if component: filters.append('_SYSTEMD_UNIT:{}'.format(_get_unit_type(component))) for f in filters: key_value = f.split(':') if len(key_value) != 2: raise SystemExit('Invalid filter parameter {}. ' 'Must be --filter=key:value'.format(f)) filter_query += '&filter={}'.format(f) endpoint = '/system/v1' if leader: endpoint += '/logs/v1/' if slave: endpoint += '/agent/{}/logs/v1/'.format(slave) endpoint_type = 'range' if follow: endpoint_type = 'stream' dcos_url = config.get_config_val('core.dcos_url').rstrip("/") if not dcos_url: raise config.missing_config_exception(['core.dcos_url']) url = (dcos_url + endpoint + endpoint_type + '/?skip_prev={}'.format(lines) + filter_query) if follow: return log.follow_logs(url) return log.print_logs_range(url)
def _dcos_log(follow, lines, leader, slave, component, filters): """ Print logs from dcos-log backend. :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param leader: whether to print the leading master's log :type leader: bool :param slave: the slave ID to print :type slave: str | None :param component: DC/OS component name :type component: string :param filters: a list of filters ["key:value", ...] :type filters: list """ filter_query = '' if component: filters.append('_SYSTEMD_UNIT:{}'.format(_get_unit_type(component))) for f in filters: key_value = f.split(':') if len(key_value) != 2: raise SystemExit('Invalid filter parameter {}. ' 'Must be --filter=key:value'.format(f)) filter_query += '&filter={}'.format(f) endpoint = '/system/v1' if leader: endpoint += _build_leader_url(component) elif slave: endpoint += '/agent/{}/logs/v1/'.format(slave) endpoint_type = 'range' if follow: endpoint_type = 'stream' dcos_url = config.get_config_val('core.dcos_url').rstrip("/") if not dcos_url: raise config.missing_config_exception(['core.dcos_url']) url = (dcos_url + endpoint + endpoint_type + '/?skip_prev={}'.format(lines) + filter_query) if follow: return log.follow_logs(url) return log.print_logs_range(url)
def _dcos_log_v2(follow, lines, leader, slave, component, filters): """ Print logs from dcos-log v2 backend. :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param leader: whether to print the leading master's log :type leader: bool :param slave: the slave ID to print :type slave: str | None :param component: DC/OS component name :type component: string :param filters: a list of filters ["key:value", ...] :type filters: list """ filter_query = '' for f in filters: key_value = f.split(':') if len(key_value) != 2: raise DCOSException('Invalid filter parameter {}. ' 'Must be --filter=key:value'.format(f)) filter_query += '&filter={}'.format(f) endpoint = '/system/v1' if leader: endpoint += _build_leader_url(component, version=2) elif slave: endpoint += '/agent/{}/logs/v2/component'.format(slave) if component: component_with_type = _get_unit_type(component) endpoint += '/{}'.format(component_with_type) dcos_url = config.get_config_val('core.dcos_url').rstrip("/") if not dcos_url: raise config.missing_config_exception(['core.dcos_url']) # dcos-log v2 required the skip option be a negative integer if lines > 0: lines *= -1 url = dcos_url + endpoint + '?skip={}'.format(lines) + filter_query if follow: return log.follow_logs(url) return log.print_logs_range(url)
def _dcos_log_v2(follow, tasks, lines, file_): """ a client to dcos-log v2 :param follow: same as unix tail's -f :type follow: bool :param tasks: tasks pattern to match :type tasks: list of str :param lines: number of lines to print :type lines: int :param file_: file path to read :type file_: str """ if len(tasks) != 1: raise DCOSException( "found more than one task with the same name: {}. Please provide " "a unique task name.".format([task['id'] for task in tasks])) task = tasks[0] endpoint = '/system/v1/logs/v2/task/{}/file/{}'.format(task['id'], file_) dcos_url = config.get_config_val('core.dcos_url').rstrip('/') if not dcos_url: raise config.missing_config_exception(['core.dcos_url']) if lines: # according to dcos-log v2 API the skip parameter must be negative # integer. dcos-cli uses positive int to limit the number of last lines # use "lines * -1" to make it negative. if lines > 0: lines *= -1 endpoint += '?cursor=END&skip={}'.format(lines) url = dcos_url + endpoint if follow: return log.follow_logs(url) return log.print_logs_range(url)
def _dcos_log(follow, tasks, lines, file_, completed): """ a client to dcos-log :param follow: same as unix tail's -f :type follow: bool :param task: task pattern to match :type task: str :param lines: number of lines to print :type lines: int :param file_: file path to read :type file_: str :param completed: whether to include completed tasks :type completed: bool """ # only stdout and stderr is supported if file_ not in ('stdout', 'stderr'): raise DCOSException('Expect file stdout or stderr. ' 'Got {}'.format(file_)) # state json may container tasks and completed_tasks fields. Based on # user request we should traverse the appropriate field. tasks_field = 'tasks' if completed: tasks_field = 'completed_tasks' for task in tasks: executor_info = task.executor() if not executor_info: continue if (tasks_field not in executor_info and not isinstance(executor_info[tasks_field], list)): logger.debug('Executor info: {}'.format(executor_info)) raise DCOSException('Invalid executor info. ' 'Missing field {}'.format(tasks_field)) for t in executor_info[tasks_field]: container_id = get_nested_container_id(t) if not container_id: logger.debug('Executor info: {}'.format(executor_info)) raise DCOSException( 'Invalid executor info. Missing container id') # get slave_id field slave_id = t.get('slave_id') if not slave_id: logger.debug('Executor info: {}'.format(executor_info)) raise DCOSException( 'Invalid executor info. Missing field `slave_id`') framework_id = t.get('framework_id') if not framework_id: logger.debug('Executor info: {}'.format(executor_info)) raise DCOSException( 'Invalid executor info. Missing field `framework_id`') # try `executor_id` first. executor_id = t.get('executor_id') if not executor_id: # if `executor_id` is an empty string, default to `id`. executor_id = t.get('id') if not executor_id: logger.debug('Executor info: {}'.format(executor_info)) raise DCOSException( 'Invalid executor info. Missing executor id') dcos_url = config.get_config_val('core.dcos_url').rstrip('/') if not dcos_url: raise config.missing_config_exception(['core.dcos_url']) # dcos-log provides 2 base endpoints /range/ and /stream/ # for range and streaming requests. endpoint_type = 'range' if follow: endpoint_type = 'stream' endpoint = ('/system/v1/agent/{}/logs/v1/{}/framework/{}' '/executor/{}/container/{}'.format(slave_id, endpoint_type, framework_id, executor_id, container_id)) # append request parameters. # `skip_prev` will move the cursor to -n lines. # `filter=STREAM:{STDOUT,STDERR}` will filter logs by label. url = (dcos_url + endpoint + '?skip_prev={}&filter=STREAM:{}'.format(lines, file_.upper())) if follow: return log.follow_logs(url) return log.print_logs_range(url)