Exemplo n.º 1
0
class InfoUrlHandler(ShowSomeUrlHandler):
    """Tells you what the bot's information url is."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('info url', takes_args=False),
            trigger.Trigger('information url', takes_args=False),
        ],
    }

    def _get_what_host_port_url(self):
        try:
            status_port = self.config.status.port
        except AttributeError:
            status_port = None
        try:
            ssl_on = bool(self.config.status.ssl)
        except AttributeError:
            ssl_on = False
        return munch.Munch({
            'name': 'information',
            'enabled': bool(status_port),
            'port': status_port,
            'hostname': self.bot.hostname,
            'ssl_on': ssl_on,
        })
Exemplo n.º 2
0
class Handler(handler.TriggeredHandler):
    """Shows the version of the bot that is running."""
    what = 'padre'
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('version', takes_args=False),
        ],
    }

    def _run(self, **kwargs):
        me = pkg_resources.get_distribution(self.what)
        replier = self.message.reply_text
        if not me:
            replier("I am not really sure what version I am.",
                    threaded=True,
                    prefixed=False)
        else:
            replier("I am %s version `%s`." % (self.what, me.version),
                    threaded=True,
                    prefixed=False)
Exemplo n.º 3
0
class AraUrlHandler(ShowSomeUrlHandler):
    """Tells you what the bot's ara url is."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('ara url', takes_args=False),
        ],
    }

    def _get_what_host_port_url(self):
        try:
            ara_enabled = self.config.ara_enabled
        except AttributeError:
            ara_enabled = False
        ara_hostname = self.config.get("ara_hostname")
        if not ara_hostname:
            ara_hostname = self.bot.hostname
        return munch.Munch({
            'name': 'ara',
            'enabled': ara_enabled,
            'port': self.config.get("ara_port"),
            'hostname': ara_hostname,
        })
Exemplo n.º 4
0
class Handler(handler.TriggeredHandler):
    """Welcomes you to the future!"""

    hellos = [
        'Hallo',
        'Bonjour',
        'Guten tag',
        u'Shalóm',
        'Konnichiwa',
        u'Namastē',
        'Hola',
        u'Nǐ hǎo',
    ]
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('hello', takes_args=False),
            trigger.Trigger('hi', takes_args=False),
            trigger.Trigger('howdy', takes_args=False),
        ],
    }

    def _run(self):
        hi_there = random.choice(self.hellos)
        replier = self.message.reply_text
        replier(hi_there, threaded=True, prefixed=False)
Exemplo n.º 5
0
class Handler(handler.TriggeredHandler):
    """Shows you what this bots current config is."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('show config', takes_args=False),
            trigger.Trigger('config show', takes_args=False),
        ],
    }

    def _run(self, **kwargs):
        tmp_config = copy.deepcopy(self.bot.config)
        tmp_config = munch.unmunchify(tmp_config)
        tmp_config = utils.mask_dict_password(tmp_config)
        tmp_config = utils.prettify_yaml(tmp_config,
                                         explicit_end=False,
                                         explicit_start=False)
        lines = []
        lines.append("I am running with configuration:")
        lines.extend([
            "```",
            tmp_config,
            "```",
        ])
        replier = self.message.reply_text
        replier("\n".join(lines), threaded=True, prefixed=True)
Exemplo n.º 6
0
class ShowHandler(handler.TriggeredHandler):
    """Shows the internal time schedule (for periodic jobs)."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('periodics show', takes_args=False),
        ],
    }

    def _run(self):
        sched_state, jobs = peu.format_scheduler(self.bot.scheduler)
        text = "Scheduler is in `%s` state" % sched_state
        if jobs:
            text += " with the following jobs:"
            self.message.reply_attachments(
                text=text, attachments=[peu.format_job(job) for job in jobs],
                log=LOG, link_names=True, as_user=True,
                thread_ts=self.message.body.ts,
                channel=self.message.body.channel)
        else:
            text += "."
            self.message.reply_text(text, threaded=True, prefixed=False)
Exemplo n.º 7
0
class Handler(handler.TriggeredHandler):
    """Show's how many seconds the bot has been alive for."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('uptime', takes_args=False),
        ],
    }

    def _run(self, **kwargs):
        started_at = self.bot.started_at
        replier = self.message.reply_text
        if started_at is None:
            replier(
                "I am not alive, how are you sending this?",
                threaded=True, prefixed=False)
        else:
            now = self.date_wrangler.get_now()
            diff = now - started_at
            replier(
                "I have been alive"
                " for %s." % utils.format_seconds(diff.total_seconds()),
                threaded=True, prefixed=False)
Exemplo n.º 8
0
class Handler(handler.TriggeredHandler):
    """Finds a hostnames ip address."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('dns lookup', takes_args=True),
        ],
        'args': {
            'order': ['hostname'],
            'help': {
                'hostname': 'hostname to lookup',
            },
            'schema':
            Schema({
                Required("hostname"): All(su.string_types(), Length(min=1)),
            }),
        },
    }

    def _run(self, hostname):
        replier = functools.partial(self.message.reply_text,
                                    threaded=True,
                                    prefixed=False)
        hostname_ip = socket.gethostbyname(hostname)
        replier("The ip address for `%s` is `%s`" % (hostname, hostname_ip))
