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