Ejemplo n.º 1
0
def create_task_reminders(db_session=None):
    """Creates multiple task reminders."""
    tasks = task_service.get_overdue_tasks(db_session=db_session)
    log.debug(f"New tasks that need reminders. NumTasks: {len(tasks)}")
    # lets only remind for active incidents for now
    tasks = [t for t in tasks if t.incident.status == IncidentStatus.active]
    if tasks:
        contact_fullname = contact_weblink = DISPATCH_HELP_EMAIL
        # NOTE INCIDENT_ONCALL_SERVICE_ID is optional
        if INCIDENT_ONCALL_SERVICE_ID:
            oncall_service = service_service.get_by_external_id(
                db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID)
            oncall_plugin = plugin_service.get_by_slug(
                db_session=db_session, slug=oncall_service.type)
            if oncall_plugin.enabled:
                log.warning(
                    f"Unable to resolve oncall, INCIDENT_ONCALL_SERVICE_ID configured but associated plugin ({oncall_plugin.name}) is not enabled."
                )
                oncall_email = oncall_plugin.instance.get(
                    service_id=INCIDENT_ONCALL_SERVICE_ID)
                oncall_individual = individual_service.resolve_user_by_email(
                    oncall_email, db_session)
                contact_fullname = oncall_individual["fullname"]
                contact_weblink = oncall_individual["weblink"]

        grouped_tasks = group_tasks_by_assignee(tasks)
        for assignee, tasks in grouped_tasks.items():
            create_reminder(db_session, assignee, tasks, contact_fullname,
                            contact_weblink)
Ejemplo n.º 2
0
def incident_engage_oncall_flow(user_email: str,
                                incident_id: int,
                                action: dict,
                                db_session=None):
    """Runs the incident engage oncall flow."""
    oncall_service_id = action["submission"]["oncall_service_id"]
    page = action["submission"]["page"]

    # we load the incident instance
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # we resolve the oncall service
    oncall_service = service_service.get_by_external_id(
        db_session=db_session, external_id=oncall_service_id)
    oncall_plugin = plugins.get(oncall_service.type)
    oncall_email = oncall_plugin.get(service_id=oncall_service_id)

    # we add the oncall to the incident
    incident_add_or_reactivate_participant_flow(oncall_email,
                                                incident.id,
                                                db_session=db_session)

    if page == "Yes":
        # we page the oncall
        oncall_plugin.page(oncall_service_id, incident.name, incident.title,
                           incident.description)

    log.debug(f"{user_email} has engaged oncall service {oncall_service.name}")
Ejemplo n.º 3
0
def create_task_reminders(db_session: SessionLocal, project: Project):
    """Creates multiple task reminders."""
    tasks = task_service.get_overdue_tasks(db_session=db_session,
                                           project_id=project.id)
    log.debug(f"New tasks that need reminders. NumTasks: {len(tasks)}")

    # let's only remind for active incidents for now
    tasks = [t for t in tasks if t.incident.status == IncidentStatus.active]

    if tasks:
        contact_fullname = contact_weblink = DISPATCH_HELP_EMAIL

        # NOTE INCIDENT_ONCALL_SERVICE_ID is optional
        if INCIDENT_ONCALL_SERVICE_ID:
            oncall_service = service_service.get_by_external_id(
                db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID)

            if not oncall_service:
                log.warning(
                    "INCIDENT_ONCALL_SERVICE_ID configured in the .env file, but not found in the database. Did you create the oncall service in the UI?"
                )
                return

            oncall_plugin = plugin_service.get_active_instance(
                db_session=db_session,
                project_id=project.id,
                plugin_type="oncall")

            if not oncall_plugin:
                log.warning(
                    f"Unable to resolve oncall. No oncall plugin is enabled. Project: {project.name}"
                )

            if oncall_plugin.plugin.slug != oncall_service.type:
                log.warning(
                    f"Unable to resolve the oncall. Oncall plugin enabled not of type {oncall_plugin.plugin.slug}."
                )
                return

            if not oncall_plugin:
                log.warning(
                    f"Unable to resolve the oncall, INCIDENT_ONCALL_SERVICE_ID configured, but associated plugin ({oncall_plugin.plugin.slug}) is not enabled."
                )
                contact_fullname = "Unknown"
                contact_weblink = None
            else:
                oncall_email = oncall_plugin.instance.get(
                    service_id=INCIDENT_ONCALL_SERVICE_ID)
                oncall_individual = individual_service.resolve_user_by_email(
                    oncall_email, db_session)
                contact_fullname = oncall_individual["fullname"]
                contact_weblink = oncall_individual["weblink"]

        grouped_tasks = group_tasks_by_assignee(tasks)
        for assignee, tasks in grouped_tasks.items():
            create_reminder(db_session, assignee, tasks, contact_fullname,
                            contact_weblink)