Exemplo n.º 9
0
class Handler(handler.TriggeredHandler):
    """Helps you (the user) with some useful buzzwords."""

    buzz_url = "http://www.buzzwordipsum.com/buzzwords/"
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('help me', takes_args=False),
        ],
    }

    def _run(self):
        bullshit_response_from_buzzwordipsum = requests.get(self.buzz_url,
                                                            params={
                                                                "format":
                                                                "text",
                                                                "paragraphs":
                                                                "1",
                                                                "type":
                                                                "sentences"
                                                            })
        bullshit_response_from_buzzwordipsum.raise_for_status()
        replier = self.message.reply_text
        replier(bullshit_response_from_buzzwordipsum.text.strip("\n"),
                threaded=True,
                prefixed=False)
Exemplo n.º 10
0
class SpinHandler(handler.TriggeredHandler):
    """Handler that never stops (for testing)."""

    update_frequency = 5.0
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('spin', takes_args=False),
            trigger.Trigger('spinner', takes_args=False),
        ],
    }

    def _run(self):
        replier = self.message.reply_text
        m = self.message.make_manual_progress_bar()
        with timeutils.StopWatch() as w:
            replier("Spinner initiated.", threaded=True, prefixed=False)
            while not self.dead.is_set():
                self.dead.wait(self.update_frequency)
                if self.dead.is_set():
                    break
                else:
                    m.update("%0.2f seconds" % w.elapsed())
            replier("Spinner stopped.", threaded=True, prefixed=False)
Exemplo n.º 11
0
class ClearHandler(handler.TriggeredHandler):
    """Remove all aliases (for the calling user)."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('alias clear', takes_args=False),
        ],
    }

    def _run(self):
        from_who = self.message.body.user_id
        if not from_who:
            return
        from_who = "user:%s" % from_who
        with self.bot.locks.brain:
            try:
                user_info = self.bot.brain[from_who]
            except KeyError:
                user_info = {}
            if 'aliases' in user_info:
                num_aliases = len(user_info['aliases'])
                user_info['aliases'] = {}
                self.bot.brain[from_who] = user_info
                self.bot.brain.sync()
            else:
                num_aliases = 0
        replier = self.message.reply_text
        replier("Removed %s aliases." % num_aliases,
                threaded=True,
                prefixed=False)
Exemplo n.º 12
0
class Handler(handler.TriggeredHandler):
    """Hands out random jokes."""

    joke_url = 'http://icanhazdadjoke.com'

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('tell me a joke', takes_args=False),
        ],
    }

    def _run(self):
        resp = requests.get(self.joke_url, headers=OUT_HEADERS)
        resp.raise_for_status()
        joke = resp.json().get("joke")
        if not joke:
            joke_text = "No joke found when calling `%s`." % self.joke_url
        else:
            joke_text = joke
        replier = self.message.reply_text
        replier(joke_text, threaded=True, prefixed=False)
Exemplo n.º 13
0
class Handler(handler.TriggeredHandler):
    """Causes the bot to restart itself."""

    ack_prefix = 'Restart acknowledged.'
    ack_messages = [
        "Be back in a bit!",
    ]
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('restart', takes_args=False),
        ],
        'authorizer':
        auth.user_in_ldap_groups('admins_cloud'),
    }

    def _run(self, **kwargs):
        ack_msg = self.ack_prefix
        ack_msg += " "
        ack_msg += random.choice(self.ack_messages)
        replier = self.message.reply_text
        replier(ack_msg, threaded=True, prefixed=False)
        if not self.bot.dead.is_set():
            self.bot.dead.set(self.bot.dead.RESTART)
Exemplo n.º 14
0
class Handler(handler.TriggeredHandler):
    """Causes the bot to turn itself off/shutdown."""

    ack_prefix = 'Shutdown acknowledged.'
    ack_messages = [
        "Goodbye!",
        "I am out of here.",
        "I am so out of here.",
        "Live long and prosper.",
        "Peace out y'all!",
    ]
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('shutdown', takes_args=False),
        ],
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
    }

    def _run(self, **kwargs):
        ack_msg = self.ack_prefix
        ack_msg += " "
        ack_msg += random.choice(self.ack_messages)
        replier = self.message.reply_text
        replier(ack_msg, threaded=True, prefixed=False)
        if not self.bot.dead.is_set():
            self.bot.dead.set()
Exemplo n.º 15
0
class Handler(handler.TriggeredHandler):
    """Shows what this bot is."""

    what = 'padre'
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('what are you', takes_args=False),
            trigger.Trigger('what are you?', takes_args=False),
        ],
    }
    pkg_info_attrs = tuple([
        'author_email',
        'author',
        'classifiers',
        'description',
        'home_page',
        'license',
        'name',
        'platform',
        'summary',
        'version',
    ])

    def _run(self, **kwargs):
        replier = self.message.reply_text
        me = pkginfo.get_metadata(self.what)
        if not me:
            replier("I am not really sure what I am.",
                    threaded=True,
                    prefixed=False)
        else:
            lines = _format_pkg(me, self.pkg_info_attrs)
            if lines:
                replier = self.message.reply_attachments
                attachment = {
                    'pretext': "I am the following:",
                    'text': "\n".join(lines),
                    'mrkdwn_in': ['text'],
                }
                replier(text=' ',
                        log=LOG,
                        attachments=[attachment],
                        link_names=True,
                        as_user=True,
                        channel=self.message.body.channel,
                        thread_ts=self.message.body.ts)
            else:
                replier("I am not really sure what I am.",
                        threaded=True,
                        prefixed=False)
Exemplo n.º 16
0
class RemoveHandler(handler.TriggeredHandler):
    """Remove a alias to a long command (for the calling user)."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('alias remove', True),
        ],
        'schema':
        Schema({
            Required("short"): All(su.string_types(), Length(min=1)),
        }),
        'args': {
            'order': [
                'short',
            ],
            'help': {
                'short': 'alias of full command to remove',
            },
        },
    }

    def _run(self, short):
        from_who = self.message.body.user_id
        if not from_who:
            return
        from_who = "user:%s" % from_who
        lines = []
        with self.bot.locks.brain:
            try:
                user_info = self.bot.brain[from_who]
            except KeyError:
                user_info = {}
            user_aliases = user_info.get('aliases', {})
            try:
                long = user_aliases.pop(short)
                self.bot.brain[from_who] = user_info
                self.bot.brain.sync()
                lines = [
                    ("Alias of `%s` to `%s` has"
                     " been removed.") % (short, long),
                ]
            except KeyError:
                lines = [
                    "No alias found for `%s`" % short,
                ]
        if lines:
            replier = self.message.reply_text
            replier("\n".join(lines), threaded=True, prefixed=False)
