1import enum 2import math 3import re 4import requests 5 6 7class RedfishHttpStatus(enum.IntEnum): 8 ok = 200 9 created = 201 10 no_content = 204 11 bad_request = 400 12 not_found = 404 13 internal_server_error = 500 14 15 16class RedfishRequest: 17 telemetry_service_path = '/redfish/v1/TelemetryService' 18 metric_definition_path = f'{telemetry_service_path}/MetricDefinitions' 19 metric_report_definition_path = \ 20 f'{telemetry_service_path}/MetricReportDefinitions' 21 metric_report_path = f'{telemetry_service_path}/MetricReports' 22 23 def __init__(self, host_addr, username, password): 24 self.host_addr = host_addr 25 self.username = username 26 self.password = password 27 28 def get(self, path, code=RedfishHttpStatus.ok): 29 u = self.host_addr + path 30 r = requests.get(u, auth=(self.username, self.password), verify=False) 31 assert r.status_code == code, \ 32 f'{r.status_code} == {code} on path {u}\n{r.text}' 33 print(r.text) 34 return r.json() 35 36 def post(self, path, body, code=RedfishHttpStatus.created): 37 u = self.host_addr + path 38 r = requests.post(u, auth=(self.username, self.password), verify=False, 39 json=body) 40 assert r.status_code == code, \ 41 f'{r.status_code} == {code} on path {u}\n{r.text}' 42 print(r.text) 43 return r.json() 44 45 def delete(self, path, code=RedfishHttpStatus.no_content): 46 u = self.host_addr + path 47 r = requests.delete(u, auth=(self.username, self.password), 48 verify=False) 49 assert r.status_code == code, \ 50 f'{r.status_code} == {code} on path {u}\n{r.text}' 51 52 53class TelemetryService: 54 def __init__(self, redfish, metric_limit): 55 r = redfish.get(redfish.telemetry_service_path) 56 self.min_interval = Duration.to_seconds(r['MinCollectionInterval']) 57 self.max_reports = r['MaxReports'] 58 self.metrics = [] 59 r = redfish.get(redfish.metric_definition_path) 60 for m in r['Members']: 61 path = m['@odata.id'] 62 metricDef = redfish.get(path) 63 self.metrics += [x for x in metricDef['MetricProperties']] 64 self.metrics = self.metrics[:metric_limit] 65 66 67class ReportDef: 68 def __init__(self, redfish): 69 self.redfish = redfish 70 71 def get_collection(self): 72 r = self.redfish.get(self.redfish.metric_report_definition_path) 73 return [x['@odata.id'] for x in r['Members']] 74 75 def add_report(self, id, metrics=None, type='OnRequest', actions=None, 76 interval=None, code=RedfishHttpStatus.created): 77 body = { 78 'Id': id, 79 'Metrics': [], 80 'MetricReportDefinitionType': type, 81 'ReportActions': ['RedfishEvent', 'LogToMetricReportsCollection'] 82 } 83 if metrics is not None: 84 body['Metrics'] = metrics 85 if actions is not None: 86 body['ReportActions'] = actions 87 if interval is not None: 88 body['Schedule'] = {'RecurrenceInterval': interval} 89 return self.redfish.post(self.redfish.metric_report_definition_path, 90 body, code) 91 92 def delete_report(self, path): 93 self.redfish.delete(f'{path}') 94 95 96class Duration: 97 def __init__(self): 98 pass 99 100 def to_iso8061(time): 101 assert time >= 0, 'Invalid argument, time is negative' 102 days = int(time / (24 * 60 * 60)) 103 time = math.fmod(time, (24 * 60 * 60)) 104 hours = int(time / (60 * 60)) 105 time = math.fmod(time, (60 * 60)) 106 minutes = int(time / 60) 107 time = round(math.fmod(time, 60), 3) 108 return f'P{str(days)}DT{str(hours)}H{str(minutes)}M{str(time)}S' 109 110 def to_seconds(duration): 111 r = re.fullmatch(r'-?P(\d+D)?(T(\d+H)?(\d+M)?(\d+(.\d+)?S)?)?', 112 duration) 113 assert r, 'Invalid argument, not match with regex' 114 days = r.group(1) 115 hours = r.group(3) 116 minutes = r.group(4) 117 seconds = r.group(5) 118 result = 0 119 if days is not None: 120 result += int(days[:-1]) * 60 * 60 * 24 121 if hours is not None: 122 result += int(hours[:-1]) * 60 * 60 123 if minutes is not None: 124 result += int(minutes[:-1]) * 60 125 if seconds is not None: 126 result += float(seconds[:-1]) 127 return result 128