Beispiel #1
0
def test_create_instance(session, incident, workflow, participant, project):
    from dispatch.workflow.service import create_instance
    from dispatch.workflow.models import WorkflowInstanceCreate
    from dispatch.document.models import DocumentCreate

    parameters = [{}]
    run_reason = "reason"
    status = "submitted"

    artifacts = [
        DocumentCreate(
            name="name",
            resource_id="resource_id",
            resource_type="resource_type",
            project=project,
            weblink="https://www.example.com/doc",
        )
    ]

    workflow_in = WorkflowInstanceCreate(
        parameters=parameters,
        run_reason=run_reason,
        status=status,
        incident=incident,
        workflow=workflow,
        creator=participant,
        artifacts=artifacts,
    )
    workflow_instance = create_instance(db_session=session,
                                        workflow_in=workflow_in)
    assert workflow_instance
Beispiel #2
0
def create_instance(*, db_session,
                    instance_in: WorkflowInstanceCreate) -> WorkflowInstance:
    """Creates a new workflow instance."""
    instance = WorkflowInstance(**instance_in.dict(
        exclude={"incident", "workflow", "creator", "artifacts"}))

    incident = incident_service.get(db_session=db_session,
                                    incident_id=instance_in.incident["id"])
    instance.incident = incident

    workflow = get(db_session=db_session,
                   workflow_id=instance_in.workflow["id"])
    instance.workflow = workflow

    creator = participant_service.get_by_incident_id_and_email(
        db_session=db_session,
        incident_id=incident.id,
        email=instance_in.creator["email"])
    instance.creator = creator

    for a in instance_in.artifacts:
        artifact_document = document_service.create(
            db_session=db_session, document_in=DocumentCreate(**a))
        instance.artifacts.append(artifact_document)

    db_session.add(instance)
    db_session.commit()

    return instance
def test_create(session, project):
    from dispatch.document.service import create
    from dispatch.document.models import DocumentCreate

    name = "XXX"
    resource_id = "XXX"
    resource_type = "XXX"
    weblink = "https://example.com/"

    document_in = DocumentCreate(
        name=name,
        resource_id=resource_id,
        resource_type=resource_type,
        weblink=weblink,
        project={"id": project.id, "name": project.name},
    )
    document = create(db_session=session, document_in=document_in)
    assert document
