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 |