Exemplo n.º 17
0
class DescribeUserHandler(handler.TriggeredHandler):
    """Lists the details of some ldap user."""

    required_clients = ("ldap", )
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('ldap describe user', takes_args=True),
        ],
        'args': {
            'order': [
                'user',
            ],
            'help': {
                'user': '******',
            },
            'schema':
            Schema({
                Required("user"): All(scu.string_types(), Length(min=1)),
            }),
        },
    }

    def _run(self, user):
        ldap_client = self.bot.clients.ldap_client
        tmp_user = ldap_client.describe_user(user)
        replier = self.message.reply_text
        if not tmp_user:
            replier("No user with name `%s` found." % (user),
                    threaded=True,
                    prefixed=False)
        else:
            tbl_headers = []
            row = []
            for k in sorted(tmp_user.keys()):
                v = tmp_user.get(k)
                if v is not None:
                    h_k = k.replace("_", ' ')
                    h_k = h_k[0].upper() + h_k[1:]
                    tbl_headers.append(h_k)
                    row.append(v)
            rows = [row]
            lines = [
                "```",
                tabulate.tabulate(rows, headers=tbl_headers),
                "```",
            ]
            replier("\n".join(lines), threaded=True, prefixed=False)
Exemplo n.º 18
0
class RunAllHandler(handler.TriggeredHandler):
    """Explicitly runs all periodic jobs."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('periodics run all', takes_args=True),
        ],
        'args': {
            'order': [
                'skip_paused',
            ],
            'help': {
                'skip_paused': ('skip over paused jobs (ie do not'
                                ' unpause them)'),
            },
            'defaults': {
                'skip_paused': True,
            },
            'converters': {
                'skip_paused': hu.strict_bool_from_string,
            },
            'schema': Schema({
                Required("skip_paused"): bool,
            }),
        },
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
    }

    def _run(self, skip_paused):
        kicked = 0
        seen_jobs = set()
        skipped = 0
        for job in self.bot.scheduler.get_jobs():
            if job.id in seen_jobs:
                continue
            seen_jobs.add(job.id)
            if skip_paused and job.next_run_time is None:
                skipped += 1
                continue
            job.modify(next_run_time=self.date_wrangler.get_now())
            kicked += 1
        if kicked:
            self.bot.scheduler.wakeup()
        text = ("Kicked %s jobs"
                " and skipped %s jobs.") % (kicked, skipped)
        self.message.reply_text(text, threaded=True, prefixed=False)
Exemplo n.º 19
0
class AddHandler(handler.TriggeredHandler):
    """Alias a long command to a short(er) one (for the calling user)."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('alias add', True),
        ],
        'schema':
        Schema({
            Required("long"): All(su.string_types(), Length(min=1)),
            Required("short"): All(su.string_types(), Length(min=1)),
        }),
        'args': {
            'order': [
                'long',
                'short',
            ],
            'help': {
                'long': "full command",
                'short': 'shorter alias of full command',
            },
        },
    }

    def _run(self, long, short):
        from_who = self.message.body.user_id
        if not from_who:
            return
        from_who = "user:%s" % from_who
        with self.bot.locks.brain:
            try:
                user_info = self.bot.brain[from_who]
            except KeyError:
                user_info = {}
            user_aliases = user_info.setdefault('aliases', {})
            user_aliases[short] = long
            self.bot.brain[from_who] = user_info
            self.bot.brain.sync()
            lines = [
                "Alias of `%s` to `%s` has been recorded." % (short, long),
            ]
        replier = self.message.reply_text
        replier("\n".join(lines), threaded=True, prefixed=False)