Beispiel #4
0
def create_executive_report(
    user_email: str,
    incident_id: int,
    executive_report_in: ExecutiveReportCreate,
    organization_slug: str = None,
    db_session=None,
):
    """Creates an executive report."""
    current_date = date.today().strftime("%B %d, %Y")

    current_status = executive_report_in.current_status
    overview = executive_report_in.overview
    next_steps = executive_report_in.next_steps

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

    if not incident.incident_type.executive_template_document:
        raise ValidationError(
            [
                ErrorWrapper(
                    InvalidConfigurationError(
                        msg="No executive report template defined."),
                    loc="executive_template_document",
                )
            ],
            model=ExecutiveReportCreate,
        )

    # we fetch all previous executive reports
    executive_reports = get_all_by_incident_id_and_type(
        db_session=db_session,
        incident_id=incident_id,
        report_type=ReportTypes.executive_report)

    previous_executive_reports = []
    for executive_report in executive_reports:
        previous_executive_reports.append(
            f"{executive_report.document.name} - {executive_report.document.weblink}\n"
        )

    # we create a new executive report
    details = {
        "current_status": current_status,
        "overview": overview,
        "next_steps": next_steps
    }
    executive_report_in = ReportCreate(
        details=details,
        type=ReportTypes.executive_report,
    )
    executive_report = create(db_session=db_session,
                              report_in=executive_report_in)

    # we load the participant
    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email)

    # we save the executive report
    participant.reports.append(executive_report)
    incident.reports.append(executive_report)

    db_session.add(participant)
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Incident Participant",
        description=
        f"{participant.individual.name} created a new executive report",
        details={
            "current_status": current_status,
            "overview": overview,
            "next_steps": next_steps
        },
        incident_id=incident_id,
        individual_id=participant.individual.id,
    )

    # we create a new document for the executive report
    storage_plugin = plugin_service.get_active_instance(
        db_session=db_session,
        project_id=incident.project.id,
        plugin_type="storage")
    executive_report_document_name = f"{incident.name} - Executive Report - {current_date}"
    executive_report_document = storage_plugin.instance.copy_file(
        folder_id=incident.storage.resource_id,
        file_id=incident.incident_type.executive_template_document.resource_id,
        name=executive_report_document_name,
    )

    executive_report_document.update({
        "name":
        executive_report_document_name,
        "resource_type":
        DocumentResourceTypes.executive,
    })

    storage_plugin.instance.move_file(
        new_folder_id=incident.storage.resource_id,
        file_id=executive_report_document["id"])

    event_service.log(
        db_session=db_session,
        source=storage_plugin.plugin.title,
        description="Executive report document added to storage",
        incident_id=incident.id,
    )

    document_in = DocumentCreate(
        name=executive_report_document["name"],
        resource_id=executive_report_document["id"],
        resource_type=executive_report_document["resource_type"],
        project=incident.project,
        weblink=executive_report_document["weblink"],
    )
    executive_report.document = document_service.create(
        db_session=db_session, document_in=document_in)

    incident.documents.append(executive_report.document)

    db_session.add(executive_report)
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Executive report document added to incident",
        incident_id=incident.id,
    )

    # we update the incident update document
    document_plugin = plugin_service.get_active_instance(
        db_session=db_session,
        project_id=incident.project.id,
        plugin_type="document")
    document_plugin.instance.update(
        executive_report_document["id"],
        name=incident.name,
        title=incident.title,
        current_date=current_date,
        current_status=current_status,
        overview=overview,
        next_steps=next_steps,
        previous_reports="\n".join(previous_executive_reports),
        commander_fullname=incident.commander.individual.name,
        commander_team=incident.commander.team,
        commander_weblink=incident.commander.individual.weblink,
    )

    # we send the executive report to the notifications group
    send_executive_report_to_notifications_group(incident.id, executive_report,
                                                 db_session)

    return executive_report
Beispiel #5
0
def incident_stable_status_flow(incident: Incident, db_session=None):
    """Runs the incident stable flow."""
    # we set the stable time
    incident.stable_at = datetime.utcnow()

    # set time immediately
    db_session.add(incident)
    db_session.commit()

    if incident.incident_review_document:
        log.debug("Incident review document already created... skipping creation.")
        return

    storage_plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage")
    if not storage_plugin:
        log.warning("Incident review document not created, no storage plugin enabled.")
        return

    # we create a copy of the incident review document template and we move it to the incident storage
    incident_review_document_name = f"{incident.name} - Post Incident Review Document"
    template = document_service.get_incident_review_template(db_session=db_session)

    # incident review document is optional
    if not template:
        log.warning("No incident review template specificed.")
        return

    incident_review_document = storage_plugin.instance.copy_file(
        folder_id=incident.storage.resource_id,
        file_id=template.resource_id,
        name=incident_review_document_name,
    )

    incident_review_document.update(
        {
            "name": incident_review_document_name,
            "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT,
        }
    )

    storage_plugin.instance.move_file(
        new_folder_id=incident.storage.resource_id,
        file_id=incident_review_document["id"],
    )

    event_service.log(
        db_session=db_session,
        source=storage_plugin.title,
        description="Incident review document added to storage",
        incident_id=incident.id,
    )

    document_in = DocumentCreate(
        name=incident_review_document["name"],
        resource_id=incident_review_document["id"],
        resource_type=incident_review_document["resource_type"],
        weblink=incident_review_document["weblink"],
    )
    incident.documents.append(
        document_service.create(db_session=db_session, document_in=document_in)
    )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident review document added to incident",
        incident_id=incident.id,
    )

    # we update the incident review document
    document_plugin = plugin_service.get_active(db_session=db_session, plugin_type="document")
    if document_plugin:
        document_plugin.instance.update(
            incident.incident_review_document.resource_id,
            name=incident.name,
            priority=incident.incident_priority.name,
            status=incident.status,
            type=incident.incident_type.name,
            title=incident.title,
            description=incident.description,
            commander_fullname=incident.commander.name,
            conversation_weblink=resolve_attr(incident, "conversation.weblink"),
            document_weblink=resolve_attr(incident, "incident_document.weblink"),
            storage_weblink=resolve_attr(incident, "storage.weblink"),
            ticket_weblink=resolve_attr(incident, "ticket.weblink"),
            conference_weblink=resolve_attr(incident, "conference.weblink"),
            conference_challenge=resolve_attr(incident, "conference.challendge"),
        )
    else:
        log.warning("No document plugin enabled, could not update template.")

    # we send a notification about the incident review document to the conversation
    send_incident_review_document_notification(
        incident.conversation.channel_id,
        incident.incident_review_document.weblink,
        db_session,
    )

    db_session.add(incident)
    db_session.commit()
