cli: Add 'ara playbook metrics'

This provides a first implementation in order to retrieve playbook
metrics through the CLI.

Change-Id: Id04359b798b86f5e2131aa772d4871c521acfc45
This commit is contained in:
David Moreau Simard 2020-11-02 21:25:44 -05:00
parent 4b5f09df87
commit f3da150e10
3 changed files with 212 additions and 0 deletions

View File

@ -373,3 +373,184 @@ class PlaybookPrune(Command):
self.deleted += 1
self.log.info("%s playbooks deleted" % self.deleted)
class PlaybookMetrics(Lister):
""" Provides metrics about playbooks """
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(PlaybookMetrics, self).get_parser(prog_name)
parser = global_arguments(parser)
# fmt: off
parser.add_argument(
"--aggregate",
choices=["name", "path", "ansible_version", "controller"],
default="path",
help=("Aggregate playbooks by path, name, ansible version or controller. Defaults to path."),
)
# Playbook search arguments and ordering as per ara.api.filters.PlaybookFilter
parser.add_argument(
"--label",
metavar="<label>",
default=None,
help=("List playbooks matching the provided label"),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=("List playbooks matching the provided name (full or partial)"),
)
parser.add_argument(
"--path",
metavar="<path>",
default=None,
help=("List playbooks matching the provided path (full or partial)"),
)
parser.add_argument(
"--status",
metavar="<status>",
default=None,
help=("List playbooks matching a specific status ('completed', 'running', 'failed')"),
)
parser.add_argument(
"--long",
action="store_true",
default=False,
help=("Don't truncate paths and include additional fields: name, plays, files, records")
)
parser.add_argument(
"--order",
metavar="<order>",
default="-started",
help=(
"Orders playbooks by a field ('id', 'created', 'updated', 'started', 'ended', 'duration')\n"
"Defaults to '-started' descending so the most recent playbook is at the top.\n"
"The order can be reversed by omitting the '-': ara playbook list --order=started"
),
)
parser.add_argument(
"--limit",
metavar="<limit>",
default=os.environ.get("ARA_CLI_LIMIT", 1000),
help=("Returns the first <limit> determined by the ordering. Defaults to ARA_CLI_LIMIT or 1000.")
)
# fmt: on
return parser
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.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)
# TODO: This could probably be made more efficient without needing to iterate a second time
# Group playbooks by aggregate
aggregate = {}
for playbook in playbooks["results"]:
item = playbook[args.aggregate]
if item not in aggregate:
aggregate[item] = []
aggregate[item].append(playbook)
data = {}
for item, playbooks in aggregate.items():
data[item] = {
"count": len(playbooks),
"hosts": 0,
"plays": 0,
"tasks": 0,
"results": 0,
"files": 0,
"records": 0,
"expired": 0,
"failed": 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 playbook in playbooks:
for status in ["completed", "expired", "failed", "running", "unknown"]:
if playbook["status"] == status:
data[item][status] += 1
for obj in ["files", "hosts", "plays", "tasks", "records", "results"]:
data[item][obj] += playbook["items"][obj]
if playbook["duration"] is not None:
data[item]["duration_total"] = cli_utils.sum_timedelta(
playbook["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",
"duration_total",
"duration_avg",
"plays",
"tasks",
"results",
"hosts",
"files",
"records",
"completed",
"expired",
"failed",
"running",
"unknown"
)
else:
columns = (
"aggregate",
"count",
"duration_total",
"duration_avg",
"tasks",
"results",
"hosts",
"completed",
"failed",
"running"
)
return (
columns, (
[data[playbook][column] for column in columns]
for playbook in sorted(data.keys())
)
)
# fmt: on

View File

@ -85,6 +85,36 @@ ara playbook delete
.. command-output:: ara playbook delete --help
ara playbook metrics
--------------------
.. command-output:: ara playbook metrics --help
Examples:
.. code-block:: bash
# Return metrics about more than the last 1000 playbooks
ara playbook metrics --limit 10000
# Return playbook metrics in json or csv
ara playbook metrics -f json
ara playbook metrics -f csv
# Return metrics about playbooks matching a (full or partial) path
ara playbook metrics --path site.yml
# Return metrics for playbooks matching a label
ara playbook metrics --label "check:False"
# Return additional metrics without truncating paths
ara playbook metrics --long
# Aggregate metrics by playbook path (default), name, by ansible version or by controller
ara playbook metrics --aggregate name
ara playbook metrics --aggregate ansible_version
ara playbook metrics --aggregate controller
ara playbook prune
------------------

View File

@ -40,6 +40,7 @@ ara.cli =
playbook show = ara.cli.playbook:PlaybookShow
playbook delete = ara.cli.playbook:PlaybookDelete
playbook prune = ara.cli.playbook:PlaybookPrune
playbook metrics = ara.cli.playbook:PlaybookMetrics
play list = ara.cli.play:PlayList
play show = ara.cli.play:PlayShow
play delete = ara.cli.play:PlayDelete