| Wludzik, Jozef | 405c1e4 | 2021-01-28 16:24:27 +0100 | [diff] [blame] | 1 | import enum | 
|  | 2 | import math | 
|  | 3 | import re | 
|  | 4 | import requests | 
|  | 5 |  | 
|  | 6 |  | 
|  | 7 | class 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 |  | 
|  | 16 | class 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 |  | 
|  | 53 | class 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 |  | 
|  | 67 | class 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 |  | 
|  | 96 | class 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 |