Beispiel #6
0
def incident_create_flow(*, incident_id: int, checkpoint: str = None, db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session, incident_id=incident_id)

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    if ticket:
        incident.ticket = ticket_service.create(
            db_session=db_session, ticket_in=TicketCreate(**ticket)
        )

        # we set the incident name
        incident.name = ticket["resource_id"]

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(
            user_email=individual.email, incident_id=incident.id, db_session=db_session
        )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    participant_emails = [x.individual.email for x in incident.participants]

    group_plugin = plugin_service.get_active(db_session=db_session, plugin_type="participant-group")
    tactical_group = None
    notification_group = None
    if group_plugin:
        try:
            tactical_group, notification_group = create_participant_groups(
                incident, individual_participants, team_participants, db_session
            )

            for g in [tactical_group, notification_group]:
                group_in = GroupCreate(
                    name=g["name"],
                    email=g["email"],
                    resource_type=g["resource_type"],
                    resource_id=g["resource_id"],
                    weblink=g["weblink"],
                )
                incident.groups.append(
                    group_service.create(db_session=db_session, group_in=group_in)
                )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Tactical and notification groups added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of tactical and notification groups failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    storage_plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage")
    if storage_plugin:
        # we create storage resource
        if group_plugin:
            storage = create_incident_storage(
                incident, [tactical_group["email"], notification_group["email"]], db_session
            )
        else:
            # we don't have a group so add participants directly
            storage = create_incident_storage(incident, participant_emails, db_session)

        incident.storage = storage_service.create(
            db_session=db_session,
            resource_id=storage["resource_id"],
            resource_type=storage["resource_type"],
            weblink=storage["weblink"],
        )

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Storage added to incident",
            incident_id=incident.id,
        )

        # we create collaboration documents, don't fail the whole flow if this fails
        try:
            collab_documents = create_collaboration_documents(incident, db_session)

            for d in collab_documents:
                document_in = DocumentCreate(
                    name=d["name"],
                    resource_id=d["resource_id"],
                    resource_type=d["resource_type"],
                    weblink=d["weblink"],
                )
                incident.documents.append(
                    document_service.create(db_session=db_session, document_in=document_in)
                )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Documents added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident documents failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    conference_plugin = plugin_service.get_active(db_session=db_session, plugin_type="conference")
    if conference_plugin:
        try:
            participants = participant_emails
            
            if group_plugin:
                # we use the tactical group email if the group plugin is enabled
                participants = [tactical_group["email"]]
                
            conference = create_conference(incident, participants, db_session)

            conference_in = ConferenceCreate(
                resource_id=conference["resource_id"],
                resource_type=conference["resource_type"],
                weblink=conference["weblink"],
                conference_id=conference["id"],
                conference_challenge=conference["challenge"],
            )
            incident.conference = conference_service.create(
                db_session=db_session, conference_in=conference_in
            )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Conference added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident conference failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    # we create the conversation for real-time communications

    conversation_plugin = plugin_service.get_active(
        db_session=db_session, plugin_type="conversation"
    )
    if conversation_plugin:
        try:
            conversation = create_conversation(incident, participant_emails, db_session)

            conversation_in = ConversationCreate(
                resource_id=conversation["resource_id"],
                resource_type=conversation["resource_type"],
                weblink=conversation["weblink"],
                channel_id=conversation["id"],
            )
            incident.conversation = conversation_service.create(
                db_session=db_session, conversation_in=conversation_in
            )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Conversation added to incident",
                incident_id=incident.id,
            )

            # we set the conversation topic
            set_conversation_topic(incident, db_session)
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident conversation failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    db_session.add(incident)
    db_session.commit()

    # we update the incident ticket
    update_external_incident_ticket(incident, db_session)

    # we update the investigation document
    document_plugin = plugin_service.get_active(db_session=db_session, plugin_type="document")
    if document_plugin:
        if incident.incident_document:
            try:
                document_plugin.instance.update(
                    incident.incident_document.resource_id,
                    name=incident.name,
                    priority=incident.incident_priority.name,
                    status=incident.status,
                    type=incident.incident_type.name,
                    title=incident.title,
                    description=incident.description,
                    commander_fullname=incident.commander.name,
                    conversation_weblink=resolve_attr(incident, "conversation.weblink"),
                    document_weblink=resolve_attr(incident, "incident_document.weblink"),
                    storage_weblink=resolve_attr(incident, "storage.weblink"),
                    ticket_weblink=resolve_attr(incident, "ticket.weblink"),
                    conference_weblink=resolve_attr(incident, "conference.weblink"),
                    conference_challenge=resolve_attr(incident, "conference.challendge"),
                )
            except Exception as e:
                event_service.log(
                    db_session=db_session,
                    source="Dispatch Core App",
                    description=f"Incident documents rendering failed. Reason: {e}",
                    incident_id=incident.id,
                )
                log.exception(e)

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )

    suggested_document_items = get_suggested_document_items(incident.id, db_session)

    for participant in incident.participants:
        # we announce the participant in the conversation
        # should protect ourselves from failures of any one participant
        try:
            send_incident_participant_announcement_message(
                participant.individual.email, incident.id, db_session
            )

            # we send the welcome messages to the participant
            send_incident_welcome_participant_messages(
                participant.individual.email, incident.id, db_session
            )

            send_incident_suggested_reading_messages(
                incident.id, suggested_document_items, participant.individual.email, db_session
            )

        except Exception as e:
            log.exception(e)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )
Beispiel #7
0
def incident_stable_flow(incident_id: int,
                         command: Optional[dict] = None,
                         db_session=None):
    """Runs the incident stable flow."""
    # we load the incident instance
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # we set the stable time
    incident.stable_at = datetime.utcnow()

    # we remind the incident commander to write a status report
    send_incident_status_report_reminder(incident)

    # we update the incident cost
    incident_cost = incident_service.calculate_cost(incident_id, db_session)

    # we update the external ticket
    update_incident_ticket(incident.ticket.resource_id,
                           status=IncidentStatus.stable.lower(),
                           cost=incident_cost)

    incident_review_document = get_document(
        db_session=db_session,
        incident_id=incident.id,
        resource_type=INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT,
    )

    if not incident_review_document:
        storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG)

        # we create a copy of the incident review document template and we move it to the incident storage
        incident_review_document_name = f"{incident.name} - Post Incident Review Document"
        incident_review_document = storage_plugin.copy_file(
            team_drive_id=incident.storage.resource_id,
            file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID,
            name=incident_review_document_name,
        )

        incident_review_document.update({
            "name":
            incident_review_document_name,
            "resource_type":
            INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT,
        })

        storage_plugin.move_file(
            new_team_drive_id=incident.storage.resource_id,
            file_id=incident_review_document["id"])

        event_service.log(
            db_session=db_session,
            source=storage_plugin.title,
            description="Incident review document added to storage",
            incident_id=incident.id,
        )

        document_in = DocumentCreate(
            name=incident_review_document["name"],
            resource_id=incident_review_document["id"],
            resource_type=incident_review_document["resource_type"],
            weblink=incident_review_document["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident review document added to incident",
            incident_id=incident.id,
        )

        # we get the incident investigation and faq documents
        incident_document = get_document(
            db_session=db_session,
            incident_id=incident_id,
            resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT,
        )

        # we update the incident review document
        update_document(
            incident_review_document["id"],
            incident.name,
            incident.incident_priority.name,
            incident.status,
            incident.incident_type.name,
            incident.title,
            incident.description,
            incident.commander.name,
            incident.conversation.weblink,
            incident_document.weblink,
            incident.storage.weblink,
            incident.ticket.weblink,
        )

        # we send a notification about the incident review document to the conversation
        send_incident_review_document_notification(
            incident.conversation.channel_id,
            incident_review_document["weblink"])

    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description=f"Incident marked as {incident.status}",
        incident_id=incident.id,
    )