Ejemplo n.º 4
0
def incident_engage_oncall_flow(
    user_email: str, incident_id: int, oncall_service_id: str, page=None, db_session=None
):
    """Runs the incident engage oncall flow."""
    # we load the incident instance
    incident = incident_service.get(db_session=db_session, incident_id=incident_id)

    # we resolve the oncall service
    oncall_service = service_service.get_by_external_id(
        db_session=db_session, external_id=oncall_service_id
    )

    # we get the active oncall plugin
    oncall_plugin = plugin_service.get_active(db_session=db_session, plugin_type="oncall")

    if oncall_plugin:
        if oncall_plugin.slug != oncall_service.type:
            log.warning(
                f"Unable to engage the oncall. Oncall plugin enabled not of type {oncall_plugin.slug}."
            )
            return None, None
    else:
        log.warning("Unable to engage the oncall. No oncall plugins enabled.")
        return None, None

    oncall_email = oncall_plugin.instance.get(service_id=oncall_service_id)

    # we add the oncall to the incident
    incident_add_or_reactivate_participant_flow(oncall_email, incident.id, db_session=db_session)

    # we load the individual
    individual = individual_service.get_by_email(db_session=db_session, email=oncall_email)

    event_service.log(
        db_session=db_session,
        source=oncall_plugin.title,
        description=f"{individual.name} engages oncall service {oncall_service.name}",
        incident_id=incident.id,
    )

    if page == "Yes":
        # we page the oncall
        oncall_plugin.instance.page(
            oncall_service_id, incident.name, incident.title, incident.description
        )

        event_service.log(
            db_session=db_session,
            source=oncall_plugin.title,
            description=f"{oncall_service.name} on-call paged",
            incident_id=incident.id,
        )

    return individual, oncall_service
Ejemplo n.º 5
0
def incident_engage_oncall_flow(user_id: str,
                                user_email: str,
                                incident_id: int,
                                action: dict,
                                db_session=None):
    """Runs the incident engage oncall flow."""
    oncall_service_id = action["submission"]["oncall_service_id"]
    page = action["submission"]["page"]

    # we load the incident instance
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # we resolve the oncall service
    oncall_service = service_service.get_by_external_id(
        db_session=db_session, external_id=oncall_service_id)
    oncall_plugin = plugins.get(oncall_service.type)
    oncall_email = oncall_plugin.get(service_id=oncall_service_id)

    # we add the oncall to the incident
    incident_add_or_reactivate_participant_flow(oncall_email,
                                                incident.id,
                                                db_session=db_session)

    # we load the individual
    individual = individual_service.get_by_email(db_session=db_session,
                                                 email=user_email)

    event_service.log(
        db_session=db_session,
        source=oncall_plugin.title,
        description=
        f"{individual.name} engages oncall service {oncall_service.name}",
        incident_id=incident.id,
    )

    if page == "Yes":
        # we page the oncall
        oncall_plugin.page(oncall_service_id, incident.name, incident.title,
                           incident.description)

        event_service.log(
            db_session=db_session,
            source=oncall_plugin.title,
            description=f"{oncall_service.name} on-call paged",
            incident_id=incident.id,
        )
