def _deployment_watch(deployment_id, max_count, interval): """ :param deployment_id: the application id :type deployment_di: str :param max_count: maximum number of polling calls :type max_count: str :param interval: wait interval in seconds between polling calls :type interval: str :returns: process return code :rtype: int """ if max_count is not None: max_count = util.parse_int(max_count) interval = 1 if interval is None else util.parse_int(interval) client = marathon.create_client() count = 0 while max_count is None or count < max_count: deployment = client.get_deployment(deployment_id) if deployment is None: return 0 emitter.publish(deployment) time.sleep(interval) count += 1 return 0
def _log(follow, lines, master, slave): """ Prints the contents of master and slave logs. :param follow: same as unix tail's -f :type follow: bool :param lines: number of lines to print :type lines: int :param master: whether to print the master log :type master: bool :param slave: the slave ID to print :type slave: str | None :returns: process return code :rtype: int """ if not (master or slave): raise DCOSException('You must choose one of --master or --slave.') lines = util.parse_int(lines) mesos_files = _mesos_files(master, slave) log.log_files(mesos_files, follow, lines) return 0
def _log(follow, lines, ssh_config_file, service, file_): """Prints the contents of the logs for a given service. The service task is located by first identifying the marathon app running a framework named `service`. :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. Used for marathon. :type ssh_config_file: str | None :param service: service name :type service: str :param file_: file path to read :type file_: str :returns: process return code :rtype: int """ lines = util.parse_int(lines) if service == 'marathon': if file_: raise DCOSException('The <file> argument is invalid for marathon.' ' The systemd journal is always used for the' ' marathon log.') return _log_marathon(follow, lines, ssh_config_file) else: if ssh_config_file: raise DCOSException( 'The `--ssh-config-file` argument is invalid for non-marathon ' 'services. SSH is not used.') return _log_service(follow, lines, service, file_)
def _unset(name, index): """ :returns: process status :rtype: int """ config_path, toml_config = _load_config() toml_config_pre = copy.deepcopy(toml_config) section = name.split(".", 1)[0] if section not in toml_config_pre._dictionary: toml_config_pre._dictionary[section] = {} value = toml_config.pop(name, None) if value is None: raise DCOSException("Property {!r} doesn't exist".format(name)) elif isinstance(value, collections.Mapping): raise DCOSException(_generate_choice_msg(name, value)) elif (isinstance(value, collections.Sequence) and not isinstance(value, six.string_types)): if index is not None: index = util.parse_int(index) if index < 0 or index >= len(value): raise DCOSException( 'Index ({}) is out of bounds - possible values are ' 'between {} and {}'.format(index, 0, len(value) - 1)) value.pop(index) toml_config[name] = value elif index is not None: raise DCOSException( 'Unsetting based on an index is only supported for lists') _save_config_file(config_path, toml_config) return 0
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' lines = util.parse_int(lines) mesos_files = _mesos_files(completed, fltr, file_) if not mesos_files: raise DCOSException('No matching tasks. Exiting.') log.log_files(mesos_files, follow, lines) return 0
def _calculate_version(client, app_id, version): """ :param client: Marathon client :type client: dcos.marathon.Client :param app_id: The ID of the application :type app_id: str :param version: Relative or absolute version or None :type version: str :returns: The absolute version as an ISO8601 date-time :rtype: str """ # First let's try to parse it as a negative integer try: value = util.parse_int(version) except DCOSException: logger.exception('Unable to parse version %s', version) return version else: if value < 0: value = -1 * value # We have a negative value let's ask Marathon for the last # abs(value) versions = client.get_app_versions(app_id, value + 1) if len(versions) <= value: # We don't have enough versions. Return an error. msg = "Application {!r} only has {!r} version(s)." raise DCOSException(msg.format(app_id, len(versions), value)) else: return versions[value] else: raise DCOSException( 'Relative versions must be negative: {}'.format(version))
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 """ 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 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.') 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
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 """ 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 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.') 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('No matching tasks. Exiting.') log.log_files(mesos_files, follow, lines) return 0
def deployment_watch(self, deployment_id, max_count, interval): """ :param deployment_id: the application id :type deployment_id: str :param max_count: maximum number of polling calls :type max_count: str :param interval: wait interval in seconds between polling calls :type interval: str :returns: process return code :rtype: int """ if max_count is not None: max_count = util.parse_int(max_count) interval = 1 if interval is None else util.parse_int(interval) client = self._create_marathon_client() count = 0 while max_count is None or count < max_count: deployment = client.get_deployment(deployment_id) if deployment is None: return 0 if util.is_windows_platform(): os.system('cls') else: if 'TERM' in os.environ: os.system('clear') emitter.publish('Deployment update time: ' '{} \n'.format( time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))) emitter.publish(deployment) time.sleep(interval) count += 1 return 0
def _deployment_watch(deployment_id, max_count, interval): """ :param deployment_id: the application id :type deployment_di: str :param max_count: maximum number of polling calls :type max_count: str :param interval: wait interval in seconds between polling calls :type interval: str :returns: process return code :rtype: int """ if max_count is not None: max_count = util.parse_int(max_count) interval = 1 if interval is None else util.parse_int(interval) client = marathon.create_client() count = 0 while max_count is None or count < max_count: deployment = client.get_deployment(deployment_id) if deployment is None: return 0 if util.is_windows_platform(): os.system('cls') else: if 'TERM' in os.environ: os.system('clear') emitter.publish('Deployment update time: ' '{} \n'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))) emitter.publish(deployment) time.sleep(interval) count += 1 return 0
def _log(follow, lines, leader, slave, component, filters): """ Prints the contents of leader and slave logs. :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 :returns: process return code :rtype: int """ if not (leader or slave): raise DCOSException('You must choose one of --leader or --mesos-id.') if lines is None: lines = 10 lines = util.parse_int(lines) if log.dcos_log_enabled(version=2): _dcos_log_v2(follow, lines, leader, slave, component, filters) return 0 if not log.has_journald_capability(): if component or filters: raise DCOSException('--component or --filter is not ' 'supported by files API') # fall back to mesos files API. mesos_files = _mesos_files(leader, slave) log.log_files(mesos_files, follow, lines) return 0 # dcos-log does not support logs from leader and agent. if leader and slave: raise DCOSException( 'You must choose one of --leader or --mesos-id.') # if journald logging enabled. _dcos_log(follow, lines, leader, slave, component, filters) return 0
def _unset(name, index): """ :returns: process status :rtype: int """ toml_config = util.get_config(True) toml_config_pre = copy.deepcopy(toml_config) section = name.split(".", 1)[0] if section not in toml_config_pre._dictionary: toml_config_pre._dictionary[section] = {} value = toml_config.pop(name, None) if value is None: raise DCOSException("Property {!r} doesn't exist".format(name)) elif isinstance(value, collections.Mapping): raise DCOSException(_generate_choice_msg(name, value)) elif ((isinstance(value, collections.Sequence) and not isinstance(value, six.string_types)) and index is not None): index = util.parse_int(index) if not value: raise DCOSException( 'Index ({}) is out of bounds - [{}] is empty'.format( index, name)) if index < 0 or index >= len(value): raise DCOSException( 'Index ({}) is out of bounds - possible values are ' 'between {} and {}'.format(index, 0, len(value) - 1)) popped_value = value.pop(index) emitter.publish( "[{}]: removed element '{}' at index '{}'".format( name, popped_value, index)) toml_config[name] = value config.save(toml_config) return 0 elif index is not None: raise DCOSException( 'Unsetting based on an index is only supported for lists') else: emitter.publish("Removed [{}]".format(name)) config.save(toml_config) return 0
def _start(app_id, instances, force): """Start a Marathon application. :param app_id: the id for the application :type app_id: str :param instances: the number of instances to start :type instances: str :param force: whether to override running deployments :type force: bool :returns: process return code :rtype: int """ # Check that the application exists client = marathon.create_client() desc = client.get_app(app_id) if desc['instances'] > 0: emitter.publish( 'Application {!r} already started: {!r} instances.'.format( app_id, desc['instances'])) return 1 # Need to add the 'id' because it is required app_json = {'id': app_id} # Set instances to 1 if not specified if instances is None: instances = 1 else: instances = util.parse_int(instances) if instances <= 0: emitter.publish( 'The number of instances must be positive: {!r}.'.format( instances)) return 1 app_json['instances'] = instances deployment = client.update_app(app_id, app_json, force) emitter.publish('Created deployment {}'.format(deployment)) return 0
def _log(follow, lines, leader, slave, component, filters): """ Prints the contents of leader and slave logs. :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 :returns: process return code :rtype: int """ if not (leader or slave): raise DCOSException('You must choose one of --leader or --mesos-id.') if lines is None: lines = 10 lines = util.parse_int(lines) # if journald logging is disabled. Read from files API and exit. # https://github.com/dcos/dcos/blob/master/gen/calc.py#L151 if 'journald' not in log.logging_strategy(): if component or filters: raise DCOSException('--component or --filter is not ' 'supported by files API') # fail back to mesos files API. mesos_files = _mesos_files(leader, slave) log.log_files(mesos_files, follow, lines) return 0 # dcos-log does not support logs from leader and agent. if leader and slave: raise DCOSException('You must choose one of --leader or --mesos-id.') # if journald logging enabled. _dcos_log(follow, lines, leader, slave, component, filters) return 0
def _log(follow, lines, leader, slave, component, filters): """ Prints the contents of leader and slave logs. :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 :returns: process return code :rtype: int """ if not (leader or slave) or (leader and slave): raise DCOSException( 'You must choose one of --leader or --mesos-id.') if lines is None: lines = 10 lines = util.parse_int(lines) try: _dcos_log(follow, lines, leader, slave, component, filters) return 0 except (DCOSAuthenticationException, DCOSAuthorizationException): raise except DCOSException as e: emitter.publish(DefaultError(e)) emitter.publish(DefaultError('Falling back to files API...')) if component or filters: raise DCOSException('--component or --filter is not ' 'supported by files API') # fail back to mesos files API. mesos_files = _mesos_files(leader, slave) log.log_files(mesos_files, follow, lines) return 0
def _log(follow, lines, leader, slave, component, filters): """ Prints the contents of leader and slave logs. :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 :returns: process return code :rtype: int """ if not (leader or slave): raise DCOSException('You must choose one of --leader or --mesos-id.') if lines is None: lines = 10 lines = util.parse_int(lines) if not log.has_journald_capability(): if component or filters: raise DCOSException('--component or --filter is not ' 'supported by files API') # fall back to mesos files API. mesos_files = _mesos_files(leader, slave) log.log_files(mesos_files, follow, lines) return 0 # dcos-log does not support logs from leader and agent. if leader and slave: raise DCOSException( 'You must choose one of --leader or --mesos-id.') # if journald logging enabled. _dcos_log(follow, lines, leader, slave, component, filters) return 0
def _unset(name, index): """ :returns: process status :rtype: int """ toml_config = util.get_config(True) toml_config_pre = copy.deepcopy(toml_config) section = name.split(".", 1)[0] if section not in toml_config_pre._dictionary: toml_config_pre._dictionary[section] = {} value = toml_config.pop(name, None) if value is None: raise DCOSException("Property {!r} doesn't exist".format(name)) elif isinstance(value, collections.Mapping): raise DCOSException(_generate_choice_msg(name, value)) elif ((isinstance(value, collections.Sequence) and not isinstance(value, six.string_types)) and index is not None): index = util.parse_int(index) if not value: raise DCOSException( 'Index ({}) is out of bounds - [{}] is empty'.format( index, name)) if index < 0 or index >= len(value): raise DCOSException( 'Index ({}) is out of bounds - possible values are ' 'between {} and {}'.format(index, 0, len(value) - 1)) popped_value = value.pop(index) emitter.publish("[{}]: removed element '{}' at index '{}'".format( name, popped_value, index)) toml_config[name] = value config.save(toml_config) return 0 elif index is not None: raise DCOSException( 'Unsetting based on an index is only supported for lists') else: emitter.publish("Removed [{}]".format(name)) config.save(toml_config) return 0
def _version_list(app_id, max_count): """ :param app_id: the id of the application :type app_id: str :param max_count: the maximum number of version to fetch and return :type max_count: str :returns: process return code :rtype: int """ if max_count is not None: max_count = util.parse_int(max_count) client = marathon.create_client() versions = client.get_app_versions(app_id, max_count) emitter.publish(versions) return 0
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