Exemplo n.º 20
0
class JenkinsRestartHandler(handler.TriggeredHandler):
    """Triggers the jenkins the bot is connected to, to restart."""

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('jenkins restart', takes_args=True),
        ],
        'args': {
            'order': [
                'safe',
            ],
            'schema': Schema({
                Required("safe"): bool,
            }),
            'converters': {
                'safe': hu.strict_bool_from_string,
            },
            'help': {
                'safe': "perform a safe restart (letting active jobs finish)",
            },
            'defaults': {
                'safe': True,
            },
        },
        'authorizer':
        auth.user_in_ldap_groups('admins_cloud'),
    }
    required_clients = ('jenkins', )

    def _run(self, safe):
        jenkins_client = self.bot.clients.jenkins_client
        replier = self.message.reply_text
        replier = functools.partial(replier, threaded=True, prefixed=False)
        if safe:
            replier("Engaging *safe* jenkins restart, please wait...")
        else:
            replier("Engaging *unsafe* (ie forceful)"
                    " jenkins restart, please wait...")
        if jenkins_client.perform_restart(safe=safe):
            replier("Restart acknowledged.")
        else:
            replier("Restart failed.")
Exemplo n.º 21
0
class Handler(handler.TriggeredHandler):
    """Emit some message to some set of slack channels."""

    required_clients = ('slack',)
    requires_slack_sender = True
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('emit message', takes_args=True),
        ],
        'args': {
            'order': ['channels', 'message'],
            'schema': Schema({
                Required("channels"): All(scu.string_types(), Length(min=1)),
                Required("message"): All(scu.string_types(), Length(min=1)),
            }),
            'help': {
                'channels': ('comma separated list of channels'
                             ' to broadcast to'),
                'message': 'what to broadcast',
            },
        },
    }

    def _run(self, channels, message):
        slack_sender = self.bot.slack_sender
        slack_server = self.bot.clients.slack_client.server
        ok_channels = []
        seen = set()
        for maybe_c in channels.split(","):
            maybe_c = maybe_c.strip()
            if maybe_c and maybe_c not in seen:
                tmp_c = slack_server.channels.find(maybe_c)
                if tmp_c is None:
                    raise RuntimeError("Could not find channel '%s'" % maybe_c)
                else:
                    if tmp_c.id not in seen:
                        seen.add(maybe_c)
                        seen.add(tmp_c.id)
                        ok_channels.append(tmp_c)
        for ch in ok_channels:
            slack_sender.rtm_send(message, channel=ch.id)
Exemplo n.º 22
0
class WatchHandler(JobWatcher):
    """Watches a jenkins jobs build."""

    build_info_delay = 10

    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'followers': [
            ConsoleFollower,
            AbortFollower,
        ],
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('jenkins watch', takes_args=True),
        ],
        'args': {
            'order': [
                'job_name',
                'build',
            ],
            'schema':
            Schema({
                Required("job_name"): All(scu.string_types(), Length(min=1)),
                Required("build"): int,
            }),
            'converters': {
                'build': int,
            },
            'help': {
                'job_name': "job name to watch",
                "build": "build number to watch",
            },
        },
        'authorizer':
        auth.user_in_ldap_groups('admins_cloud'),
    }
    required_clients = ('jenkins', )

    def _run(self, job_name, build):
        clients = self.bot.clients
        return self._watch(job_name, build, clients.jenkins_client)
