def _github_create_repo(cls, **kwargs): """Create repo on GitHub. If repository already exists then RunException will be raised. Raises: devassistant.exceptions.RunException on error """ username = cls._github_username(**kwargs) reponame = cls._github_reponame(**kwargs) password = getpass.getpass(prompt='GitHub password:'******'Repository already exists on GitHub' logger.error(msg) raise exceptions.RunException(msg) else: new_repo = user.create_repo(reponame) logger.info('Your new repository: {0}'.format( new_repo.html_url)) except github.GithubException as e: msg = 'GitHub error: {0}'.format(e) logger.log(msg) raise exceptions.RunException(msg)
def run(cls, comm_type, comm, **kwargs): if comm_type in map(lambda x: 'log_{0}'.format(x), settings.LOG_LEVELS_MAP): logger.log(logging._levelNames[settings.LOG_LEVELS_MAP[comm_type[-1]]], comm) if comm_type[-1] in 'ce': raise exceptions.RunException(comm) else: logger.warning('Unknown logging command {0} with message {1}'.format(comm_type, comm))
def _github_create_repo(cls, **kwargs): """Create repo on GitHub. If repository already exists then RunException will be raised. Raises: devassistant.exceptions.RunException on error """ username = cls._github_username(**kwargs) reponame = cls._github_reponame(**kwargs) password = getpass.getpass(prompt='GitHub password:'******'Repository already exists on GitHub' logger.error(msg) raise exceptions.RunException(msg) else: new_repo = user.create_repo(reponame) logger.info('Your new repository: {0}'.format(new_repo.html_url)) except github.GithubException as e: msg = 'GitHub error: {0}'.format(e) logger.log(msg) raise exceptions.RunException(msg)
def run(cls, c): if c.comm_type in map(lambda x: 'log_{0}'.format(x), settings.LOG_LEVELS_MAP): logger.log(settings.LOG_SHORT_TO_NUM_LEVEL[c.comm_type[-1]], c.input_res) if c.comm_type[-1] in 'ce': e = exceptions.CommandException(c.input_res) e.already_logged = True raise e else: raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type)) return [True, c.input_res]
def run(cls, c): if c.comm_type in map(lambda x: 'log_{0}'.format(x), settings.LOG_LEVELS_MAP): logger.log(logging._levelNames[settings.LOG_LEVELS_MAP[c.comm_type[-1]]], c.input_res) if c.comm_type[-1] in 'ce': e = exceptions.CommandException(c.input_res) e.already_logged = True raise e else: raise exceptions.CommandException('Unknown command type {ct}.'.format(ct=c.comm_type)) return [True, c.input_res]
def run(cls, c): if c.comm_type in map(lambda x: "log_{0}".format(x), settings.LOG_LEVELS_MAP): logger.log(settings.LOG_SHORT_TO_NUM_LEVEL[c.comm_type[-1]], c.input_res) if c.comm_type[-1] in "ce": e = exceptions.CommandException(c.input_res) e.already_logged = True raise e else: raise exceptions.CommandException("Unknown command type {ct}.".format(ct=c.comm_type)) return [True, c.input_res]
def load_yaml_by_path(cls, path, log_debug=False): """Load a yaml file that is at given path""" try: return yaml.load(open(path, 'r'), Loader=Loader) or {} except (yaml.scanner.ScannerError, yaml.parser.ParserError) as e: log_level = logging.DEBUG if log_debug else logging.WARNING logger.log(log_level, 'Yaml error in {path} (line {ln}, column {col}): {err}'. format(path=path, ln=e.problem_mark.line, col=e.problem_mark.column, err=e.problem)) return None
def run(cls, comm_type, comm, **kwargs): if comm_type in map(lambda x: 'log_{0}'.format(x), settings.LOG_LEVELS_MAP): logger.log( logging._levelNames[settings.LOG_LEVELS_MAP[comm_type[-1]]], comm) if comm_type[-1] in 'ce': raise exceptions.RunException(comm) else: logger.warning( 'Unknown logging command {0} with message {1}'.format( comm_type, comm))
def load_yaml_by_path(cls, path, log_debug=False): """Load a yaml file that is at given path""" try: return yaml.load(open(path, 'r'), Loader=Loader) or {} except (yaml.scanner.ScannerError, yaml.parser.ParserError) as e: log_level = logging.DEBUG if log_debug else logging.WARNING logger.log(log_level, 'Yaml error in {path} (line {ln}, column {col}): {err}'.\ format(path=path, ln=e.problem_mark.line, col=e.problem_mark.column, err=e.problem)) return None
def load_yaml_by_path(cls, path, log_debug=False): """Load a yaml file that is at given path, if the path is not a string, it is assumed it's a file-like object""" try: if isinstance(path, six.string_types): return yaml.load(open(path, 'r'), Loader=Loader) or {} else: return yaml.load(path, Loader=Loader) or {} except (yaml.scanner.ScannerError, yaml.parser.ParserError) as e: log_level = logging.DEBUG if log_debug else logging.WARNING logger.log(log_level, 'Yaml error in {path} (line {ln}, column {col}): {err}'. format(path=path, ln=e.problem_mark.line, col=e.problem_mark.column, err=e.problem)) return None
def load_yaml_by_path(cls, path, log_debug=False): """Load a yaml file that is at given path, if the path is not a string, it is assumed it's a file-like object""" try: if isinstance(path, six.string_types): return yaml.load(open(path, 'r'), Loader=Loader) or {} else: return yaml.load(path, Loader=Loader) or {} except (yaml.scanner.ScannerError, yaml.parser.ParserError) as e: log_level = logging.DEBUG if log_debug else logging.WARNING logger.log( log_level, 'Yaml error in {path} (line {ln}, column {col}): {err}'.format( path=path, ln=e.problem_mark.line, col=e.problem_mark.column, err=e.problem)) return None
def run_command(cls, cmd_str, log_level=logging.DEBUG, scls=[], ignore_sigint=False): """Runs a command from string, e.g. "cp foo bar" """ # format for scl execution if needed cmd_str = cls.format_for_scls(cmd_str, scls) logger.log(log_level, cmd_str, extra={'event_type': 'cmd_call'}) if cmd_str.startswith('cd '): # special-case cd to behave like shell cd and stay in the directory try: # delete any qoutes, the quoting is automatical in os.chdir directory = cmd_str.split()[1].replace('"', '').replace('\'', '') os.chdir(directory) except OSError as e: raise exceptions.ClException(cmd_str, 1, str(e)) return '' stdin_pipe = None stdout_pipe = subprocess.PIPE stderr_pipe = subprocess.STDOUT preexec_fn = cls.ignore_sigint if ignore_sigint else None proc = subprocess.Popen(cmd_str, stdin=stdin_pipe, stdout=stdout_pipe, stderr=stderr_pipe, shell=True, preexec_fn=preexec_fn) stdout = [] while proc.poll() == None: output = proc.stdout.readline().strip().decode('utf8') stdout.append(output) logger.log(log_level, output, extra={'event_type': 'cmd_out'}) stdout = '\n'.join(stdout) # there may be some remains not read after exiting the previous loop output_rest = proc.stdout.read().strip().decode('utf8') # we want to log lines separately, not as one big chunk output_rest_lines = output_rest.splitlines() for i, l in enumerate(output_rest.splitlines()): logger.log(log_level, l, extra={'event_type': 'cmd_out'}) # if rest of the output doesn't end with \n, we don't add \n after last line if i == len(output_rest_lines) - 1 and not output_rest.endswith('\n'): stdout += l # else we add it everytime else: stdout += l + '\n' # log return code always on debug level logger.log(logging.DEBUG, proc.returncode, extra={'event_type': 'cmd_retcode'}) if proc.returncode == 0: return stdout.strip() else: raise exceptions.ClException(cmd_str, proc.returncode, stdout)
def log(cls, level, msg, event_type, secret): extra = {'event_type': event_type} if secret: if event_type == 'cmd_call': logger.log(level, 'LOGGING PREVENTED FOR SECURITY REASONS', extra=extra) elif event_type == 'cmd_retcode': logger.log(level, msg, extra=extra) # no output logging for secret commands else: logger.log(level, msg, extra=extra)
def _run_one_section(self, section, kwargs): skip_else = False for i, command_dict in enumerate(section): if self.stop_flag: break for comm_type, comm in command_dict.items(): if comm_type.startswith('call'): # calling workflow: # 1) get proper run section (either from self or from snippet) # 2) if running snippet, add its files to kwargs['__files__'] # 3) actually run # 4) if running snippet, pop its files from kwargs['__files__'] sect = self._get_section_from_call(comm, 'run') if sect is None: logger.warning( 'Couldn\'t find section to run: {0}.'.format(comm)) continue if self._is_snippet_call(comm, **kwargs): # we're calling a snippet => add files and template_dir to kwargs snippet = yaml_snippet_loader.YamlSnippetLoader.get_snippet_by_name( comm.split('.')[0]) if '__files__' not in kwargs: kwargs['__files__'] = [] kwargs['__template_dir__'] = [] kwargs['__files__'].append(snippet.get_files_section()) kwargs['__template_dir__'].append( snippet.get_template_dir()) self._run_one_section(sect, copy.deepcopy(kwargs)) if self._is_snippet_call(comm, **kwargs): kwargs['__files__'].pop() kwargs['__template_dir__'].pop() elif comm_type.startswith('$'): # intentionally pass kwargs as dict, not as keywords try: self._assign_variable(comm_type, comm, kwargs) except exceptions.YamlSyntaxError as e: logger.error(e) raise e elif comm_type.startswith('if'): possible_else = None if len(section) > i + 1: # do we have "else" clause? possible_else = list(section[i + 1].items())[0] _, skip_else, to_run = self._get_section_from_condition( (comm_type, comm), possible_else, **kwargs) if to_run: # run with original kwargs, so that they might be changed for code after this if self._run_one_section(to_run, kwargs) elif comm_type == 'else': if not skip_else: logger.warning( 'Yaml error: encountered "else" with no associated "if", skipping.' ) skip_else = False elif comm_type.startswith('for'): # syntax: "for $i in $x: <section> or "for $i in cl_command: <section>" try: control_var, expression = self._parse_for(comm_type) except exceptions.YamlSyntaxError as e: logger.error(e) raise e try: eval_expression = evaluate_expression() except exceptions.YamlSyntaxError as e: logger.log(e) raise e for i in eval_expression: kwargs[control_var] = i self._run_one_section(comm, kwargs) elif comm_type.startswith('scl'): if '__scls__' not in kwargs: kwargs['__scls__'] = [] # list of lists of scl names kwargs['__scls__'].append(comm_type.split()[1:]) self._run_one_section(comm, kwargs) kwargs['__scls__'].pop() else: files = kwargs['__files__'][-1] if kwargs.get( '__files__', None) else self._files template_dir = kwargs['__template_dir__'][-1] if kwargs.get( '__template_dir__', None) else self.template_dir run_command( comm_type, CommandFormatter.format(comm_type, comm, template_dir, files, **kwargs), **kwargs)
def run_command(cls, cmd_str, log_level=logging.DEBUG, ignore_sigint=False, output_callback=None, as_user=None): """Runs a command from string, e.g. "cp foo bar" Args: cmd_str: the command to run as string log_level: level at which to log command output (DEBUG by default) ignore_sigint: should we ignore sigint during this command (False by default) output_callback: function that gets called with every line of output as argument as_user: run as specified user (the best way to do this will be deduced by DA) runs as current user if as_user == None """ # run format processors on cmd_str for name, cmd_proc in cls.command_processors.items(): cmd_str = cmd_proc(cmd_str) # TODO: how to do cd with as_user? if as_user and not cmd_str.startswith('cd '): cmd_str = cls.format_for_another_user(cmd_str, as_user) logger.log(log_level, cmd_str, extra={'event_type': 'cmd_call'}) if cmd_str.startswith('cd '): # special-case cd to behave like shell cd and stay in the directory try: directory = cmd_str[3:] # delete any quotes, os.chdir doesn't split words like sh does if directory[0] == directory[-1] == '"': directory = directory[1:-1] os.chdir(directory) except OSError as e: raise exceptions.ClException(cmd_str, 1, six.text_type(e)) return '' stdin_pipe = None stdout_pipe = subprocess.PIPE stderr_pipe = subprocess.STDOUT preexec_fn = cls.ignore_sigint if ignore_sigint else None proc = subprocess.Popen(cmd_str, stdin=stdin_pipe, stdout=stdout_pipe, stderr=stderr_pipe, shell=True, preexec_fn=preexec_fn) # register process to cls.subprocesses cls.subprocesses[proc.pid] = proc stdout = [] while proc.poll() is None: output = proc.stdout.readline().decode('utf8') if output: output = output.strip() stdout.append(output) logger.log(log_level, output, extra={'event_type': 'cmd_out'}) if output_callback: output_callback(output) # remove process from cls.subprocesses cls.subprocesses.pop(proc.pid) # add a newline to the end - if there is more output in output_rest, we'll be appending # it line by line; if there's no more output, we strip anyway stdout = '\n'.join(stdout) + '\n' # there may be some remains not read after exiting the previous loop output_rest = proc.stdout.read().strip().decode('utf8') # we want to log lines separately, not as one big chunk output_rest_lines = output_rest.splitlines() for i, l in enumerate(output_rest_lines): logger.log(log_level, l, extra={'event_type': 'cmd_out'}) # add newline for every line - for last line, only add it if it was originally present if i != len(output_rest_lines) - 1 or output_rest.endswith('\n'): l += '\n' stdout += l if output_callback: output_callback(l) # log return code always on debug level logger.log(logging.DEBUG, proc.returncode, extra={'event_type': 'cmd_retcode'}) stdout = stdout.strip() if proc.returncode == 0: return stdout else: raise exceptions.ClException(cmd_str, proc.returncode, stdout)
def _run_one_section(self, section, kwargs): skip_else = False for i, command_dict in enumerate(section): if self.stop_flag: break for comm_type, comm in command_dict.items(): if comm_type.startswith('call'): # calling workflow: # 1) get proper run section (either from self or from snippet) # 2) if running snippet, add its files to kwargs['__files__'] # 3) actually run # 4) if running snippet, pop its files from kwargs['__files__'] sect = self._get_section_from_call(comm, 'run') if sect is None: logger.warning('Couldn\'t find section to run: {0}.'.format(comm)) continue if self._is_snippet_call(comm, **kwargs): # we're calling a snippet => add files and template_dir to kwargs snippet = yaml_snippet_loader.YamlSnippetLoader.get_snippet_by_name(comm.split('.')[0]) if '__files__' not in kwargs: kwargs['__files__'] = [] kwargs['__template_dir__'] = [] kwargs['__files__'].append(snippet.get_files_section()) kwargs['__template_dir__'].append(snippet.get_template_dir()) self._run_one_section(sect, copy.deepcopy(kwargs)) if self._is_snippet_call(comm, **kwargs): kwargs['__files__'].pop() kwargs['__template_dir__'].pop() elif comm_type.startswith('$'): # intentionally pass kwargs as dict, not as keywords try: self._assign_variable(comm_type, comm, kwargs) except exceptions.YamlSyntaxError as e: logger.error(e) raise e elif comm_type.startswith('if'): possible_else = None if len(section) > i + 1: # do we have "else" clause? possible_else = list(section[i + 1].items())[0] _, skip_else, to_run = self._get_section_from_condition((comm_type, comm), possible_else, **kwargs) if to_run: # run with original kwargs, so that they might be changed for code after this if self._run_one_section(to_run, kwargs) elif comm_type == 'else': if not skip_else: logger.warning('Yaml error: encountered "else" with no associated "if", skipping.') skip_else = False elif comm_type.startswith('for'): # syntax: "for $i in $x: <section> or "for $i in cl_command: <section>" try: control_var, expression = self._parse_for(comm_type) except exceptions.YamlSyntaxError as e: logger.error(e) raise e try: eval_expression = evaluate_expression() except exceptions.YamlSyntaxError as e: logger.log(e) raise e for i in eval_expression: kwargs[control_var] = i self._run_one_section(comm, kwargs) elif comm_type.startswith('scl'): if '__scls__' not in kwargs: kwargs['__scls__'] = [] # list of lists of scl names kwargs['__scls__'].append(comm_type.split()[1:]) self._run_one_section(comm, kwargs) kwargs['__scls__'].pop() else: files = kwargs['__files__'][-1] if kwargs.get('__files__', None) else self._files template_dir = kwargs['__template_dir__'][-1] if kwargs.get('__template_dir__', None) else self.template_dir run_command(comm_type, CommandFormatter.format(comm_type, comm, template_dir, files, **kwargs), **kwargs)
def run_command(cls, cmd_str, log_level=logging.DEBUG, scls=[], ignore_sigint=False, output_callback=None): """Runs a command from string, e.g. "cp foo bar" Args: cmd_str: the command to run as string log_level: level at which to log command output (DEBUG by default) scls: list of ['enable', 'foo', 'bar'] (scriptlet name + arbitrary number of scl names) ignore_sigint: should we ignore sigint during this command (False by default) output_callback: function that gets called with every line of output as argument """ # format for scl execution if needed cmd_str = cls.format_for_scls(cmd_str, scls) logger.log(log_level, cmd_str, extra={'event_type': 'cmd_call'}) if cmd_str.startswith('cd '): # special-case cd to behave like shell cd and stay in the directory try: # delete any qoutes, the quoting is automatical in os.chdir directory = cmd_str.split()[1].replace('"', '').replace('\'', '') os.chdir(directory) except OSError as e: raise exceptions.ClException(cmd_str, 1, six.text_type(e)) return '' stdin_pipe = None stdout_pipe = subprocess.PIPE stderr_pipe = subprocess.STDOUT preexec_fn = cls.ignore_sigint if ignore_sigint else None proc = subprocess.Popen(cmd_str, stdin=stdin_pipe, stdout=stdout_pipe, stderr=stderr_pipe, shell=True, preexec_fn=preexec_fn) stdout = [] while proc.poll() is None: output = proc.stdout.readline().decode('utf8') if output: output = output.strip() stdout.append(output) logger.log(log_level, output, extra={'event_type': 'cmd_out'}) if output_callback: output_callback(output) # add a newline to the end - if there is more output in output_rest, we'll be appending # it line by line; if there's no more output, we strip anyway stdout = '\n'.join(stdout) + '\n' # there may be some remains not read after exiting the previous loop output_rest = proc.stdout.read().strip().decode('utf8') # we want to log lines separately, not as one big chunk output_rest_lines = output_rest.splitlines() for i, l in enumerate(output_rest_lines): logger.log(log_level, l, extra={'event_type': 'cmd_out'}) # add newline for every line - for last line, only add it if it was originally present if i != len(output_rest_lines) - 1 or output_rest.endswith('\n'): l += '\n' stdout += l if output_callback: output_callback(l) # log return code always on debug level logger.log(logging.DEBUG, proc.returncode, extra={'event_type': 'cmd_retcode'}) stdout = stdout.strip() if proc.returncode == 0: return stdout else: raise exceptions.ClException(cmd_str, proc.returncode, stdout)