Beispiel #8
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(user_email=individual.email,
                                          incident_id=incident.id,
                                          db_session=db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="External ticket added to incident",
        incident_id=incident.id,
    )

    # we set the incident name
    name = ticket["resource_id"]
    incident.name = name

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    tactical_group, notification_group = create_participant_groups(
        incident, individual_participants, team_participants, db_session)

    for g in [tactical_group, notification_group]:
        group_in = GroupCreate(
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(
            group_service.create(db_session=db_session, group_in=group_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Tactical and notification groups added to incident",
        incident_id=incident.id,
    )

    # we create storage resource
    storage = create_incident_storage(
        incident, [tactical_group["email"], notification_group["email"]],
        db_session)
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Storage added to incident",
        incident_id=incident.id,
    )

    # we create the incident documents
    incident_document, incident_sheet = create_collaboration_documents(
        incident, db_session)

    # TODO: we need to delineate between the investigation document and suggested documents
    # # get any additional documentation based on priority or terms
    # incident_documents = get_incident_documents(
    #     db_session, incident.incident_type, incident.incident_priority, incident.description
    # )
    #
    # incident.documents = incident_documents

    faq_document = {
        "name": "Incident FAQ",
        "resource_id": INCIDENT_FAQ_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}",
        "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT,
    }

    conversation_commands_reference_document = {
        "name":
        "Incident Conversation Commands Reference Document",
        "resource_id":
        INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID}",
        "resource_type":
        INCIDENT_RESOURCE_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT,
    }

    for d in [
            incident_document,
            incident_sheet,
            faq_document,
            conversation_commands_reference_document,
    ]:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Documents added to incident",
        incident_id=incident.id,
    )

    conference = create_conference(incident, [tactical_group["email"]],
                                   db_session)

    conference_in = ConferenceCreate(
        resource_id=conference["resource_id"],
        resource_type=conference["resource_type"],
        weblink=conference["weblink"],
        conference_id=conference["id"],
        conference_challenge=conference["challenge"],
    )
    incident.conference = conference_service.create(
        db_session=db_session, conference_in=conference_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conference added to incident",
        incident_id=incident.id,
    )

    # we create the conversation for real-time communications
    participant_emails = [x.individual.email for x in incident.participants]
    conversation = create_conversation(incident, participant_emails,
                                       db_session)

    conversation_in = ConversationCreate(
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    incident.conversation = conversation_service.create(
        db_session=db_session, conversation_in=conversation_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conversation added to incident",
        incident_id=incident.id,
    )

    db_session.add(incident)
    db_session.commit()

    # we set the conversation topic
    set_conversation_topic(incident)

    # we update the incident ticket
    update_incident_ticket(
        incident.ticket.resource_id,
        title=incident.title,
        description=incident.description,
        incident_type=incident.incident_type.name,
        priority=incident.incident_priority.name,
        status=incident.status,
        commander_email=incident.commander.email,
        reporter_email=incident.reporter.email,
        conversation_weblink=incident.conversation.weblink,
        document_weblink=incident_document["weblink"],
        storage_weblink=incident.storage.weblink,
        conference_weblink=incident.conference.weblink,
        visibility=incident.visibility,
    )

    # we update the investigation document
    update_document(
        incident_document["id"],
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.incident_type.name,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
        incident.ticket.weblink,
        incident.conference.weblink,
        incident.conference.conference_challenge,
    )

    for participant in incident.participants:
        # we announce the participant in the conversation
        send_incident_participant_announcement_message(
            participant.individual.email, incident.id, db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(
            participant.individual.email, incident.id, db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )
Beispiel #9
0
def incident_stable_flow(incident_id: int,
                         command: Optional[dict] = None,
                         db_session=None):
    """Runs the incident stable flow."""
    # we load the incident instance
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    if incident.status == IncidentStatus.stable:
        if command:
            convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG)
            convo_plugin.send_ephemeral(
                command["channel_id"],
                command["user_id"],
                "Incident Already Stable Notification",
                blocks=[{
                    "type": "section",
                    "text": {
                        "type":
                        "plain_text",
                        "text":
                        "The incident is already stable. Aborting command...",
                    },
                }],
            )
            return

    # we update the status of the incident
    update_incident_status(db_session=db_session,
                           incident=incident,
                           status=IncidentStatus.stable)

    log.debug(
        f"We have updated the status of the incident to {IncidentStatus.stable}."
    )

    # we update the incident cost
    incident_cost = incident_service.calculate_cost(incident_id, db_session)

    log.debug(f"We have updated the cost of the incident.")

    # we update the external ticket
    update_incident_ticket(
        incident.ticket.resource_id,
        incident_type=incident.incident_type.name,
        status=IncidentStatus.stable.lower(),
        cost=incident_cost,
    )

    log.debug(
        f"We have updated the status of the external ticket to {IncidentStatus.stable}."
    )

    # we update the conversation topic
    set_conversation_topic(incident)

    incident_review_document = get_document(
        db_session=db_session,
        incident_id=incident.id,
        resource_type=INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT,
    )

    if not incident_review_document:
        storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG)

        # we create a copy of the incident review document template and we move it to the incident storage
        incident_review_document_name = f"{incident.name} - Post Incident Review Document"
        incident_review_document = storage_plugin.copy_file(
            team_drive_id=incident.storage.resource_id,
            file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID,
            name=incident_review_document_name,
        )

        incident_review_document.update({
            "name":
            incident_review_document_name,
            "resource_type":
            INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT,
        })

        storage_plugin.move_file(
            new_team_drive_id=incident.storage.resource_id,
            file_id=incident_review_document["id"])

        log.debug(
            "We have added the incident review document in the incident storage."
        )

        document_in = DocumentCreate(
            name=incident_review_document["name"],
            resource_id=incident_review_document["id"],
            resource_type=incident_review_document["resource_type"],
            weblink=incident_review_document["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

        db_session.add(incident)
        db_session.commit()

        log.debug(
            "We have added the incident review document to the incident.")

        # we get the incident investigation and faq documents
        incident_document = get_document(
            db_session=db_session,
            incident_id=incident_id,
            resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT,
        )

        # we update the incident review document
        update_document(
            incident_review_document["id"],
            incident.name,
            incident.incident_priority.name,
            incident.status,
            incident.title,
            incident.description,
            incident.commander.name,
            incident.conversation.weblink,
            incident_document.weblink,
            incident.storage.weblink,
            incident.ticket.weblink,
        )

        log.debug("We have updated the incident review document.")

        # we send a notification about the incident review document to the conversation
        send_incident_review_document_notification(
            incident.conversation.channel_id,
            incident_review_document["weblink"])

        log.debug(
            "We have sent a notification about the incident review document to the conversation."
        )

    # we send the stable notifications
    send_incident_status_notifications(incident, db_session)

    log.debug("We have sent the incident stable notifications.")
Beispiel #10
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        db_session, incident.incident_type, incident.incident_priority,
        incident.description)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(db_session=db_session,
                                          user_email=individual.email,
                                          incident_id=incident.id)

    log.debug(f"Added {len(individual_participants)} to incident.")

    # create the incident ticket
    ticket = create_incident_ticket(
        incident.title,
        incident.incident_type.name,
        incident.incident_priority.name,
        incident.commander.email,
        incident.reporter.email,
    )

    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    log.debug("Added ticket to incident.")

    # we set the incident name
    name = ticket["resource_id"]

    incident.name = name

    log.debug("Added name to incident.")

    # we create the participant groups (tactical and notification)
    tactical_group, notification_group = create_participant_groups(
        name, team_participants, [x.individual for x in incident.participants])

    for g in [tactical_group, notification_group]:
        group = group_service.create(
            db_session=db_session,
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(group)

    log.debug("Added groups to incident.")

    # we create storage resource
    storage = create_incident_storage(
        name, [tactical_group["email"], notification_group["email"]])
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    # we create the incident documents
    incident_document, incident_sheet = create_collaboration_documents(
        incident.name,
        incident.incident_type.name,
        incident.storage.resource_id,
        incident.incident_type.template_document.resource_id,
    )

    # TODO: we need to delineate between the investigation document and suggested documents
    # # get any additional documentation based on priority or terms
    # incident_documents = get_incident_documents(
    #     db_session, incident.incident_type, incident.incident_priority, incident.description
    # )
    #
    # incident.documents = incident_documents

    faq_document = {
        "name": "Incident FAQ",
        "resource_id": INCIDENT_FAQ_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}",
        "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT,
    }

    for d in [incident_document, incident_sheet, faq_document]:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    log.debug("Added documents to incident.")

    # we create the conversation for real-time communications
    conversation = create_conversation(
        incident, [x.individual.email for x in incident.participants])

    log.debug("Conversation created. Participants and bots added.")

    incident.conversation = conversation_service.create(
        db_session=db_session,
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    db_session.add(incident)
    db_session.commit()

    log.debug("Added conversation to incident.")

    # we set the conversation topic
    set_conversation_topic(incident)

    update_incident_ticket(
        incident.ticket.resource_id,
        incident.title,
        incident.description,
        incident.incident_type.name,
        incident.incident_priority.name,
        incident.status,
        incident.commander.email,
        incident.reporter.email,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
    )

    log.debug("Updated incident ticket.")

    update_document(
        incident_document["id"],
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
        incident.ticket.weblink,
    )

    log.debug("Updated incident document.")

    for participant in incident.participants:
        # we announce the participant in the conversation
        send_incident_participant_announcement_message(
            participant.individual.email, incident, db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(
            participant.individual.email, incident, db_session)

    log.debug("Sent incident welcome and announcement notifications.")

    send_incident_notifications(incident, db_session)

    log.debug("Sent incident notifications.")
Beispiel #11
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(user_email=individual.email,
                                          incident_id=incident.id,
                                          db_session=db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="External ticket added to incident",
        incident_id=incident.id,
    )

    # we set the incident name
    name = ticket["resource_id"]
    incident.name = name

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    tactical_group, notification_group = create_participant_groups(
        incident, individual_participants, team_participants, db_session)

    for g in [tactical_group, notification_group]:
        group_in = GroupCreate(
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(
            group_service.create(db_session=db_session, group_in=group_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Tactical and notification groups added to incident",
        incident_id=incident.id,
    )

    # we create storage resource
    storage = create_incident_storage(
        incident, [tactical_group["email"], notification_group["email"]],
        db_session)
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Storage added to incident",
        incident_id=incident.id,
    )

    # we create the incident documents
    collab_documents = create_collaboration_documents(incident, db_session)

    for d in collab_documents:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Documents added to incident",
        incident_id=incident.id,
    )

    conference = create_conference(incident, [tactical_group["email"]],
                                   db_session)

    conference_in = ConferenceCreate(
        resource_id=conference["resource_id"],
        resource_type=conference["resource_type"],
        weblink=conference["weblink"],
        conference_id=conference["id"],
        conference_challenge=conference["challenge"],
    )
    incident.conference = conference_service.create(
        db_session=db_session, conference_in=conference_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conference added to incident",
        incident_id=incident.id,
    )

    # we create the conversation for real-time communications
    participant_emails = [x.individual.email for x in incident.participants]
    conversation = create_conversation(incident, participant_emails,
                                       db_session)

    conversation_in = ConversationCreate(
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    incident.conversation = conversation_service.create(
        db_session=db_session, conversation_in=conversation_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conversation added to incident",
        incident_id=incident.id,
    )

    db_session.add(incident)
    db_session.commit()

    # we set the conversation topic
    set_conversation_topic(incident)

    # we update the incident ticket
    update_external_incident_ticket(incident, db_session)

    # we update the investigation document
    update_document(
        incident.incident_document.resource_id,
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.incident_type.name,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident.incident_document.weblink,
        incident.storage.weblink,
        incident.ticket.weblink,
        incident.conference.weblink,
        incident.conference.conference_challenge,
    )

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )

    suggested_document_items = get_suggested_document_items(
        incident, db_session)

    for participant in incident.participants:
        # we announce the participant in the conversation
        # should protect ourselves from failures of any one participant
        try:
            send_incident_participant_announcement_message(
                participant.individual.email, incident.id, db_session)

            # we send the welcome messages to the participant
            send_incident_welcome_participant_messages(
                participant.individual.email, incident.id, db_session)

            send_incident_suggested_reading_messages(incident,
                                                     suggested_document_items,
                                                     participant.email)

        except Exception as e:
            log.exception(e)
            sentry_sdk.capture_exception(e)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )
Beispiel #12
0
def create_executive_report(user_id: str,
                            user_email: str,
                            incident_id: int,
                            action: dict,
                            db_session=None):
    """Creates an executive report."""
    report_template = document_service.get_executive_report_template(
        db_session=db_session)

    current_date = date.today().strftime("%B %d, %Y")

    current_status = action["submission"]["current_status"]
    overview = action["submission"]["overview"]
    next_steps = action["submission"]["next_steps"]

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

    if not report_template:
        send_feedack_to_user(incident.conversation.channel_id, user_id,
                             "No executive report template defined.")
        return

    # we fetch all previous executive reports
    executive_reports = get_all_by_incident_id_and_type(
        db_session=db_session,
        incident_id=incident_id,
        report_type=ReportTypes.executive_report)

    previous_executive_reports = []
    for executive_report in executive_reports:
        previous_executive_reports.append(
            f"{executive_report.document.name} - {executive_report.document.weblink}\n"
        )

    # we create a new executive report
    details = {
        "current_status": current_status,
        "overview": overview,
        "next_steps": next_steps
    }
    executive_report_in = ReportCreate(
        details=details,
        type=ReportTypes.executive_report,
    )
    executive_report = create(db_session=db_session,
                              report_in=executive_report_in)

    # we load the participant
    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email)

    # we save the executive report
    participant.reports.append(executive_report)
    incident.reports.append(executive_report)

    db_session.add(participant)
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Incident Participant",
        description=
        f"{participant.individual.name} created a new executive report",
        details={
            "current_status": current_status,
            "overview": overview,
            "next_steps": next_steps
        },
        incident_id=incident_id,
        individual_id=participant.individual.id,
    )

    # we create a new document for the executive report
    storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG)
    executive_report_document_name = f"{incident.name} - Executive Report - {current_date}"
    executive_report_document = storage_plugin.copy_file(
        team_drive_id=incident.storage.resource_id,
        file_id=report_template.resource_id,
        name=executive_report_document_name,
    )

    executive_report_document.update({
        "name":
        executive_report_document_name,
        "resource_type":
        INCIDENT_RESOURCE_EXECUTIVE_REPORT_DOCUMENT,
    })

    storage_plugin.move_file(new_team_drive_id=incident.storage.resource_id,
                             file_id=executive_report_document["id"])

    event_service.log(
        db_session=db_session,
        source=storage_plugin.title,
        description="Executive report document added to storage",
        incident_id=incident.id,
    )

    document_in = DocumentCreate(
        name=executive_report_document["name"],
        resource_id=executive_report_document["id"],
        resource_type=executive_report_document["resource_type"],
        weblink=executive_report_document["weblink"],
    )
    executive_report.document = document_service.create(
        db_session=db_session, document_in=document_in)

    incident.documents.append(executive_report.document)

    db_session.add(executive_report)
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Executive report document added to incident",
        incident_id=incident.id,
    )

    # we update the incident update document
    document_plugin = plugins.get(INCIDENT_PLUGIN_DOCUMENT_SLUG)
    document_plugin.update(
        executive_report_document["id"],
        name=incident.name,
        title=incident.title,
        current_date=current_date,
        current_status=current_status,
        overview=overview,
        next_steps=next_steps,
        previous_reports="\n".join(previous_executive_reports),
        commander_fullname=incident.commander.name,
        commander_weblink=incident.commander.weblink,
    )

    # we let the user know that the report has been created
    send_feedack_to_user(
        incident.conversation.channel_id,
        user_id,
        f"The executive report document has been created and can be found in the incident storage here: {executive_report_document['weblink']}",
    )

    # we send the executive report to the notifications group
    send_executive_report_to_notifications_group(incident_id, executive_report,
                                                 db_session)

    # we let the user know that the report has been sent to the notifications group
    send_feedack_to_user(
        incident.conversation.channel_id,
        user_id,
        f"The executive report has been emailed to the notifications distribution list ({incident.notifications_group.email}).",
    )

    return executive_report