Skip to content

Commit f6986a1

Browse files
author
Alan Christie
committed
feat: New org-jobs, adds pbp to coins and requirements update
1 parent 5830508 commit f6986a1

File tree

3 files changed

+117
-3
lines changed

3 files changed

+117
-3
lines changed

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
im-squonk2-client >= 1.17.2, < 2.0.0
1+
im-squonk2-client >= 3.0.2, < 4.0.0
22
python-dateutil == 2.8.2
33
rich == 12.6.0
44
pyyaml == 6.0.1

tools/coins.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections import namedtuple
66
from decimal import Decimal
77
import sys
8-
from typing import Any, Dict, Optional
8+
from typing import Any, Dict
99
import urllib3
1010

1111
from rich.pretty import pprint
@@ -56,7 +56,7 @@ def main(c_args: argparse.Namespace) -> None:
5656
remaining_days: int = p_rv.msg["product"]["coins"]["remaining_days"]
5757

5858
# Get the product's charges...
59-
pc_rv: AsApiRv = AsApi.get_product_charges(token, product_id=args.product)
59+
pc_rv: AsApiRv = AsApi.get_product_charges(token, product_id=args.product, pbp=args.pbp)
6060
if not pc_rv.success:
6161
console.log(pc_rv.msg)
6262
console.log(f"[bold red]ERROR[/bold red] Failed to get [blue]{args.product}[/blue]")
@@ -191,11 +191,20 @@ def _calculate_adjusted_coins(total_coins: Decimal,
191191
)
192192
parser.add_argument('environment', type=str, help='The environment name')
193193
parser.add_argument('product', type=str, help='The Product UUID')
194+
parser.add_argument(
195+
'--pbp',
196+
help='The prior billing period (default is 0, current)',
197+
type=int,
198+
default=0,
199+
)
194200
parser.add_argument(
195201
'--verbose',
196202
help='Set to print extra information',
197203
action='store_true',
198204
)
199205
args: argparse.Namespace = parser.parse_args()
200206

207+
if args.pbp > 0:
208+
parser.error("The prior billing period must be less than or equal to 0")
209+
201210
main(args)

tools/org-jobs.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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

Comments
 (0)