UnitTestPlaybookAnalyzer

Profiles execution time of tasks in a playbook or sub-playbook (if specified). Provides minimum, maximum, and average task execution times in milliseconds as well as the state of each task: completed, error, notexecuted, started, and waiting.

python · Content Testing

Source

from datetime import datetime

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401


def BuildTask(t) -> dict:
    duration = -1.0
    state = "Unknown"
    started = 0
    notexecuted = 0

    if "state" in t:
        state = t["state"]
        if state == "Completed":
            if "startDate" in t and "completedDate" in t:
                start = datetime.fromisoformat(t["startDate"].replace("Z", "+00:00"))
                end = datetime.fromisoformat(t["completedDate"].replace("Z", "+00:00"))
                delta = end - start
                duration = int(delta.total_seconds() * 1000)
            else:
                notexecuted = 1
        elif state == "inprogress":
            started = 1
        elif state == "WillNotBeExecuted":
            notexecuted = 1

    newtask = {
        "name": t["task"]["name"],
        "duration": duration,
        "state": state,
        "tid": t["id"],
        "started": started,
        "notexecuted": notexecuted,
    }

    return newtask


def GetSubpbTasks(subplaybook, t, tasks):
    if "subPlaybook" in t:
        for _k, ts in t["subPlaybook"]["tasks"].items():
            if subplaybook == t["subPlaybook"]["name"]:
                if ts["type"] in ["regular", "condition", "playbook", "collection"]:
                    tasks.append(BuildTask(ts))
            else:
                tasks = GetSubpbTasks(subplaybook, ts, tasks)
    return tasks


def GetTasks(incid: str, subplaybook: str) -> list:
    body = {
        "states": [
            "Error",
            "Waiting",
            "Completed",
            "inprogress"
        ],
        "types": [
            "regular",
            "condition",
            "playbook,"
            "collection"
        ]
    }
    resp = execute_command("core-api-post", {
        "uri": f"/investigation/{incid}/workplan/tasks",
        "body": body
    })
    tasks: list = []

    if "response" in resp and resp["response"] is not None:
        for t in resp["response"]:
            if t["type"] in ["regular", "condition", "playbook", "collection"]:
                if t["type"] == "playbook" and subplaybook != "":
                    tasks = GetSubpbTasks(subplaybook, t, tasks)
                else:
                    tasks.append(BuildTask(t))

    return tasks


def TaskStats(task: list, taskstat: dict) -> dict:
    for t in task:
        taskid = t["tid"]
        dur = int(t["duration"])
        if taskid not in taskstat:
            taskstat[taskid] = {
                "tid": taskid,
                "name": t["name"],
                "mindur": 1000000,
                "maxdur": 0,
                "avgdur": 0,
                "totdur": 0,
                "count": 0,
                "completed": 0,
                "started": 0,
                "notexecuted": 0,
                "error": 0,
                "waiting": 0,
            }
        if t["state"] == "Completed":
            if dur > taskstat[taskid]["maxdur"]:
                taskstat[taskid]["maxdur"] = dur
            if dur < taskstat[taskid]["mindur"]:
                taskstat[taskid]["mindur"] = dur
            taskstat[taskid]["totdur"] += dur
            taskstat[taskid]["completed"] += 1
        elif t["state"] == "Error":
            taskstat[taskid]["error"] += 1
        elif t["state"] == "Waiting":
            taskstat[taskid]["waiting"] += 1
        else:
            taskstat[taskid]["started"] += t["started"]
            taskstat[taskid]["notexecuted"] += t["notexecuted"]
        taskstat[taskid]["count"] += 1

    for _key, ts in taskstat.items():
        ts["avgdur"] = int(ts["totdur"] / ts["count"])

    return taskstat


def GetTaskStats(playbookname, subplaybookname, firstday, lastday, maxinc):
    argument = {"size": maxinc,
                "query": f'playbook:"{playbookname}" occurred:>="{firstday}T00:00:00" and occurred:<="{lastday}T23:59:59"'}
    response = execute_command("getIncidents", argument)
    taskstat: dict = {}
    taskstats: dict = {}
    count = 0
    if response["data"] is not None:
        for inc in response["data"]:
            tasks = GetTasks(inc["id"], subplaybookname)
            taskstats = TaskStats(tasks, taskstat)
            count += 1
            if count >= maxinc:
                break

    return taskstats, count


def SummaryMarkdown(playbook, subplaybook: str, firstday: str, lastday: str, count: int) -> str:
    output = f"### Playbook: {playbook}\n"
    output += f"#### Sub-playbook: {subplaybook}\n"
    output += f"#### First Day: {firstday}\n"
    output += f"#### Last Day: {lastday}\n"
    output += f"#### Analysis Date: {datetime_to_string(datetime.now())}\n"
    output += f"#### Incidents Analyzed: {count}\n"
    return output


def StatsInfoMarkdown(stats: dict) -> str:
    markdown = "<br/>\n"
    markdown += "|Task Name|Minimum Duration(ms)|Average Duration(ms)|Maximum Duration(ms)|\n"
    markdown += "|---|:---:|:---:|:---:|\n"

    for _key, val in stats.items():
        if val["mindur"] is None:
            val["mindur"] = 0
        markdown += f"|{val['name']}|{val['mindur']}|{val['avgdur']}|{val['maxdur']}|\n"

    return markdown


def main():
    try:
        pb = demisto.args()["playbook"].strip()
        spb = demisto.args()["subplaybook"].strip()
        firstday = demisto.args()["firstday"].strip()
        lastday = demisto.args()["lastday"].strip()
        maxinc = (demisto.args().get("maxinc") or "").strip() or 50
        maxcount = arg_to_number(maxinc)
        taskstats, count = GetTaskStats(pb, spb, firstday, lastday, maxcount)
        demisto.setContext("PlaybookStatistics", json.dumps(taskstats))
        smarkdown = SummaryMarkdown(pb, spb, firstday, lastday, count)
        imarkdown = StatsInfoMarkdown(taskstats)
        execute_command(
            "setIncident",
            {"customFields": json.dumps({"contenttestingdependencies": smarkdown, "contenttestingpbainfo": imarkdown})},
        )
    except Exception as ex:
        demisto.error(traceback.format_exc())
        return_error(f"UnitTestPlaybookAnalyzer: Exception failed to execute. Error: {ex!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Profiles execution time of tasks in a playbook or sub-playbook (if specified). Provides minimum, maximum, and average task execution times in milliseconds as well as the state of each task: completed, error, notexecuted, started, and waiting.

Script Data


Name Description
Script Type python3

Inputs


Argument Name Description
playbook Name of the playbook to analyze.
subplaybook Sub-playbook for analysis versus the parent playbook.
firstday First day to find incident occurrences using the playbook. Example: “2023-03-01”.
lastday Last day to find incident occurrences using the playbook: Example: “2023-03-31”.
maxinc Maximum number of incidents to analyze.

Outputs


There are no outputs for this script.