Page MenuHomec4science

Sausage
No OneTemporary

File Metadata

Created
Sat, Nov 23, 17:02
#!/usr/bin/python3
# © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE,
# Switzerland
# SCITAS - Scientific IT and Application Support, 2021
# See the LICENSE.txt file for more details.
import configparser
import requests
import json
import argparse
import getpass
from datetime import date
from datetime import datetime
def valid_date(date):
try:
validate = datetime.strptime(date, "%Y-%m-%d")
return validate
except ValueError:
msg = "Not a valid date: '{0}', YYYY-MM-DD expected.".format(date)
raise argparse.ArgumentTypeError(msg)
class AppArgs(object):
def __init__(self):
self.response = {}
self.parser = argparse.ArgumentParser(
prog='Sausage', description='SCITAS Account Usage.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
self.add_args()
def add_args(self):
self.parser.add_argument(
'-u', '--user', help='If not provided whoami is considered')
self.parser.add_argument(
'-a', '--all', help='all users from an account are printed', action='store_true')
self.parser.add_argument(
'-A', '--account', help='Prints account consumption per cluster')
self.parser.add_argument(
'-s', '--start', help='Start date - format YYYY-MM-DD', type=valid_date)
self.parser.add_argument(
'-e', '--end', help='End date - format YYYY-MM-DD', type=valid_date)
self.parser.add_argument(
'-c', '--co2', help='Prints the co2 footprint per cluster', action='store_true')
args = self.parser.parse_args()
if args.start and args.end is None:
self.parser.error("range requires both dates (--start and --end)")
if args.end:
if args.start is None:
self.parser.error(
"range requires both dates (--start and --end)")
if args.end < args.start:
self.parser.error("start date must be earlier than end date")
if args.all and args.account is None:
self.parser.error(
"the option --all requires a valid account (--all and --account)")
if args.all and args.user:
self.parser.error(
"--all option is not compatible with --user option")
self.response = {
"user": args.user,
"account": args.account,
"all": args.all,
"start": args.start,
"end": args.end,
"co2": args.co2
}
class GetData(object):
def __init__(self, conf, args):
self.cfg_parser = configparser.ConfigParser()
self.cfg_parser.read(conf)
self.server = self.cfg_parser.get(
"server", "url") + ":" + self.cfg_parser.get("server", "port")
self.unit = self.cfg_parser.get("cluster", "calc_unit")
self.co2 = args["co2"]
self.message = []
self.vseparator = '|'
self.hseparator = '-' * 57
if args["start"]:
self.firstday = str(args["start"].date())
else:
self.firstday = str(date.today().replace(day=1))
if args["end"]:
self.lastday = str(args["end"].date())
else:
self.lastday = str(date.today())
self.request(args)
def formatheader(self, a, b, c, d):
self.message.append(f"{a:^19}" + self.vseparator + f"{b:^12}" +
self.vseparator + f"{c:^12}" + self.vseparator + f"{d:^11}")
def formatline(self, a, b, c, d):
self.message.append(f" {a:<18}" + self.vseparator + f" {b:<11}" +
self.vseparator + f"{c:>11} " + self.vseparator + f"{d:>10} ")
def formatfooter(self, a, b, c):
self.message.append(f" {a:<28}" + f"{b:>13} " + f"{c:<12}")
def printbox(self):
# Init internal variables
data = self.response.json()
element = data["name"]
carbon = 0
money = 0
walltime = 0
# begin print headers
# Default values
title = "ACCOUNT: "
head_b = "Cluster"
# By case values
if self.format == 1:
head_a = ""
elif self.format == 2:
head_a = "Account"
title = "USER: "
elif self.format == 3:
head_a = "Username"
head_c = self.unit + "-hrs"
if self.co2:
head_d = "kg.eCO²"
else:
head_d = "CHF"
# Append headers
self.message.append(self.hseparator)
self.message.append(title + element)
self.message.append("Global usage from " +
self.firstday + " to " + self.lastday)
self.message.append(self.hseparator)
self.formatheader(head_a, head_b, head_c, head_d)
self.message.append(self.hseparator)
# end print headers
# Begin print body
for key, value in sorted(data.items()):
if isinstance(value, dict):
head_a = key
# Format 1 used in cases 3 and 6
if self.format == 1:
# if money is less than 0.01 CHF, print '-'
if value['chf'] > 0.00999:
chf = "{:.2f}".format(value['chf'])
money += value['chf']
else:
chf = "-"
# if time is less than 1 second, print '-'
if value['time'] > 0.00999:
time = "{:.2f}".format(value['time'])
else:
time = "-"
# Convert co2 in ekg
if value['co2'] > 9.999:
co2 = "{:.2f}".format(value['co2'] / 1000)
else:
co2 = "-"
carbon += value['co2']
money += value['chf']
walltime += value['time']
head_c = str(time)
if self.co2:
head_d = str(co2)
else:
head_d = str(chf)
self.formatline(head_a, head_b, head_c, head_d)
# Format 2 used in cases 1, 2, 4 and 6
# Format 3 used in case 5
elif self.format == 2 or self.format == 3:
for k, v in value.items():
head_b = k
# Convert co2 in ekg
if v['co2'] > 9.999:
co2 = "{:.2f}".format(v['co2'] / 1000)
else:
co2 = "-"
# if money is less than 0.01 CHF, print '-'
if v['chf'] > 0.00999:
chf = "{:.2f}".format(v['chf'])
money += v['chf']
else:
chf = "-"
# if time is less than 1 second, print '-'
if v['time'] > 0.00999:
time = "{:.2f}".format(v['time'])
else:
time = "-"
carbon += v['co2']
walltime += v['time']
head_c = str(time)
if self.co2:
head_d = str(co2)
else:
head_d = str(chf)
self.formatline(head_a, head_b, head_c, head_d)
# End print body
# Begin print footer
carbon = "{:.2f}".format(carbon / 1000)
money = "{:.2f}".format(money)
walltime = "{:.2f}".format(walltime)
self.message.append(self.hseparator)
self.formatfooter("Total costs:", str(money), "CHF")
self.formatfooter("Total walltime:", str(walltime), self.unit + "-hrs")
self.formatfooter("Estimated carbon footprint:",
str(carbon), "kg. eCO²")
self.message.append(self.hseparator)
# End print footer
def request(self, args):
# First case : without arguments
if all(v == None for v in [args["user"], args["account"], args["start"], args["end"]]) and not args["all"]:
self.response = requests.get(
self.server + '/user/' + getpass.getuser())
self.format = 2
# Second case : only with user
elif args["user"] and all(v == None for v in [args["account"], args["start"], args["end"]]) and not args["all"]:
self.response = requests.get(self.server + '/user/' + args["user"])
self.format = 2
# Third case : only with account
elif args["account"] and all(v == None for v in [args["user"], args["start"], args["end"]]) and not args["all"]:
self.response = requests.get(
self.server + '/account/' + args["account"])
self.format = 1
# Fourth case : with user and account (without range)
elif all(v != None for v in [args["user"], args["account"]]) and all(v == None for v in [args["start"], args["end"]]) and not args["all"]:
self.response = requests.get(
self.server + '/account/' + args["account"] + '/' + args["user"])
self.format = 2
# Fifth case : with account and all
elif args["account"] and all(v == None for v in [args["user"], args["start"], args["end"]]) and args["all"]:
self.response = requests.get(
self.server + '/account/' + args["account"] + '/' + "all")
self.format = 3
# Sixth case : with range
elif all(v != None for v in [args["start"], args["end"]]) and (args["user"] or args["account"]):
if args["account"]:
if args["all"]:
self.response = requests.get(
self.server + '/range/all/' + self.firstday + '/' + self.lastday + '/' + args["account"])
self.format = 3
else:
self.response = requests.get(
self.server + '/range/account/' + self.firstday + '/' + self.lastday + '/' + args["account"])
self.format = 1
elif args["user"]:
self.response = requests.get(
self.server + '/range/user/' + self.firstday + '/' + self.lastday + '/' + args["user"])
self.format = 2
if self.response.status_code == 200:
self.printbox()
conf_file = "/etc/sausage/sausage.cfg"
options = AppArgs()
getdata = GetData(conf_file, options.response)
for item in getdata.message:
print("#" + "{0:^57}".format(item) + "#")

Event Timeline