Ejemplo n.º 6
0
def job_engage_oncall_flow(user_id: str,
                           user_email: str,
                           job_id: int,
                           action: dict,
                           db_session=None):
    """Runs the job engage oncall flow."""
    oncall_service_id = action["submission"]["oncall_service_id"]
    page = action["submission"]["page"]

    # we load the job instance
    job = job_service.get(db_session=db_session, job_id=job_id)

    # we resolve the oncall service
    oncall_service = service_service.get_by_external_id(
        db_session=db_session, external_id=oncall_service_id)
    oncall_plugin = plugins.get(oncall_service.type)
    oncall_email = oncall_plugin.get(service_id=oncall_service_id)

    # we add the oncall to the job
    job_add_or_reactivate_participant_flow(oncall_email,
                                           job.id,
                                           db_session=db_session)

    event_service.log(
        db_session=db_session,
        source=oncall_plugin.job_code,
        description=
        f"{user_email} engages oncall service {oncall_service.name}",
        job_id=job.id,
    )

    if page == "Yes":
        # we page the oncall
        oncall_plugin.page(oncall_service_id, job.name, job.job_code,
                           job.description)

        event_service.log(
            db_session=db_session,
            source=oncall_plugin.job_code,
            description=f"{oncall_service.name} on-call paged",
            job_id=job.id,
        )
Ejemplo n.º 7
0
def daily_summary(db_session=None):
    """Fetches all open incidents and provides a daily summary."""

    blocks = []
    blocks.append({
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": f"*{INCIDENT_DAILY_SUMMARY_DESCRIPTION}*"
        },
    })

    active_incidents = get_all_by_status(db_session=db_session,
                                         status=IncidentStatus.active)
    if active_incidents:
        blocks.append({
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text":
                f"*{INCIDENT_DAILY_SUMMARY_ACTIVE_INCIDENTS_DESCRIPTION}*",
            },
        })
        for idx, incident in enumerate(active_incidents):
            if incident.visibility == Visibility.open:
                try:
                    blocks.append({
                        "type": "section",
                        "text": {
                            "type":
                            "mrkdwn",
                            "text":
                            (f"*<{incident.ticket.weblink}|{incident.name}>*\n"
                             f"*Title*: {incident.title}\n"
                             f"*Type*: {incident.incident_type.name}\n"
                             f"*Priority*: {incident.incident_priority.name}\n"
                             f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>"
                             ),
                        },
                        "block_id":
                        f"{ConversationButtonActions.invite_user}-active-{idx}",
                        "accessory": {
                            "type": "button",
                            "text": {
                                "type": "plain_text",
                                "text": "Join Incident"
                            },
                            "value": f"{incident.id}",
                        },
                    })
                except Exception as e:
                    sentry_sdk.capture_exception(e)
    else:
        blocks.append({
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": INCIDENT_DAILY_SUMMARY_NO_ACTIVE_INCIDENTS_DESCRIPTION,
            },
        })

    blocks.append({"type": "divider"})
    blocks.append({
        "type": "section",
        "text": {
            "type":
            "mrkdwn",
            "text":
            f"*{INCIDENT_DAILY_SUMMARY_STABLE_CLOSED_INCIDENTS_DESCRIPTION}*",
        },
    })

    hours = 24
    stable_incidents = get_all_last_x_hours_by_status(
        db_session=db_session, status=IncidentStatus.stable, hours=hours)
    closed_incidents = get_all_last_x_hours_by_status(
        db_session=db_session, status=IncidentStatus.closed, hours=hours)
    if stable_incidents or closed_incidents:
        for idx, incident in enumerate(stable_incidents):
            if incident.visibility == Visibility.open:
                try:
                    blocks.append({
                        "type": "section",
                        "text": {
                            "type":
                            "mrkdwn",
                            "text":
                            (f"*<{incident.ticket.weblink}|{incident.name}>*\n"
                             f"*Title*: {incident.title}\n"
                             f"*Type*: {incident.incident_type.name}\n"
                             f"*Priority*: {incident.incident_priority.name}\n"
                             f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n"
                             f"*Status*: {incident.status}"),
                        },
                        "block_id":
                        f"{ConversationButtonActions.invite_user}-{idx}",
                        "accessory": {
                            "type": "button",
                            "text": {
                                "type": "plain_text",
                                "text": "Join Incident"
                            },
                            "value": f"{incident.id}",
                        },
                    })
                except Exception as e:
                    sentry_sdk.capture_exception(e)

        for incident in closed_incidents:
            if incident.visibility == Visibility.open:
                try:
                    blocks.append({
                        "type": "section",
                        "text": {
                            "type":
                            "mrkdwn",
                            "text":
                            (f"*<{incident.ticket.weblink}|{incident.name}>*\n"
                             f"*Title*: {incident.title}\n"
                             f"*Type*: {incident.incident_type.name}\n"
                             f"*Priority*: {incident.incident_priority.name}\n"
                             f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n"
                             f"*Status*: {incident.status}"),
                        },
                    })
                except Exception as e:
                    sentry_sdk.capture_exception(e)
    else:
        blocks.append({
            "type": "section",
            "text": {
                "type":
                "mrkdwn",
                "text":
                INCIDENT_DAILY_SUMMARY_NO_STABLE_CLOSED_INCIDENTS_DESCRIPTION,
            },
        })

    # NOTE INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID is optional
    if INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID:
        oncall_service = service_service.get_by_external_id(
            db_session=db_session,
            external_id=INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID)

        oncall_plugin = plugins.get(oncall_service.type)
        oncall_email = oncall_plugin.get(
            service_id=INCIDENT_DAILY_SUMMARY_ONCALL_SERVICE_ID)

        oncall_individual = individual_service.resolve_user_by_email(
            oncall_email)

        blocks.append({
            "type":
            "context",
            "elements": [{
                "type":
                "mrkdwn",
                "text":
                f"For questions about this notification, reach out to <{oncall_individual['weblink']}|{oncall_individual['fullname']}> (current on-call)",
            }],
        })

    convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG)
    for c in INCIDENT_NOTIFICATION_CONVERSATIONS:
        convo_plugin.send(c, "Incident Daily Summary", {}, "", blocks=blocks)