Exemplo n.º 23
0
class RunOneHandler(handler.TriggeredHandler):
    """Explicitly runs one periodic jobs."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('periodics run one', takes_args=True),
        ],
        'args': {
            'order': [
                'job_id',
            ],
            'help': {
                'job_id': 'job id to run',
            },
            'schema': Schema({
                Required("job_id"): All(scu.string_types(), Length(min=1)),
            }),
        },
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
    }

    def _run(self, job_id):
        job = self.bot.scheduler.get_job(job_id)
        if job is None:
            raise excp.NotFound("Could not find job id '%s'" % job_id)
        elif job.next_run_time is None:
            raise RuntimeError("Paused job '%s' can not be explicitly"
                               " ran (please resume it first)" % job_id)
        else:
            job.modify(next_run_time=self.date_wrangler.get_now())
            self.bot.scheduler.wakeup()
            self.message.reply_text("Job `%s` has had"
                                    " its next run time"
                                    " updated to be now (hopefully it"
                                    " runs soon)." % job_id,
                                    threaded=True, prefixed=False)
Exemplo n.º 24
0
class ResumeHandler(handler.TriggeredHandler):
    """Resumes a previously paused periodic job."""

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('periodics resume', takes_args=True),
        ],
        'args': {
            'order': [
                'job_id',
            ],
            'help': {
                'job_id': 'job id to resume',
            },
            'schema': Schema({
                Required("job_id"): All(scu.string_types(), Length(min=1)),
            }),
        },
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
    }

    def _run(self, job_id):
        job = self.bot.scheduler.get_job(job_id)
        if job is None:
            raise excp.NotFound("Could not find job id '%s'" % job_id)
        if job.next_run_time is None:
            job.resume()
            self.bot.scheduler.wakeup()
            self.message.reply_text("Job `%s` has"
                                    " been resumed." % job_id,
                                    threaded=True, prefixed=False)
        else:
            self.message.reply_text("Job `%s` is not paused (so it can"
                                    " not be resumed)." % job_id,
                                    threaded=True, prefixed=False)
Exemplo n.º 25
0
class Handler(handler.TriggeredHandler):
    """Say various trump phrases."""

    trump_url = 'https://api.whatdoestrumpthink.com/api/v1/quotes'
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('what would trump say', takes_args=False),
            trigger.Trigger('what would trump say?', takes_args=False),
            trigger.Trigger('trump say something', takes_args=False),
        ],
    }

    def _run(self):
        trump_messages = requests.get(self.trump_url)
        trump_messages.raise_for_status()
        message = random.choice(
            trump_messages.json()["messages"]["non_personalized"])
        replier = self.message.reply_text
        replier(message, threaded=True, prefixed=False)
Exemplo n.º 26
0
class PruneHandler(handler.TriggeredHandler):
    """Prunes a docker artifactory repositories."""

    config_section = 'artifactory'
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('artifactory prune', takes_args=True),
        ],
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
        'args': {
            'order': ['project', 'target_size'],
            'help': {
                'project': 'project to scan',
                'target_size': 'target size to prune project repo to',
            },
            'schema': Schema({
                Required("project"): All(su.string_types(), Length(min=1)),
                Required("target_size"): All(int, Range(min=0)),
            }),
            'converters': {
                'target_size': functools.partial(strutils.string_to_bytes,
                                                 return_int=True,
                                                 # Because artifactory
                                                 # is using the SI
                                                 # system... arg...
                                                 unit_system='SI'),
            },
        },
    }
    required_secrets = (
        'ci.artifactory.ro_account',
        'ci.artifactory.push_account',
    )
    required_configurations = ('base_url',)

    def _do_prune(self, prune_what):
        dirs_pruned = 0
        files_pruned = 0
        was_finished = True
        pbar = self.message.make_progress_bar(
            len(prune_what), update_period=_calc_emit_every(prune_what))
        for child in pbar.wrap_iter(prune_what):
            if self.dead.is_set():
                was_finished = False
                break
            stack = collections.deque()
            stack.append((child.path, False))
            while stack:
                # NOTE: we do not check dead.is_set() here which might
                # be ok, but is done so that we don't delete a sub child
                # half-way (which if done may leave any docker images
                # half-way-working... ie missing components/layers...
                # which would be bad).
                p, p_visited = stack.pop()
                p_is_dir = p.is_dir()
                if p_is_dir and not p_visited:
                    stack.append((p, True))
                    stack.extend((c_p, False) for c_p in p.iterdir())
                elif p_is_dir and p_visited:
                    p.rmdir()
                    dirs_pruned += 1
                else:
                    p.unlink()
                    files_pruned += 1
        return (dirs_pruned, files_pruned, was_finished)

    def _do_scan(self, replier, path, target_size):
        root_child_paths = list(path.iterdir())
        all_sub_children = []
        replier("Finding all sub-children of"
                " %s top-level children." % len(root_child_paths))

        if root_child_paths:
            pbar = self.message.make_progress_bar(
                len(root_child_paths),
                update_period=_calc_emit_every(root_child_paths))
            for child_path in pbar.wrap_iter(root_child_paths):
                if self.dead.is_set():
                    raise excp.Dying
                replier("Scanning top-level"
                        " child `%s`, please wait..." % child_path.name)
                sub_child_paths = list(child_path.iterdir())
                if sub_child_paths:
                    rc_pbar = self.message.make_progress_bar(
                        len(sub_child_paths),
                        update_period=_calc_emit_every(sub_child_paths))
                    for sub_child_path in rc_pbar.wrap_iter(sub_child_paths):
                        if self.dead.is_set():
                            raise excp.Dying
                        all_sub_children.append(munch.Munch({
                            'path': sub_child_path,
                            'frozen': _is_frozen(sub_child_path),
                            'ctime': sub_child_path.stat().ctime,
                            'size': sub_child_path.stat().size,
                            'parent': child_path,
                        }))

        all_sub_children = sorted(all_sub_children, key=lambda p: p.ctime)
        num_childs_frozen = sum(int(sc.frozen) for sc in all_sub_children)
        replier("Determining total sizes"
                " of %s sub-children"
                " (%s are frozen)." % (len(all_sub_children),
                                       num_childs_frozen))
        if all_sub_children:
            pbar = self.message.make_progress_bar(
                len(all_sub_children),
                update_period=_calc_emit_every(all_sub_children))
            for sub_child in pbar.wrap_iter(all_sub_children):
                if self.dead.is_set():
                    raise excp.Dying
                try:
                    total_size = _calc_docker_size(sub_child.path,
                                                   sub_child.size)
                except excp.NotFound:
                    total_size = 0
                    for size in _iter_sizes_deep(sub_child.path):
                        if self.dead.is_set():
                            raise excp.Dying
                        total_size += size
                sub_child.total_size = total_size

        accum_size = 0
        prune_what = []
        for sub_child in reversed(all_sub_children):
            if sub_child.frozen:
                continue
            accum_size += sub_child.total_size
            if accum_size >= target_size:
                prune_what.append(sub_child)
        prune_what.reverse()
        return prune_what

    def _format_child(self, child):
        try:
            child_pretext = "%s/%s" % (child.parent.name, child.path.name)
        except AttributeError:
            child_pretext = "%s" % child.path.name
        attachment = {
            'pretext': child_pretext,
            'mrkdwn_in': [],
            'footer': "Artifactory",
            'footer_icon': ART_ICON,
        }
        tot_size = utils.format_bytes(child.total_size)
        attachment['fields'] = [
            {
                'title': 'Size',
                'value': tot_size,
                'short': utils.is_short(tot_size),
            },
            {
                "title": "Created on",
                "value": _format_dt(child.ctime),
                "short": True,
            },
        ]
        return attachment

    def _run(self, project, target_size):
        push_account = self.bot.secrets.ci.artifactory.push_account
        path = _find_path(self.config, project, push_account)
        if not path:
            raise excp.NotFound("Could not find project '%s'" % project)
        replier = functools.partial(self.message.reply_text,
                                    threaded=True, prefixed=False)
        replier("Scanning `%s`, please wait..." % project)
        try:
            prune_what = self._do_scan(replier, path, target_size)
        except excp.Dying:
            replier("Died during scanning, please try"
                    " again next time...")
            return
        if not prune_what:
            replier("Nothing to prune found.")
            return
        self.message.reply_attachments(
            attachments=list(self._format_child(c) for c in prune_what),
            log=LOG, link_names=True, as_user=True,
            thread_ts=self.message.body.ts,
            channel=self.message.body.channel)
        replier("Please confirm the pruning of"
                " %s paths." % len(prune_what))
        f = followers.ConfirmMe(confirms_what='pruning')
        replier(f.generate_who_satisifies_message(self))
        self.wait_for_transition(wait_timeout=300, follower=f,
                                 wait_start_state='CONFIRMING')
        if self.state != 'CONFIRMED_CANCELLED':
            self.change_state("PRUNING")
            replier("Initiating prune of %s paths." % len(prune_what))
            dirs_pruned, files_pruned, done = self._do_prune(prune_what)
            replier("Pruned %s directories and"
                    " %s files." % (dirs_pruned, files_pruned))
            if not done:
                replier("This was a partial prune, since I died"
                        " during pruning, please try"
                        " again next time...")
        else:
            replier("Pruning cancelled.")
Exemplo n.º 27
0
class CalcSizeHandler(handler.TriggeredHandler):
    """Determines size of docker artifactory repositories."""

    config_section = 'artifactory'
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('artifactory calculate size', takes_args=True),
        ],
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
        'args': {
            'order': ['project'],
            'help': {
                'project': 'project to scan',
            },
            'schema': Schema({
                Required("project"): All(su.string_types(), Length(min=1)),
            }),
        },
    }
    required_secrets = (
        'ci.artifactory.ro_account',
    )
    required_configurations = ('base_url',)

    def _run(self, project):
        ro_account = self.bot.secrets.ci.artifactory.ro_account

        path = _find_path(self.config, project, ro_account)
        if not path:
            raise excp.NotFound("Could not find project '%s'" % project)

        replier = self.message.reply_text
        replier = functools.partial(replier, threaded=True, prefixed=False)
        replier("Determining current size of `%s`, please"
                " wait..." % project)

        all_sizes = [
            path.stat().size,
        ]
        child_paths = list(path.iterdir())
        child_paths = sorted(child_paths, key=lambda p: p.name)
        if child_paths:
            c_pbar = self.message.make_progress_bar(
                len(child_paths), update_period=_calc_emit_every(child_paths))
            for child_path in c_pbar.wrap_iter(child_paths):
                if self.dead.is_set():
                    break
                all_sizes.append(child_path.stat().size)
                replier("Determining total size"
                        " of top-level child `%s`, please"
                        " wait..." % child_path.name)
                sub_child_paths = list(child_path.iterdir())
                if sub_child_paths:
                    sc_pbar = self.message.make_progress_bar(
                        len(sub_child_paths),
                        update_period=_calc_emit_every(sub_child_paths))
                    for sub_child_path in sc_pbar.wrap_iter(sub_child_paths):
                        if self.dead.is_set():
                            break
                        try:
                            sub_child_size = _calc_docker_size(
                                sub_child_path, sub_child_path.stat().size)
                        except excp.NotFound:
                            sub_child_size = 0
                            for size in _iter_sizes_deep(sub_child_path):
                                if self.dead.is_set():
                                    break
                                sub_child_size += size
                        all_sizes.append(sub_child_size)

        if self.dead.is_set():
            replier("Died during scanning, please"
                    " try again next time...")
        else:
            replier(
                "Size of `%s` is %s" % (project,
                                        utils.format_bytes(
                                            sum(all_sizes), quote=True)))
Exemplo n.º 28
0
class Handler(handler.TriggeredHandler):
    """Triggers a workflow to downgrade/upgrade the version of this bot."""
    wait_jenkins_queue_item = 0.1
    config_section = 'updater'
    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('upgrade', takes_args=True),
            trigger.Trigger('update', takes_args=True),
            trigger.Trigger('upgrayedd', takes_args=True),
        ],
        'authorizer': auth.user_in_ldap_groups('admins_cloud'),
        'args': {
            'order': [
                'version',
            ],
            'help': {
                'version': ('version of padre container to deploy'
                            ' (must exist in artifactory), if'
                            ' not provided then the lastest will'
                            ' be found'),
            },
        }
    }
    required_clients = ('jenkins',)
    required_secrets = (
        'ci.artifactory.ro_account',
    )

    def _await_confirm(self, old_version, version, changelog_lines):
        def _show_others_active():
            active_handlers = len(self.bot.active_handlers)
            return ("There are %s other active"
                    # Remove one since thats the upgrade handler itself...
                    " handlers.") % (max(0, active_handlers - 1))
        pretext_lines = [
            "Newer version `%s` found!" % version,
            "I am older version `%s`." % old_version,
        ]
        text_lines = []
        if changelog_lines:
            text_lines.append("Last `%s` changes:" % len(changelog_lines))
            text_lines.extend(changelog_lines)
        attachments = [{
            'pretext': "\n".join(pretext_lines),
            'mrkdwn_in': ['pretext', 'text'],
            "text": "\n".join(text_lines),
        }]
        self.message.reply_attachments(
            text="Good %s." % self.date_wrangler.get_when(),
            attachments=attachments,
            link_names=True, as_user=True,
            channel=self.message.body.channel,
            log=LOG, thread_ts=self.message.body.get("ts"))
        replier = functools.partial(self.message.reply_text,
                                    threaded=True, prefixed=False,
                                    thread_ts=self.message.body.ts)
        f = followers.ConfirmMe(confirms_what='upgrading',
                                confirm_self_ok=True,
                                check_func=_show_others_active)
        replier(f.generate_who_satisifies_message(self))
        self.wait_for_transition(follower=f, wait_timeout=300,
                                 wait_start_state='CONFIRMING')
        if self.state == 'CONFIRMED_CANCELLED':
            raise excp.Cancelled

    def _run(self, **kwargs):
        replier = functools.partial(self.message.reply_text,
                                    threaded=True, prefixed=False)
        me = pkg_resources.get_distribution('padre')
        ro_account = self.bot.secrets.ci.artifactory.ro_account
        version = kwargs.get("version")
        version_provided = bool(version)
        project_url = self.bot.config.updater.project_url
        path = None
        if not version_provided:
            replier("Scanning artifactory, please wait...")
            newer_paths_it = uu.iter_updates(me.version,
                                             ro_account, project_url)
            newer_paths = sorted(newer_paths_it, key=lambda v: v.version)
            if newer_paths:
                path = newer_paths[-1].path
                version = str(newer_paths[-1].version)
        if not version:
            replier("No potentially upgradeable versions"
                    " found under '%s'" % project_url)
            return
        if me.version == version:
            replier("Nothing to upgrade, version desired is equivalent"
                    " to active version.")
            return
        if path is None:
            tmp_path = uu.check_fetch_version(version, ro_account, project_url)
            path = tmp_path.path
        self._await_confirm(
            me.version, version, uu.extract_changelog(path))
        self.change_state("UPGRADING")
        jenkins_job = self.config.jenkins_job
        jenkins_client = self.bot.clients.jenkins_client
        job = jenkins_client.get_job(jenkins_job)
        if job is not None:
            replier(
                "Triggering upgrade to"
                " version `%s` by kicking job `%s`." % (version,
                                                        jenkins_job))
            qi = job.invoke(build_params={
                'image_tag': version,
                'bot': self.bot.name or "",
            })
            replier("Your upgrade to `%s` job"
                    " has been queued." % version)
            build = None
            while build is None:
                if self.dead.is_set():
                    # Oh well, someone else killed us...
                    raise excp.Dying
                qi.poll()
                build = qi.get_build()
                if build is None:
                    self.dead.wait(self.wait_jenkins_queue_item)
            replier(
                "Your upgrade to `%s` job has"
                " started at %s. I am going into stealth/quiet"
                " mode until then (resurrection expected in %0.2f"
                " seconds), goodbye..." % (version, build.url,
                                           build.get_eta()))
            self.bot.quiescing = True
            self.bot.scheduler.shutdown(wait=False)
        else:
            raise excp.NotFound(
                "Jenkins upgrade job '%s' was not"
                " found" % jenkins_job)
Exemplo n.º 29
0
class ListHandler(handler.TriggeredHandler):
    """Lists the members of a ldap group."""

    required_clients = ("ldap", "github")
    max_before_gist = 100
    handles_what = {
        'message_matcher':
        matchers.match_or(matchers.match_slack("message"),
                          matchers.match_telnet("message")),
        'channel_matcher':
        matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('ldap list', takes_args=True),
        ],
        'args': {
            'order': [
                'group',
            ],
            'help': {
                'group': 'ldap group to list',
            },
            'schema':
            Schema({
                Required("group"): All(scu.string_types(), Length(min=1)),
            }),
        },
    }

    def _run(self, group):
        replier = self.message.reply_text
        ldap_client = self.bot.clients.ldap_client
        group_members = [
            ldap_utils.explode_member(member)
            for member in ldap_client.list_ldap_group(group)
        ]
        group_members = sorted(group_members, key=lambda m: m.get("CN"))
        tbl_headers = ['CN', 'DC', 'OU']
        rows = []
        for member in group_members:
            row = []
            for k in tbl_headers:
                v = member.get(k)
                if isinstance(v, list):
                    v = ", ".join(v)
                row.append(v)
            rows.append(row)
        if len(group_members) <= self.max_before_gist:
            lines = [
                "```",
                tabulate.tabulate(rows, headers=tbl_headers),
                "```",
            ]
            replier("\n".join(lines), threaded=True, prefixed=False)
        else:
            github_client = self.bot.clients.github_client
            me = github_client.get_user()
            to_send = {}
            upload_what = [
                ('listing', tabulate.tabulate(rows, headers=tbl_headers)),
            ]
            for what_name, contents in upload_what:
                # Github has upper limit on postings to 1MB
                contents = _chop(contents, units.Mi)
                contents = contents.strip()
                name = what_name + ".txt"
                to_send[name] = github.InputFileContent(contents)
            if to_send:
                try:
                    gist = me.create_gist(True, to_send)
                except Exception:
                    LOG.warning(
                        "Failed uploading gist for listing"
                        " of '%s' ldap group", group)
                else:
                    lines = [
                        "Gist url at: %s" % gist.html_url,
                    ]
                    replier("\n".join(lines), threaded=True, prefixed=False)
Exemplo n.º 30
0
class Handler(handler.TriggeredHandler):
    """Get stock information."""

    stock_url = 'https://www.alphavantage.co/query'

    # NOTE: If more than 100 symbols are included, the API will
    # return quotes for the first 100 symbols.
    #
    # In order to fix that just split into 100 size chunks...
    max_per_call = 100

    handles_what = {
        'message_matcher': matchers.match_or(
            matchers.match_slack("message"),
            matchers.match_telnet("message")
        ),
        'channel_matcher': matchers.match_channel(c.TARGETED),
        'triggers': [
            trigger.Trigger('stock', takes_args=True),
        ],
        'args': {
            'order': ['symbols'],
            'converters': {},
            'schema': Schema({
                Required("symbols"): All(scu.string_types(), Length(min=1)),
            }),
            'help': {
                'symbols': 'symbol(s) to lookup (comma separated)',
            },
            'defaults': {
                'symbols': 'gddy',
            },
        },
    }

    def _run(self, **kwargs):
        symbols = kwargs.get('symbols', "")
        symbols = symbols.split(",")
        symbols = [s.strip() for s in symbols if s.strip()]
        seen_symbols = set()
        headers = ["Symbol", "Price", "Volume"]
        rows = []
        uniq_symbols = []
        for s in symbols:
            tmp_s = s.upper()
            if tmp_s in seen_symbols:
                continue
            else:
                uniq_symbols.append(tmp_s)
                seen_symbols.add(tmp_s)
        for batch in utils.iter_chunks(uniq_symbols, self.max_per_call):
            url = self.stock_url + "?"
            url += urllib.urlencode({
                'function': 'BATCH_STOCK_QUOTES',
                'symbols': ",".join(batch),
                'datatype': 'csv',
                'apikey': self.config.stock.apikey,
            })
            resp = requests.get(url)
            resp.raise_for_status()
            for row in csv.DictReader(
                    six.StringIO(resp.content.decode('utf-8'))):
                rows.append([
                    row['symbol'],
                    row['price'],
                    row['volume'],
                ])
        lines = [
            "```",
            tabulate.tabulate(rows, headers=headers),
            "```",
        ]
        replier = self.message.reply_text
        replier("\n".join(lines), threaded=True, prefixed=False)