def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) if not args.confirm: self.log.info("--confirm was not specified, no playbooks will be deleted") query = {} if args.label is not None: query["label"] = args.label if args.ansible_version is not None: query["ansible_version"] = args.ansible_version if args.controller is not None: query["controller"] = args.controller if args.name is not None: query["name"] = args.name if args.path is not None: query["path"] = args.path if args.status is not None: query["status"] = args.status # generate a timestamp from n days ago in a format we can query the API with # ex: 2019-11-21T00:57:41.702229 query["started_before"] = (datetime.now() - timedelta(days=args.days)).isoformat() query["order"] = args.order query["limit"] = args.limit playbooks = client.get("/api/v1/playbooks", **query) # TODO: Improve client validation and exception handling if "count" not in playbooks: # If we didn't get an answer we can parse, it's probably due to an error 500, 403, 401, etc. # The client would have logged the error. self.log.error("Client failed to retrieve results, see logs for ara.clients.offline or ara.clients.http.") sys.exit(1) self.log.info("Found %s playbooks matching query" % playbooks["count"]) for playbook in playbooks["results"]: if not args.confirm: msg = "Dry-run: playbook {id} ({path}) would have been deleted, start date: {started}" self.log.info(msg.format(id=playbook["id"], path=playbook["path"], started=playbook["started"])) else: msg = "Deleting playbook {id} ({path}), start date: {started}" self.log.info(msg.format(id=playbook["id"], path=playbook["path"], started=playbook["started"])) client.delete("/api/v1/playbooks/%s" % playbook["id"]) self.deleted += 1 self.log.info("%s playbooks deleted" % self.deleted)
def set_options(self, task_keys=None, var_options=None, direct=None): super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) self.argument_labels = self.get_option("argument_labels") self.default_labels = self.get_option("default_labels") self.ignored_facts = self.get_option("ignored_facts") self.ignored_arguments = self.get_option("ignored_arguments") self.ignored_files = self.get_option("ignored_files") client = self.get_option("api_client") endpoint = self.get_option("api_server") timeout = self.get_option("api_timeout") username = self.get_option("api_username") password = self.get_option("api_password") insecure = self.get_option("api_insecure") self.client = client_utils.get_client( client=client, endpoint=endpoint, timeout=timeout, username=username, password=password, verify=False if insecure else True, )
def set_options(self, task_keys=None, var_options=None, direct=None): super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) self.argument_labels = self.get_option("argument_labels") self.default_labels = self.get_option("default_labels") self.ignored_facts = self.get_option("ignored_facts") self.ignored_arguments = self.get_option("ignored_arguments") self.ignored_files = self.get_option("ignored_files") client = self.get_option("api_client") endpoint = self.get_option("api_server") timeout = self.get_option("api_timeout") username = self.get_option("api_username") password = self.get_option("api_password") insecure = self.get_option("api_insecure") self.client = client_utils.get_client( client=client, endpoint=endpoint, timeout=timeout, username=username, password=password, verify=False if insecure else True, ) # TODO: Consider un-hardcoding this and plumbing pool_maxsize to requests.adapters.HTTPAdapter. # In the meantime default to 4 so we don't go above requests.adapters.DEFAULT_POOLSIZE. # Otherwise we can hit "urllib3.connectionpool: Connection pool is full" # TODO: Using >= 2 threads with the offline client can result in execution getting locked up self.thread_count = 1 if client == "offline" else 4 self.global_threads = ThreadPoolExecutor(max_workers=self.thread_count) self.log.debug("working with %s thread(s)" % self.thread_count)
def set_options(self, task_keys=None, var_options=None, direct=None): super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) self.argument_labels = self.get_option("argument_labels") self.default_labels = self.get_option("default_labels") self.ignored_facts = self.get_option("ignored_facts") self.ignored_arguments = self.get_option("ignored_arguments") self.ignored_files = self.get_option("ignored_files") client = self.get_option("api_client") endpoint = self.get_option("api_server") timeout = self.get_option("api_timeout") username = self.get_option("api_username") password = self.get_option("api_password") insecure = self.get_option("api_insecure") self.client = client_utils.get_client( client=client, endpoint=endpoint, timeout=timeout, username=username, password=password, verify=False if insecure else True, ) # TODO: Consider un-hardcoding this and plumbing pool_maxsize to requests.adapters.HTTPAdapter. # In the meantime default to 4 so we don't go above requests.adapters.DEFAULT_POOLSIZE. # Otherwise we can hit "urllib3.connectionpool: Connection pool is full" self.callback_threads = self.get_option("callback_threads") if self.callback_threads > 4: self.callback_threads = 4
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions task = client.get("/api/v1/tasks/%s" % args.task_id) if "detail" in task and task["detail"] == "Not found.": self.log.error("Task not found: %s" % args.task_id) sys.exit(1) task["report"] = "%s/playbooks/%s.html" % (args.server, task["playbook"]["id"]) columns = ( "id", "report", "name", "action", "status", "path", "lineno", "started", "ended", "duration", "tags", "handler", ) return (columns, ([task[column] for column in columns]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions play = client.get("/api/v1/plays/%s" % args.play_id) if "detail" in play and play["detail"] == "Not found.": self.log.error("Play not found: %s" % args.play_id) sys.exit(1) playbook = "(%s) %s" % (play["playbook"]["id"], play["playbook"]["name"] or play["playbook"]["path"]) play["report"] = "%s/playbooks/%s.html" % (args.server, play["playbook"]["id"]) play["playbook"] = playbook # fmt: off columns = ( "id", "report", "status", "name", "playbook", "started", "ended", "duration", "items", ) # fmt: on return (columns, ([play[column] for column in columns]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.playbook is not None: query["playbook"] = args.playbook if args.status is not None: query["status"] = args.status if args.name is not None: query["name"] = args.name if args.path is not None: query["path"] = args.path if args.action is not None: query["action"] = args.action query["order"] = args.order query["limit"] = args.limit tasks = client.get("/api/v1/tasks", **query) for task in tasks["results"]: task["results"] = task["items"]["results"] if args.resolve: playbook = cli_utils.get_playbook(client, task["playbook"]) # Paths can easily take up too much width real estate if not args.long: task["playbook"] = "(%s) %s" % (playbook["id"], cli_utils.truncatepath( playbook["path"], 50)) else: task["playbook"] = "(%s) %s" % (playbook["id"], playbook["path"]) if args.long: play = cli_utils.get_play(client, task["play"]) task["play"] = "(%s) %s" % (play["id"], play["name"]) # fmt: off if args.long: columns = ("id", "status", "results", "action", "name", "tags", "path", "lineno", "handler", "playbook", "play", "started", "duration") else: columns = ("id", "status", "results", "action", "name", "playbook", "started", "duration") # fmt: off return (columns, ([task[column] for column in columns] for task in tasks["results"]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.label is not None: query["label"] = args.label if args.controller is not None: query["controller"] = args.controller if args.name is not None: query["name"] = args.name if args.path is not None: query["path"] = args.path if args.status is not None: query["status"] = args.status query["order"] = args.order query["limit"] = args.limit playbooks = client.get("/api/v1/playbooks", **query) for playbook in playbooks["results"]: # Send items to columns playbook["plays"] = playbook["items"]["plays"] playbook["tasks"] = playbook["items"]["tasks"] playbook["results"] = playbook["items"]["results"] playbook["hosts"] = playbook["items"]["hosts"] playbook["files"] = playbook["items"]["files"] playbook["records"] = playbook["items"]["records"] # Paths can easily take up too much width real estate if not args.long: playbook["path"] = cli_utils.truncatepath(playbook["path"], 50) # fmt: off if args.long: columns = ("id", "status", "controller", "name", "path", "plays", "tasks", "results", "hosts", "files", "records", "started", "duration") else: columns = ("id", "status", "controller", "path", "tasks", "results", "hosts", "started", "duration") return (columns, ([playbook[column] for column in columns] for playbook in playbooks["results"]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.name is not None: query["name"] = args.name if args.playbook is not None: query["playbook"] = args.playbook if args.with_changed: query["changed__gt"] = 0 if args.without_changed: query["changed__lt"] = 1 if args.with_failed: query["failed__gt"] = 0 if args.without_failed: query["failed__lt"] = 1 if args.with_unreachable: query["unreachable__gt"] = 0 if args.without_unreachable: query["unreachable__lt"] = 1 query["order"] = args.order query["limit"] = args.limit hosts = client.get("/api/v1/hosts", **query) if args.resolve: for host in hosts["results"]: playbook = cli_utils.get_playbook(client, host["playbook"]) # Paths can easily take up too much width real estate if not args.long: host["playbook"] = "(%s) %s" % (playbook["id"], cli_utils.truncatepath( playbook["path"], 50)) else: host["playbook"] = "(%s) %s" % (playbook["id"], playbook["path"]) columns = ("id", "name", "playbook", "changed", "failed", "ok", "skipped", "unreachable", "updated") # fmt: off return (columns, ([host[column] for column in columns] for host in hosts["results"]))
def handle(self, *args, **options): client = options.get("client") endpoint = options.get("endpoint") username = options.get("username") password = options.get("password") insecure = options.get("insecure") timeout = options.get("timeout") days = options.get("days") confirm = options.get("confirm") # Get an instance of either an offline or http client with the specified parameters. # When using the offline client, don't run SQL migrations. api_client = get_client( client=client, endpoint=endpoint, username=username, password=password, verify=False if insecure else True, timeout=timeout, run_sql_migrations=False, ) if not confirm: logger.info("--confirm was not specified, no playbooks will be deleted") # generate a timestamp from n days ago in a format we can query the API with # ex: 2019-11-21T00:57:41.702229 limit_date = (datetime.now() - timedelta(days=days)).isoformat() logger.info("Querying %s/api/v1/playbooks/?started_before=%s" % (endpoint, limit_date)) playbooks = api_client.get("/api/v1/playbooks", started_before=limit_date) # TODO: Improve client validation and exception handling if "count" not in playbooks: # If we didn't get an answer we can parse, it's probably due to an error 500, 403, 401, etc. # The client would have logged the error. logger.error("Client failed to retrieve results, see logs for ara.clients.offline or ara.clients.http.") sys.exit(1) logger.info("Found %s playbooks matching query" % playbooks["count"]) for playbook in playbooks["results"]: if not confirm: msg = "Dry-run: playbook {id} ({path}) would have been deleted, start date: {started}" logger.info(msg.format(id=playbook["id"], path=playbook["path"], started=playbook["started"])) else: msg = "Deleting playbook {id} ({path}), start date: {started}" logger.info(msg.format(id=playbook["id"], path=playbook["path"], started=playbook["started"])) api_client.delete("/api/v1/playbooks/%s" % playbook["id"]) self.deleted += 1 logger.info("%s playbooks deleted" % self.deleted)
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions client.delete("/api/v1/tasks/%s" % args.task_id)
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.playbook is not None: query["playbook"] = args.playbook if args.name is not None: query["name"] = args.name if args.uuid is not None: query["uuid"] = args.uuid if args.status is not None: query["status"] = args.status query["order"] = args.order query["limit"] = args.limit plays = client.get("/api/v1/plays", **query) for play in plays["results"]: # Send items to columns play["tasks"] = play["items"]["tasks"] play["results"] = play["items"]["results"] if args.resolve: playbook = cli_utils.get_playbook(client, play["playbook"]) # Paths can easily take up too much width real estate if not args.long: play["playbook"] = "(%s) %s" % (playbook["id"], cli_utils.truncatepath(playbook["path"], 50)) else: play["playbook"] = "(%s) %s" % (playbook["id"], playbook["path"]) columns = ("id", "status", "name", "playbook", "tasks", "results", "started", "duration") # fmt: off return ( columns, ( [play[column] for column in columns] for play in plays["results"] ) )
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) if not args.confirm: self.log.info("--confirm was not specified, no objects will be expired") query = dict(status="running") # generate a timestamp from n days ago in a format we can query the API with # ex: 2019-11-21T00:57:41.702229 query["updated_before"] = (datetime.now() - timedelta(hours=args.hours)).isoformat() query["order"] = args.order query["limit"] = args.limit endpoints = ["/api/v1/playbooks", "/api/v1/plays", "/api/v1/tasks"] for endpoint in endpoints: objects = client.get(endpoint, **query) self.log.info("Found %s objects matching query on %s" % (objects["count"], endpoint)) # TODO: Improve client validation and exception handling if "count" not in objects: # If we didn't get an answer we can parse, it's probably due to an error 500, 403, 401, etc. # The client would have logged the error. self.log.error( "Client failed to retrieve results, see logs for ara.clients.offline or ara.clients.http." ) sys.exit(1) for obj in objects["results"]: link = "%s/%s" % (endpoint, obj["id"]) if not args.confirm: self.log.info( "Dry-run: %s would have been expired, status is running since %s" % (link, obj["updated"]) ) else: self.log.info("Expiring %s, status is running since %s" % (link, obj["updated"])) client.patch(link, status="expired") self.expired += 1 self.log.info("%s objects expired" % self.expired)
def set_options(self, task_keys=None, var_options=None, direct=None): super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) self.ignored_facts = self.get_option("ignored_facts") self.ignored_arguments = self.get_option("ignored_arguments") client = self.get_option("api_client") endpoint = self.get_option("api_server") timeout = self.get_option("api_timeout") username = self.get_option("api_username") password = self.get_option("api_password") self.client = client_utils.get_client(client=client, endpoint=endpoint, timeout=timeout, username=username, password=password)
def take_action(self, args): # TODO: Render json properly in pretty tables if args.formatter == "table": self.log.warn( "Rendering using default table formatter, use '-f yaml' or '-f json' for improved display." ) client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions record = client.get("/api/v1/records/%s" % args.record_id) if "detail" in record and record["detail"] == "Not found.": self.log.error("Record not found: %s" % args.record_id) sys.exit(1) playbook = "(%s) %s" % (record["playbook"]["id"], record["playbook"]["name"] or record["playbook"]["path"]) record["report"] = "%s/playbooks/%s.html" % (args.server, record["playbook"]["id"]) record["playbook"] = playbook # fmt: off columns = ( "id", "report", "playbook", "key", "value", "created", "updated", ) # fmt: on return (columns, ([record[column] for column in columns]))
def take_action(self, args): # TODO: Render json properly in pretty tables if args.formatter == "table": self.log.warn( "Rendering using default table formatter, use '-f yaml' or '-f json' for improved display." ) client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions playbook = client.get("/api/v1/playbooks/%s" % args.playbook_id) if "detail" in playbook and playbook["detail"] == "Not found.": self.log.error("Playbook not found: %s" % args.playbook_id) sys.exit(1) playbook["report"] = "%s/playbooks/%s.html" % (args.server, args.playbook_id) columns = ( "id", "report", "controller", "status", "path", "started", "ended", "duration", "ansible_version", "items", "labels", "arguments", ) return (columns, ([playbook[column] for column in columns]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.playbook is not None: query["playbook"] = args.playbook if args.key is not None: query["key"] = args.key query["order"] = args.order query["limit"] = args.limit records = client.get("/api/v1/records", **query) if args.resolve: for record in records["results"]: playbook = cli_utils.get_playbook(client, record["playbook"]) # Paths can easily take up too much width real estate if not args.long: record["playbook"] = "(%s) %s" % ( playbook["id"], cli_utils.truncatepath(playbook["path"], 50)) else: record["playbook"] = "(%s) %s" % (playbook["id"], playbook["path"]) columns = ("id", "key", "type", "playbook", "updated") # fmt: off return (columns, ([record[column] for column in columns] for record in records["results"]))
def take_action(self, args): # TODO: Render json properly in pretty tables if args.with_facts and args.formatter == "table": self.log.warn( "Rendering using default table formatter, use '-f yaml' or '-f json' for improved display." ) client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions host = client.get("/api/v1/hosts/%s" % args.host_id) if "detail" in host and host["detail"] == "Not found.": self.log.error("Host not found: %s" % args.host_id) sys.exit(1) host["report"] = "%s/playbooks/%s.html" % (args.server, host["playbook"]["id"]) if args.with_facts: # fmt: off columns = ("id", "report", "name", "changed", "failed", "ok", "skipped", "unreachable", "facts", "updated") # fmt: on else: # fmt: off columns = ("id", "report", "name", "changed", "failed", "ok", "skipped", "unreachable", "updated") # fmt: on return (columns, ([host[column] for column in columns]))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.playbook is not None: query["playbook"] = args.playbook if args.status is not None: query["status"] = args.status if args.name is not None: query["name"] = args.name if args.path is not None: query["path"] = args.path if args.action is not None: query["action"] = args.action query["order"] = args.order query["limit"] = args.limit tasks = client.get("/api/v1/tasks", **query) # TODO: This could probably be made more efficient without needing to iterate a second time # Group tasks by aggregate aggregate = {} for task in tasks["results"]: item = task[args.aggregate] if item not in aggregate: aggregate[item] = [] aggregate[item].append(task) data = {} for item, tasks in aggregate.items(): data[item] = { "count": len(tasks), "results": 0, "expired": 0, "running": 0, "completed": 0, "unknown": 0, "duration_total": "00:00:00.000000", } if args.aggregate == "path" and not args.long: data[item]["aggregate"] = cli_utils.truncatepath(item, 50) else: data[item]["aggregate"] = item for task in tasks: for status in ["running", "completed", "expired", "unknown"]: if task["status"] == status: data[item][status] += 1 data[item]["results"] += task["items"]["results"] if task["duration"] is not None: data[item]["duration_total"] = cli_utils.sum_timedelta( task["duration"], data[item]["duration_total"]) data[item]["duration_avg"] = cli_utils.avg_timedelta( data[item]["duration_total"], data[item]["count"]) # fmt: off if args.long: columns = ( "aggregate", "count", "results", "duration_total", "duration_avg", "completed", "running", "expired", "unknown", ) else: columns = ( "aggregate", "count", "results", "duration_total", "duration_avg", ) return (columns, ([data[action][column] for column in columns] for action in sorted(data.keys())))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.name is not None: query["name"] = args.name if args.playbook is not None: query["playbook"] = args.playbook if args.with_changed: query["changed__gt"] = 0 if args.without_changed: query["changed__lt"] = 1 if args.with_failed: query["failed__gt"] = 0 if args.without_failed: query["failed__lt"] = 1 if args.with_unreachable: query["unreachable__gt"] = 0 if args.without_unreachable: query["unreachable__lt"] = 1 query["order"] = args.order query["limit"] = args.limit resp = client.get("/api/v1/hosts", **query) # Group hosts by name hosts = {} for host in resp["results"]: name = host["name"] if name not in hosts: hosts[name] = [] hosts[name].append(host) data = {} for name, host_results in hosts.items(): data[name] = { "name": name, "count": len(host_results), "changed": 0, "failed": 0, "ok": 0, "skipped": 0, "unreachable": 0, } for host in host_results: for status in [ "changed", "failed", "ok", "skipped", "unreachable" ]: data[name][status] += host[status] columns = ("name", "count", "changed", "failed", "ok", "skipped", "unreachable") # fmt: off return (columns, ([data[host][column] for column in columns] for host in sorted(data.keys())))
def take_action(self, args): client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) query = {} if args.playbook is not None: query["playbook"] = args.playbook if args.play is not None: query["play"] = args.play if args.task is not None: query["task"] = args.task if args.host is not None: query["host"] = args.host if args.status is not None: query["status"] = args.status if args.changed: query["changed"] = args.changed query["ignore_errors"] = args.ignore_errors query["order"] = args.order query["limit"] = args.limit results = client.get("/api/v1/results", **query) if args.resolve: for result in results["results"]: playbook = cli_utils.get_playbook(client, result["playbook"]) # Paths can easily take up too much width real estate if not args.long: result["playbook"] = "(%s) %s" % ( playbook["id"], cli_utils.truncatepath(playbook["path"], 50)) else: result["playbook"] = "(%s) %s" % (playbook["id"], playbook["path"]) task = cli_utils.get_task(client, result["task"]) result["task"] = "(%s) %s" % (task["id"], task["name"]) host = cli_utils.get_host(client, result["host"]) result["host"] = "(%s) %s" % (host["id"], host["name"]) if args.long: play = cli_utils.get_play(client, result["play"]) result["play"] = "(%s) %s" % (play["id"], play["name"]) # fmt: off if args.long: columns = ( "id", "status", "changed", "ignore_errors", "playbook", "play", "task", "host", "started", "duration", ) else: columns = ( "id", "status", "playbook", "task", "host", "started", "duration", ) return (columns, ([result[column] for column in columns] for result in results["results"]))
def take_action(self, args): # TODO: Render json properly in pretty tables if args.with_content and args.formatter == "table": self.log.warn( "Rendering using default table formatter, use '-f yaml' or '-f json' for improved display." ) client = get_client( client=args.client, endpoint=args.server, timeout=args.timeout, username=args.username, password=args.password, verify=False if args.insecure else True, run_sql_migrations=False, ) # TODO: Improve client to be better at handling exceptions result = client.get("/api/v1/results/%s" % args.result_id) if "detail" in result and result["detail"] == "Not found.": self.log.error("Result not found: %s" % args.result_id) sys.exit(1) # Parse data from playbook and format it for display result["ansible_version"] = result["playbook"]["ansible_version"] playbook = "(%s) %s" % (result["playbook"]["id"], result["playbook"]["name"] or result["playbook"]["path"]) result["report"] = "%s/playbooks/%s.html" % (args.server, result["playbook"]["id"]) result["playbook"] = playbook # Parse data from play and format it for display play = "(%s) %s" % (result["play"]["id"], result["play"]["name"]) result["play"] = play # Parse data from task and format it for display task = "(%s) %s" % (result["task"]["id"], result["task"]["name"]) path = "(%s) %s:%s" % (result["task"]["file"], result["task"]["path"], result["task"]["lineno"]) result["task"] = task result["path"] = path if args.with_content: columns = ( "id", "report", "status", "playbook", "play", "task", "path", "started", "ended", "duration", "ansible_version", "content", ) else: columns = ( "id", "report", "status", "playbook", "play", "task", "path", "started", "ended", "duration", "ansible_version", ) return (columns, ([result[column] for column in columns]))