Ejemplo n.º 8
0
def test_get_by_external_id(session, service):
    from dispatch.service.service import get_by_external_id

    t_service = get_by_external_id(db_session=session,
                                   external_id=service.external_id)
    assert t_service.external_id == service.external_id
Ejemplo n.º 9
0
def daily_summary(db_session=None):
    """
    Fetches all active, and stable and closed incidents in the last 24 hours
    and sends a daily summary to all incident notification conversations.
    """
    blocks = []
    blocks.append(
        {
            "type": "header",
            "text": {
                "type": "plain_text",
                "text": f"{INCIDENT_DAILY_SUMMARY_DESCRIPTION}",
            },
        }
    )

    active_incidents = get_all_by_status(db_session=db_session, status=IncidentStatus.active)
    if active_incidents:
        blocks.append(
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{INCIDENT_DAILY_SUMMARY_ACTIVE_INCIDENTS_DESCRIPTION}*",
                },
            }
        )
        for idx, incident in enumerate(active_incidents):
            ticket_weblink = resolve_attr(incident, "ticket.weblink")
            if incident.visibility == Visibility.open:
                try:
                    blocks.append(
                        {
                            "type": "section",
                            "text": {
                                "type": "mrkdwn",
                                "text": (
                                    f"*<{ticket_weblink}|{incident.name}>*\n"
                                    f"*Title*: {incident.title}\n"
                                    f"*Type*: {incident.incident_type.name}\n"
                                    f"*Priority*: {incident.incident_priority.name}\n"
                                    f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>"
                                ),
                            },
                            "block_id": f"{ConversationButtonActions.invite_user}-active-{idx}",
                            "accessory": {
                                "type": "button",
                                "text": {"type": "plain_text", "text": "Join Incident"},
                                "value": f"{incident.id}",
                            },
                        }
                    )
                except Exception as e:
                    log.exception(e)

        blocks.append(
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f"For more information about active incidents, please visit the active incidents status <{DISPATCH_UI_URL}/incidents/status|page>.",
                    }
                ],
            }
        )
    else:
        blocks.append(
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": INCIDENT_DAILY_SUMMARY_NO_ACTIVE_INCIDENTS_DESCRIPTION,
                },
            }
        )

    blocks.append({"type": "divider"})
    blocks.append(
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": f"*{INCIDENT_DAILY_SUMMARY_STABLE_CLOSED_INCIDENTS_DESCRIPTION}*",
            },
        }
    )

    hours = 24
    stable_incidents = get_all_last_x_hours_by_status(
        db_session=db_session, status=IncidentStatus.stable, hours=hours
    )
    closed_incidents = get_all_last_x_hours_by_status(
        db_session=db_session, status=IncidentStatus.closed, hours=hours
    )
    if stable_incidents or closed_incidents:
        for idx, incident in enumerate(stable_incidents):
            ticket_weblink = resolve_attr(incident, "ticket.weblink")

            if incident.visibility == Visibility.open:
                try:
                    blocks.append(
                        {
                            "type": "section",
                            "text": {
                                "type": "mrkdwn",
                                "text": (
                                    f"*<{ticket_weblink}|{incident.name}>*\n"
                                    f"*Title*: {incident.title}\n"
                                    f"*Type*: {incident.incident_type.name}\n"
                                    f"*Priority*: {incident.incident_priority.name}\n"
                                    f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n"
                                    f"*Status*: {incident.status}"
                                ),
                            },
                            "block_id": f"{ConversationButtonActions.invite_user}-stable-{idx}",
                            "accessory": {
                                "type": "button",
                                "text": {"type": "plain_text", "text": "Join Incident"},
                                "value": f"{incident.id}",
                            },
                        }
                    )
                except Exception as e:
                    log.exception(e)

        for incident in closed_incidents:
            ticket_weblink = resolve_attr(incident, "ticket.weblink")

            if incident.visibility == Visibility.open:
                try:
                    blocks.append(
                        {
                            "type": "section",
                            "text": {
                                "type": "mrkdwn",
                                "text": (
                                    f"*<{ticket_weblink}|{incident.name}>*\n"
                                    f"*Title*: {incident.title}\n"
                                    f"*Type*: {incident.incident_type.name}\n"
                                    f"*Priority*: {incident.incident_priority.name}\n"
                                    f"*Incident Commander*: <{incident.commander.weblink}|{incident.commander.name}>\n"
                                    f"*Status*: {incident.status}"
                                ),
                            },
                        }
                    )
                except Exception as e:
                    log.exception(e)
    else:
        blocks.append(
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": INCIDENT_DAILY_SUMMARY_NO_STABLE_CLOSED_INCIDENTS_DESCRIPTION,
                },
            }
        )

    # NOTE INCIDENT_ONCALL_SERVICE_ID is optional
    if INCIDENT_ONCALL_SERVICE_ID:
        oncall_service = service_service.get_by_external_id(
            db_session=db_session, external_id=INCIDENT_ONCALL_SERVICE_ID
        )

        if not oncall_service:
            log.warning(
                "INCIDENT_ONCALL_SERVICE_ID configured in the .env file, but not found in the database. Did you create the oncall service in the UI?"
            )
            return

        oncall_plugin = plugin_service.get_active(db_session=db_session, plugin_type="oncall")
        if not oncall_plugin:
            log.warning(
                f"Unable to resolve the oncall, INCIDENT_ONCALL_SERVICE_ID configured, but associated plugin ({oncall_plugin.slug}) is not enabled."
            )
            return

        if oncall_plugin.slug != oncall_service.type:
            log.warning(
                f"Unable to resolve the oncall. Oncall plugin enabled not of type {oncall_plugin.slug}."
            )
            return

        oncall_email = oncall_plugin.instance.get(service_id=INCIDENT_ONCALL_SERVICE_ID)
        oncall_individual = individual_service.resolve_user_by_email(oncall_email, db_session)

        blocks.append(
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f"For any questions about this notification, please reach out to <{oncall_individual['weblink']}|{oncall_individual['fullname']}> (current on-call)",
                    }
                ],
            }
        )

    plugin = plugin_service.get_active(db_session=db_session, plugin_type="conversation")

    for c in INCIDENT_NOTIFICATION_CONVERSATIONS:
        plugin.instance.send(c, "Incident Daily Summary", {}, "", blocks=blocks)