Example #1
0
    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)
Example #2
0
 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()
Example #3
0
 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()
Example #4
0
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
Example #5
0
 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)
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
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
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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
Example #13
0
    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)
Example #14
0
    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)
Example #15
0
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
Example #16
0
    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)])
Example #17
0
    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)
Example #18
0
    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)])
Example #19
0
    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)
Example #20
0
    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)])