|
| 1 | +#!/usr/bin/env python |
| 2 | +"""Collects processing job information for an AS Organisation. |
| 3 | +The results are presented as a on ordered list of job (collection, |
| 4 | +job and version) with the number of times the job was run and the |
| 5 | +earliest and latest dates the Job was executed. |
| 6 | +""" |
| 7 | +import argparse |
| 8 | +from dataclasses import dataclass |
| 9 | +from datetime import datetime |
| 10 | +import sys |
| 11 | +from typing import Any, Dict, List |
| 12 | +import urllib3 |
| 13 | + |
| 14 | +from rich.console import Console |
| 15 | +from squonk2.auth import Auth |
| 16 | +from squonk2.as_api import AsApi, AsApiRv |
| 17 | +from squonk2.environment import Environment |
| 18 | + |
| 19 | +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) |
| 20 | + |
| 21 | + |
| 22 | +@dataclass |
| 23 | +class JobStats: |
| 24 | + count: int |
| 25 | + earliest: datetime |
| 26 | + latest: datetime |
| 27 | + |
| 28 | + def __repr__(self) -> str: |
| 29 | + return f'{self.count} {self.earliest.date()}/{self.latest.date()}' |
| 30 | + |
| 31 | + |
| 32 | +def main(c_args: argparse.Namespace) -> None: |
| 33 | + """Main function.""" |
| 34 | + |
| 35 | + console = Console() |
| 36 | + |
| 37 | + _ = Environment.load() |
| 38 | + env: Environment = Environment(c_args.environment) |
| 39 | + AsApi.set_api_url(env.as_api) |
| 40 | + |
| 41 | + token: str = Auth.get_access_token( |
| 42 | + keycloak_url=env.keycloak_url, |
| 43 | + keycloak_realm=env.keycloak_realm, |
| 44 | + keycloak_client_id=env.keycloak_as_client_id, |
| 45 | + username=env.admin_user, |
| 46 | + password=env.admin_password, |
| 47 | + ) |
| 48 | + |
| 49 | + # A set of all the collected Jobs... |
| 50 | + org_jobs: Dict[str, JobStats] = {} |
| 51 | + |
| 52 | + # First, we start with all the Units in an Organisation |
| 53 | + u_rv: AsApiRv = AsApi.get_units(token, org_id=args.org) |
| 54 | + if not u_rv.success: |
| 55 | + console.log(u_rv.msg) |
| 56 | + console.log(f"[bold red]ERROR[/bold red] Failed to get [blue]{args.org}[/blue]") |
| 57 | + sys.exit(1) |
| 58 | + # Then we get all the Products for each Unit |
| 59 | + for unit in u_rv.msg["units"]: |
| 60 | + p_rv: AsApiRv = AsApi.get_products_for_unit(token, unit_id=unit['id']) |
| 61 | + # And then we get the charges for each Product (for each possible billing period) |
| 62 | + for product in p_rv.msg["products"]: |
| 63 | + for pbp in range(0, args.max_pbp - 1, -1): |
| 64 | + c_rv: AsApiRv = AsApi.get_product_charges(token, product_id=product['product']['id'], pbp=pbp) |
| 65 | + # iterate through the 'processing_charges' list |
| 66 | + # to print the collection, Job and Version |
| 67 | + if "processing_charges" in c_rv.msg: |
| 68 | + for processing_charge in c_rv.msg["processing_charges"]: |
| 69 | + if "additional_data" in processing_charge["charge"]: |
| 70 | + timestamp: datetime = datetime.fromisoformat(processing_charge["charge"]["timestamp"]) |
| 71 | + ad: Dict[str, Any] = processing_charge["charge"]["additional_data"] |
| 72 | + if "job_collection" in ad: |
| 73 | + job_str: str = f'{ad["job_collection"]}|{ad["job_job"]}|{ad["job_version"]}' |
| 74 | + if job_str in org_jobs: |
| 75 | + job_stats = org_jobs[job_str] |
| 76 | + job_stats.count += 1 |
| 77 | + if timestamp < job_stats.earliest: |
| 78 | + job_stats.earliest = timestamp |
| 79 | + elif timestamp > job_stats.latest: |
| 80 | + job_stats.latest = timestamp |
| 81 | + org_jobs[job_str] = job_stats |
| 82 | + else: |
| 83 | + org_jobs[job_str] = JobStats(count=1, earliest=timestamp, latest=timestamp) |
| 84 | + jobs: List[str] = org_jobs.keys() |
| 85 | + for job in sorted(jobs): |
| 86 | + print(f'{job}: ({org_jobs[job]})') |
| 87 | + |
| 88 | + |
| 89 | +if __name__ == "__main__": |
| 90 | + |
| 91 | + # Parse command line arguments |
| 92 | + parser = argparse.ArgumentParser( |
| 93 | + prog="org-jobs", |
| 94 | + description="Displays all Jobs run by an organisation" |
| 95 | + ) |
| 96 | + parser.add_argument('environment', type=str, help='The environment name') |
| 97 | + parser.add_argument('org', type=str, help='The Organisation UUID') |
| 98 | + parser.add_argument('--max-pbp', type=int, help='The maximum Prior Billing Period to search', default=-23) |
| 99 | + args: argparse.Namespace = parser.parse_args() |
| 100 | + if args.max_pbp > 0: |
| 101 | + parser.error("The maximum Prior Billing Period cannot be greater than zero") |
| 102 | + elif args.max_pbp < -23: |
| 103 | + parser.error("The earliest Prior Billing Period cannot be less than -23") |
| 104 | + |
| 105 | + main(args) |
0 commit comments