def _get_result(self, autoskip_selector="", interactive=False, interactive_default=True): if self.covered_by_autoskip_selector(autoskip_selector): io.debug(_( "autoskip matches {item} on {node}" ).format(item=self.id, node=self.node.name)) return (self.STATUS_SKIPPED, [_("cmdline")]) if interactive is False and self.attributes['interactive'] is True: return (self.STATUS_SKIPPED, [_("interactive only")]) if self.triggered and not self.has_been_triggered: io.debug(_("skipping {} because it wasn't triggered").format(self.id)) return (self.STATUS_SKIPPED, [_("no trigger")]) if self.unless: with io.job(_(" {node} {bundle} {item} checking 'unless' condition...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): unless_result = self.bundle.node.run( self.unless, may_fail=True, ) if unless_result.return_code == 0: io.debug(_("{node}:{bundle}:action:{name}: failed 'unless', not running").format( bundle=self.bundle.name, name=self.name, node=self.bundle.node.name, )) return (self.STATUS_SKIPPED, ["unless"]) if ( interactive and self.attributes['interactive'] is not False and not io.ask( wrap_question( self.id, self.attributes['command'], _("Run action {}?").format( bold(self.name), ), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ), interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), ) ): return (self.STATUS_SKIPPED, [_("interactive")]) try: self.run() return (self.STATUS_ACTION_SUCCEEDED, None) except ActionFailure: return (self.STATUS_FAILED, None)
def _test(self): with io.job(_("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): if self._faults_missing_for_attributes: self._raise_for_faults() return self.test()
def _fetch_secret(site, secret_id): try: return cache[site][secret_id] except KeyError: pass session = sessions.setdefault(getpid(), Session()) try: full_url = "{}/api/secrets/{}/".format( config.get(site, "url"), secret_id, ) credentials = ( config.get(site, "username"), config.get(site, "password"), ) except (NoSectionError, NoOptionError): raise FaultUnavailable( "Tried to get TeamVault secret with ID '{secret_id}' " "from site '{site}', but credentials missing in {path}".format( path=CONFIG_PATH, secret_id=secret_id, site=site, ), ) with io.job(_("{tv} fetching {secret}").format(tv=bold("TeamVault"), secret=secret_id)): response = session.get(full_url, auth=credentials) if response.status_code != 200: raise FaultUnavailable( "TeamVault returned {status} for {url}".format( status=response.status_code, url=full_url, ) ) secret = response.json() response = session.get(secret['current_revision'] + "data", auth=credentials) if response.status_code != 200: raise FaultUnavailable( "TeamVault returned {status} for {url}".format( status=response.status_code, url=full_url, ) ) secret['data'] = response.json() cache.setdefault(site, {})[secret_id] = secret return secret
def get_status(self, cached=True): """ Returns an ItemStatus instance describing the current status of the item on the actual node. """ with io.job( _(" {node} {bundle} {item} checking...").format( bundle=self.bundle.name, item=self.id, node=self.node.name ) ): if not cached: del self._cache["cached_sdict"] return ItemStatus(self.cached_cdict, self.cached_sdict)
def get_status(self, cached=True): """ Returns an ItemStatus instance describing the current status of the item on the actual node. """ with io.job(_("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): if not cached: del self._cache['cached_sdict'] return ItemStatus(self.cached_cdict, self.cached_sdict, self.display_dicts)
def _fetch_secret(site, secret_id): try: return cache[site][secret_id] except KeyError: pass session = sessions.setdefault(getpid(), Session()) try: full_url = "{}/api/secrets/{}/".format( config.get(site, "url"), secret_id, ) credentials = ( config.get(site, "username"), config.get(site, "password"), ) except (NoSectionError, NoOptionError): raise FaultUnavailable( "Tried to get TeamVault secret with ID '{secret_id}' " "from site '{site}', but credentials missing in {path}".format( path=CONFIG_PATH, secret_id=secret_id, site=site, ), ) with io.job( _("{tv} fetching {secret}").format(tv=bold("TeamVault"), secret=secret_id)): response = session.get(full_url, auth=credentials) if response.status_code != 200: raise FaultUnavailable("TeamVault returned {status} for {url}".format( status=response.status_code, url=full_url, )) secret = response.json() response = session.get(secret['current_revision'] + "data", auth=credentials) if response.status_code != 200: raise FaultUnavailable("TeamVault returned {status} for {url}".format( status=response.status_code, url=full_url, )) secret['data'] = response.json() cache.setdefault(site, {})[secret_id] = secret return secret
def run(self): with io.job(_(" {node} {bundle} {item} running...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): result = self.bundle.node.run( self.attributes['command'], may_fail=True, ) if self.attributes['expected_return_code'] is not None and \ not result.return_code == self.attributes['expected_return_code']: raise ActionFailure(_( "wrong return code for action '{action}' in bundle '{bundle}': " "expected {ecode}, but was {rcode}" ).format( action=self.name, bundle=self.bundle.name, ecode=self.attributes['expected_return_code'], rcode=result.return_code, )) if self.attributes['expected_stderr'] is not None and \ result.stderr_text != self.attributes['expected_stderr']: raise ActionFailure(_( "wrong stderr for action '{action}' in bundle '{bundle}'" ).format( action=self.name, bundle=self.bundle.name, )) if self.attributes['expected_stdout'] is not None and \ result.stdout_text != self.attributes['expected_stdout']: raise ActionFailure(_( "wrong stdout for action '{action}' in bundle '{bundle}'" ).format( action=self.name, bundle=self.bundle.name, )) return result
def run(self): if self.attributes['data_stdin'] is not None: data_stdin = self.attributes['data_stdin'] # Allow users to use either a string/unicode object or raw # bytes -- or Faults. if isinstance(data_stdin, Fault): data_stdin = data_stdin.value if type(data_stdin) is not bytes: data_stdin = data_stdin.encode('UTF-8') else: data_stdin = None with io.job( _(" {node} {bundle} {item} running...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): result = self.bundle.node.run( self.attributes['command'], data_stdin=data_stdin, may_fail=True, ) if self.attributes['expected_return_code'] is not None and \ not result.return_code == self.attributes['expected_return_code']: raise ActionFailure( _("wrong return code: {}").format(result.return_code)) if self.attributes['expected_stderr'] is not None and \ result.stderr_text != self.attributes['expected_stderr']: raise ActionFailure(_("wrong stderr")) if self.attributes['expected_stdout'] is not None and \ result.stdout_text != self.attributes['expected_stdout']: raise ActionFailure(_("wrong stdout")) return result
def run(self): with io.job(_(" {node} {bundle} {item} running...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): result = self.bundle.node.run( self.attributes['command'], may_fail=True, ) if self.attributes['expected_return_code'] is not None and \ not result.return_code == self.attributes['expected_return_code']: raise ActionFailure(_("wrong return code: {}").format(result.return_code)) if self.attributes['expected_stderr'] is not None and \ result.stderr_text != self.attributes['expected_stderr']: raise ActionFailure(_("wrong stderr")) if self.attributes['expected_stdout'] is not None and \ result.stdout_text != self.attributes['expected_stdout']: raise ActionFailure(_("wrong stdout")) return result
def run(self): if self.attributes['data_stdin'] is not None: data_stdin = self.attributes['data_stdin'] # Allow users to use either a string/unicode object or raw # bytes -- or Faults. if isinstance(data_stdin, Fault): data_stdin = data_stdin.value if type(data_stdin) is not bytes: data_stdin = data_stdin.encode('UTF-8') else: data_stdin = None with io.job(_("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): result = self.bundle.node.run( self.attributes['command'], data_stdin=data_stdin, may_fail=True, ) if self.attributes['expected_return_code'] is not None and \ not result.return_code == self.attributes['expected_return_code']: raise ActionFailure(_("wrong return code: {}").format(result.return_code)) if self.attributes['expected_stderr'] is not None and \ result.stderr_text != self.attributes['expected_stderr']: raise ActionFailure(_("wrong stderr")) if self.attributes['expected_stdout'] is not None and \ result.stdout_text != self.attributes['expected_stdout']: raise ActionFailure(_("wrong stdout")) return result
def apply( self, autoskip_selector="", autoonly_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): self.node.repo.hooks.item_apply_start( self.node.repo, self.node, self, ) status_code = None status_before = None status_after = None start_time = datetime.now() if not self.covered_by_autoonly_selector(autoonly_selector): io.debug( _("autoonly does not match {item} on {node}").format( item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_CMDLINE if self.covered_by_autoskip_selector(autoskip_selector): io.debug( _("autoskip matches {item} on {node}").format( item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_CMDLINE if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_SOFTLOCK for item in self._precedes_items: if item._triggers_preceding_items(interactive=interactive): io.debug( _("preceding item {item} on {node} has been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) self.has_been_triggered = True break else: io.debug( _("preceding item {item} on {node} has NOT been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) if self.triggered and not self.has_been_triggered and status_code is None: io.debug( _("skipping {item} on {node} because it wasn't triggered"). format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_NO_TRIGGER if status_code is None and self.cached_unless_result and status_code is None: io.debug( _("'unless' for {item} on {node} succeeded, not fixing"). format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_UNLESS if self._faults_missing_for_attributes and status_code is None: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _("skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)").format( attrs=", ".join( sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE if status_code is None: try: status_before = self.cached_status except FaultUnavailable: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _("skipping {item} on {node} because it is missing Faults " "(most of the time this means you're missing " "a required key in your .secrets.cfg)").format( item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE else: if status_before.correct: status_code = self.STATUS_OK if status_code is None: if not interactive: with io.job( _("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): self.fix(status_before) else: if status_before.must_be_created: question_text = _("Doesn't exist. Will be created.") elif status_before.must_be_deleted: question_text = _("Found on node. Will be removed.") else: question_text = self.ask( status_before.display_cdict, status_before.display_sdict, status_before.display_keys_to_fix, ) if self.comment: question_text += format_comment(self.comment) question = wrap_question( self.id, question_text, _("Fix {}?").format(bold(self.id)), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ) answer = io.ask( question, interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), ) if answer: with io.job( _("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): self.fix(status_before) else: status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_INTERACTIVE if status_code is None: status_after = self.get_status(cached=False) status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED if status_code == self.STATUS_OK: details = None elif status_code == self.STATUS_SKIPPED: details = skip_reason elif status_before.must_be_created: details = True elif status_before.must_be_deleted: details = False elif status_code == self.STATUS_FAILED: details = status_after.display_keys_to_fix else: details = status_before.display_keys_to_fix self.node.repo.hooks.item_apply_end( self.node.repo, self.node, self, duration=datetime.now() - start_time, status_code=status_code, status_before=status_before, status_after=status_after, ) return (status_code, details)
def apply( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): self.node.repo.hooks.item_apply_start( self.node.repo, self.node, self, ) status_code = None status_before = None status_after = None start_time = datetime.now() if self.covered_by_autoskip_selector(autoskip_selector): io.debug(_( "autoskip matches {item} on {node}" ).format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_CMDLINE if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_SOFTLOCK for item in self._precedes_items: if item._triggers_preceding_items(interactive=interactive): io.debug(_( "preceding item {item} on {node} has been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) self.has_been_triggered = True break else: io.debug(_( "preceding item {item} on {node} has NOT been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) if self.triggered and not self.has_been_triggered and status_code is None: io.debug(_( "skipping {item} on {node} because it wasn't triggered" ).format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_NO_TRIGGER if status_code is None and self.cached_unless_result and status_code is None: io.debug(_( "'unless' for {item} on {node} succeeded, not fixing" ).format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_UNLESS if self._faults_missing_for_attributes and status_code is None: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug(_( "skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)" ).format( attrs=", ".join(sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE if status_code is None: try: status_before = self.cached_status except FaultUnavailable: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug(_( "skipping {item} on {node} because it is missing Faults " "(most of the time this means you're missing " "a required key in your .secrets.cfg)" ).format( item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_FAULT_UNAVAILABLE else: if status_before.correct: status_code = self.STATUS_OK if status_code is None: if not interactive: with io.job(_("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): self.fix(status_before) else: if status_before.must_be_created: question_text = _("Doesn't exist. Will be created.") elif status_before.must_be_deleted: question_text = _("Found on node. Will be removed.") else: question_text = self.ask( status_before.display_cdict, status_before.display_sdict, status_before.display_keys_to_fix, ) if self.comment: question_text += format_comment(self.comment) question = wrap_question( self.id, question_text, _("Fix {}?").format(bold(self.id)), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ) answer = io.ask( question, interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), ) if answer: with io.job(_("{node} {bundle} {item}").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): self.fix(status_before) else: status_code = self.STATUS_SKIPPED skip_reason = self.SKIP_REASON_INTERACTIVE if status_code is None: status_after = self.get_status(cached=False) status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED if status_code == self.STATUS_OK: details = None elif status_code == self.STATUS_SKIPPED: details = skip_reason elif status_before.must_be_created: details = True elif status_before.must_be_deleted: details = False elif status_code == self.STATUS_FAILED: details = status_after.display_keys_to_fix else: details = status_before.display_keys_to_fix self.node.repo.hooks.item_apply_end( self.node.repo, self.node, self, duration=datetime.now() - start_time, status_code=status_code, status_before=status_before, status_after=status_after, ) return (status_code, details)
def _fetch_secret(site, secret_id): try: return cache[site][secret_id] except KeyError: pass session = sessions.setdefault(getpid(), Session()) try: full_url = "{}/api/secrets/{}/".format( config.get(site, "url"), secret_id, ) if site not in cached_credentials: if ('password' not in config[site] or not config[site]['password'] and 'pass_command' in config[site]): try: password = check_output( config[site]['pass_command'], shell=True).decode('UTF-8').splitlines()[0].strip() except (FileNotFoundError, CalledProcessError, IndexError) as e: # To avoid trying to get the password over and over. cached_credentials[site] = None raise FaultUnavailable from e else: cached_credentials[site] = ( config.get(site, 'username'), password, ) else: cached_credentials[site] = ( config.get(site, "username"), config.get(site, "password"), ) except (NoSectionError, NoOptionError): raise FaultUnavailable( "Tried to get TeamVault secret with ID '{secret_id}' " "from site '{site}', but credentials missing in {path}".format( path=CONFIG_PATH, secret_id=secret_id, site=site, ), ) if cached_credentials[site] is None: raise FaultUnavailable( "Getting credentials for {site} failed in earlier try".format( site=site, ), ) try: with io.job( _("{tv} fetching {secret}").format(tv=bold("TeamVault"), secret=secret_id)): response = session.get(full_url, auth=cached_credentials[site]) except RequestException as e: raise FaultUnavailable( "Exception while getting secret {secret} from TeamVault: {exc}". format( secret=secret_id, exc=repr(e), )) if response.status_code != 200: raise FaultUnavailable("TeamVault returned {status} for {url}".format( status=response.status_code, url=full_url, )) secret = response.json() try: response = session.get(secret['current_revision'] + "data", auth=cached_credentials[site]) except RequestException as e: raise FaultUnavailable( "Exception while getting secret {secret} from TeamVault: {exc}". format( secret=secret_id, exc=repr(e), )) if response.status_code != 200: raise FaultUnavailable("TeamVault returned {status} for {url}".format( status=response.status_code, url=secret['current_revision'] + "data", )) secret['data'] = response.json() cache.setdefault(site, {})[secret_id] = secret return secret
def _get_result( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): if self.covered_by_autoskip_selector(autoskip_selector): io.debug( _("autoskip matches {item} on {node}").format( item=self.id, node=self.node.name)) return (self.STATUS_SKIPPED, [_("cmdline")]) if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): return (self.STATUS_SKIPPED, [_("soft locked")]) if interactive is False and self.attributes['interactive'] is True: return (self.STATUS_SKIPPED, [_("interactive only")]) if self.triggered and not self.has_been_triggered: io.debug( _("skipping {} because it wasn't triggered").format(self.id)) return (self.STATUS_SKIPPED, [_("no trigger")]) if self.unless: with io.job( _(" {node} {bundle} {item} checking 'unless' condition..." ).format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): unless_result = self.bundle.node.run( self.unless, may_fail=True, ) if unless_result.return_code == 0: io.debug( _("{node}:{bundle}:action:{name}: failed 'unless', not running" ).format( bundle=self.bundle.name, name=self.name, node=self.bundle.node.name, )) return (self.STATUS_SKIPPED, ["unless"]) question_body = "" if self.attributes['data_stdin'] is not None: question_body += "<" + _("data") + "> | " question_body += self.attributes['command'] if self.comment: question_body += format_comment(self.comment) if (interactive and self.attributes['interactive'] is not False and not io.ask( wrap_question( self.id, question_body, _("Run action {}?").format(bold(self.name), ), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ), interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), )): return (self.STATUS_SKIPPED, [_("interactive")]) try: self.run() return (self.STATUS_ACTION_SUCCEEDED, None) except ActionFailure as exc: return (self.STATUS_FAILED, [str(exc)])
def apply( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): self.node.repo.hooks.item_apply_start(self.node.repo, self.node, self) keys_to_fix = None status_code = None status_before = None status_after = None start_time = datetime.now() if self.covered_by_autoskip_selector(autoskip_selector): io.debug(_("autoskip matches {item} on {node}").format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED keys_to_fix = [_("cmdline")] if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): status_code = self.STATUS_SKIPPED keys_to_fix = [_("soft locked")] if self.triggered and not self.has_been_triggered and status_code is None: io.debug( _("skipping {item} on {node} because it wasn't triggered").format(item=self.id, node=self.node.name) ) status_code = self.STATUS_SKIPPED keys_to_fix = [_("not triggered")] if status_code is None and self.cached_unless_result and status_code is None: io.debug(_("'unless' for {item} on {node} succeeded, not fixing").format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED keys_to_fix = ["unless"] if self._faults_missing_for_attributes and status_code is None: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _( "skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)" ).format( attrs=", ".join(sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name ) ) status_code = self.STATUS_SKIPPED keys_to_fix = [_("Fault unavailable")] if status_code is None: try: status_before = self.cached_status except FaultUnavailable: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _( "skipping {item} on {node} because it is missing Faults " "(most of the time this means you're missing " "a required key in your .secrets.cfg)" ).format(item=self.id, node=self.node.name) ) status_code = self.STATUS_SKIPPED keys_to_fix = [_("Fault unavailable")] else: if status_before.correct: status_code = self.STATUS_OK if status_code is None: keys_to_fix = self.display_keys( copy(self.cached_cdict), copy(status_before.sdict), status_before.keys_to_fix[:] ) if not interactive: with io.job( _(" {node} {bundle} {item} fixing...").format( bundle=self.bundle.name, item=self.id, node=self.node.name ) ): self.fix(status_before) else: if status_before.must_be_created: question_text = _("Doesn't exist. Will be created.") elif status_before.must_be_deleted: question_text = _("Found on node. Will be removed.") else: cdict, sdict = self.display_dicts(copy(self.cached_cdict), copy(status_before.sdict), keys_to_fix) question_text = self.ask(cdict, sdict, keys_to_fix) question = wrap_question( self.id, question_text, _("Fix {}?").format(bold(self.id)), prefix="{x} {node} ".format(node=bold(self.node.name), x=blue("?")), ) answer = io.ask( question, interactive_default, epilogue="{x} {node}".format(node=bold(self.node.name), x=blue("?")) ) if answer: with io.job( _(" {node} {bundle} {item} fixing...").format( bundle=self.bundle.name, item=self.id, node=self.node.name ) ): self.fix(status_before) else: status_code = self.STATUS_SKIPPED keys_to_fix = [_("interactive")] if status_code is None: status_after = self.get_status(cached=False) status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED if status_code == self.STATUS_SKIPPED: # can't use else for this because status_before is None changes = keys_to_fix elif status_before.must_be_created: changes = True elif status_before.must_be_deleted: changes = False elif status_code == self.STATUS_FAILED: changes = self.display_keys( self.cached_cdict.copy(), status_after.sdict.copy(), status_after.keys_to_fix[:] ) else: changes = keys_to_fix self.node.repo.hooks.item_apply_end( self.node.repo, self.node, self, duration=datetime.now() - start_time, status_code=status_code, status_before=status_before, status_after=status_after, ) return (status_code, changes)
def _get_result( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): if self._faults_missing_for_attributes: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _("skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)").format( attrs=", ".join( sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name, )) return (self.STATUS_SKIPPED, self.SKIP_REASON_FAULT_UNAVAILABLE) if self.covered_by_autoskip_selector(autoskip_selector): io.debug( _("autoskip matches {item} on {node}").format( item=self.id, node=self.node.name)) return (self.STATUS_SKIPPED, self.SKIP_REASON_CMDLINE) if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): return (self.STATUS_SKIPPED, self.SKIP_REASON_SOFTLOCK) if interactive is False and self.attributes['interactive'] is True: return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE_ONLY) for item in self._precedes_items: if item._triggers_preceding_items(interactive=interactive): io.debug( _("preceding item {item} on {node} has been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) self.has_been_triggered = True break else: io.debug( _("preceding item {item} on {node} has NOT been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) if self.triggered and not self.has_been_triggered: io.debug( _("skipping {} because it wasn't triggered").format(self.id)) return (self.STATUS_SKIPPED, self.SKIP_REASON_NO_TRIGGER) if self.unless: with io.job( _("{node} {bundle} {item} checking 'unless' condition"). format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): unless_result = self.bundle.node.run( self.unless, may_fail=True, ) if unless_result.return_code == 0: io.debug( _("{node}:{bundle}:action:{name}: failed 'unless', not running" ).format( bundle=self.bundle.name, name=self.name, node=self.bundle.node.name, )) return (self.STATUS_SKIPPED, self.SKIP_REASON_UNLESS) question_body = "" if self.attributes['data_stdin'] is not None: question_body += "<" + _("data") + "> | " question_body += self.attributes['command'] if self.comment: question_body += format_comment(self.comment) if (interactive and self.attributes['interactive'] is not False and not io.ask( wrap_question( self.id, question_body, _("Run action {}?").format(bold(self.name), ), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ), interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), )): return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE) try: self.run() return (self.STATUS_ACTION_SUCCEEDED, None) except ActionFailure as exc: return (self.STATUS_FAILED, [str(exc)])
def apply( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): self.node.repo.hooks.item_apply_start( self.node.repo, self.node, self, ) keys_to_fix = None status_code = None status_before = None status_after = None start_time = datetime.now() if self.covered_by_autoskip_selector(autoskip_selector): io.debug( _("autoskip matches {item} on {node}").format( item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED keys_to_fix = [_("cmdline")] if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): status_code = self.STATUS_SKIPPED keys_to_fix = [_("soft locked")] if self.triggered and not self.has_been_triggered and status_code is None: io.debug( _("skipping {item} on {node} because it wasn't triggered"). format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED keys_to_fix = [_("not triggered")] if status_code is None and self.cached_unless_result and status_code is None: io.debug( _("'unless' for {item} on {node} succeeded, not fixing"). format(item=self.id, node=self.node.name)) status_code = self.STATUS_SKIPPED keys_to_fix = ["unless"] if self._faults_missing_for_attributes and status_code is None: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _("skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)").format( attrs=", ".join( sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED keys_to_fix = [_("Fault unavailable")] if status_code is None: try: status_before = self.cached_status except FaultUnavailable: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug( _("skipping {item} on {node} because it is missing Faults " "(most of the time this means you're missing " "a required key in your .secrets.cfg)").format( item=self.id, node=self.node.name, )) status_code = self.STATUS_SKIPPED keys_to_fix = [_("Fault unavailable")] else: if status_before.correct: status_code = self.STATUS_OK if status_code is None: keys_to_fix = self.display_keys( copy(self.cached_cdict), copy(status_before.sdict), status_before.keys_to_fix[:], ) if not interactive: with io.job( _(" {node} {bundle} {item} fixing...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): self.fix(status_before) else: if status_before.must_be_created: question_text = _("Doesn't exist. Will be created.") elif status_before.must_be_deleted: question_text = _("Found on node. Will be removed.") else: cdict, sdict = self.display_dicts( copy(self.cached_cdict), copy(status_before.sdict), keys_to_fix, ) question_text = self.ask(cdict, sdict, keys_to_fix) if self.comment: question_text += format_comment(self.comment) question = wrap_question( self.id, question_text, _("Fix {}?").format(bold(self.id)), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ) answer = io.ask( question, interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), ) if answer: with io.job( _(" {node} {bundle} {item} fixing...").format( bundle=self.bundle.name, item=self.id, node=self.node.name, )): self.fix(status_before) else: status_code = self.STATUS_SKIPPED keys_to_fix = [_("interactive")] if status_code is None: status_after = self.get_status(cached=False) status_code = self.STATUS_FIXED if status_after.correct else self.STATUS_FAILED if status_code == self.STATUS_SKIPPED: # can't use else for this because status_before is None changes = keys_to_fix elif status_before.must_be_created: changes = True elif status_before.must_be_deleted: changes = False elif status_code == self.STATUS_FAILED: changes = self.display_keys( self.cached_cdict.copy(), status_after.sdict.copy(), status_after.keys_to_fix[:], ) else: changes = keys_to_fix self.node.repo.hooks.item_apply_end( self.node.repo, self.node, self, duration=datetime.now() - start_time, status_code=status_code, status_before=status_before, status_after=status_after, ) return (status_code, changes)
def _get_result( self, autoskip_selector="", my_soft_locks=(), other_peoples_soft_locks=(), interactive=False, interactive_default=True, ): if self._faults_missing_for_attributes: if self.error_on_missing_fault: self._raise_for_faults() else: io.debug(_( "skipping {item} on {node} because it is missing faults " "for these attributes: {attrs} " "(most of the time this means you're missing " "a required key in your .secrets.cfg)" ).format( attrs=", ".join(sorted(self._faults_missing_for_attributes)), item=self.id, node=self.node.name, )) return (self.STATUS_SKIPPED, self.SKIP_REASON_FAULT_UNAVAILABLE) if self.covered_by_autoskip_selector(autoskip_selector): io.debug(_( "autoskip matches {item} on {node}" ).format(item=self.id, node=self.node.name)) return (self.STATUS_SKIPPED, self.SKIP_REASON_CMDLINE) if self._skip_with_soft_locks(my_soft_locks, other_peoples_soft_locks): return (self.STATUS_SKIPPED, self.SKIP_REASON_SOFTLOCK) if interactive is False and self.attributes['interactive'] is True: return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE_ONLY) for item in self._precedes_items: if item._triggers_preceding_items(interactive=interactive): io.debug(_( "preceding item {item} on {node} has been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) self.has_been_triggered = True break else: io.debug(_( "preceding item {item} on {node} has NOT been triggered by {other_item}" ).format(item=self.id, node=self.node.name, other_item=item.id)) if self.triggered and not self.has_been_triggered: io.debug(_("skipping {} because it wasn't triggered").format(self.id)) return (self.STATUS_SKIPPED, self.SKIP_REASON_NO_TRIGGER) if self.unless: with io.job(_("{node} {bundle} {item} checking 'unless' condition").format( bundle=bold(self.bundle.name), item=self.id, node=bold(self.node.name), )): unless_result = self.bundle.node.run( self.unless, may_fail=True, ) if unless_result.return_code == 0: io.debug(_("{node}:{bundle}:action:{name}: failed 'unless', not running").format( bundle=self.bundle.name, name=self.name, node=self.bundle.node.name, )) return (self.STATUS_SKIPPED, self.SKIP_REASON_UNLESS) question_body = "" if self.attributes['data_stdin'] is not None: question_body += "<" + _("data") + "> | " question_body += self.attributes['command'] if self.comment: question_body += format_comment(self.comment) if ( interactive and self.attributes['interactive'] is not False and not io.ask( wrap_question( self.id, question_body, _("Run action {}?").format( bold(self.name), ), prefix="{x} {node} ".format( node=bold(self.node.name), x=blue("?"), ), ), interactive_default, epilogue="{x} {node}".format( node=bold(self.node.name), x=blue("?"), ), ) ): return (self.STATUS_SKIPPED, self.SKIP_REASON_INTERACTIVE) try: self.run() return (self.STATUS_ACTION_SUCCEEDED, None) except ActionFailure as exc: return (self.STATUS_FAILED, [str(exc)])