| import enum |
| import math |
| import re |
| import requests |
| |
| |
| class RedfishHttpStatus(enum.IntEnum): |
| ok = 200 |
| created = 201 |
| no_content = 204 |
| bad_request = 400 |
| not_found = 404 |
| internal_server_error = 500 |
| |
| |
| class RedfishRequest: |
| telemetry_service_path = '/redfish/v1/TelemetryService' |
| metric_definition_path = f'{telemetry_service_path}/MetricDefinitions' |
| metric_report_definition_path = \ |
| f'{telemetry_service_path}/MetricReportDefinitions' |
| metric_report_path = f'{telemetry_service_path}/MetricReports' |
| |
| def __init__(self, host_addr, username, password): |
| self.host_addr = host_addr |
| self.username = username |
| self.password = password |
| |
| def get(self, path, code=RedfishHttpStatus.ok): |
| u = self.host_addr + path |
| r = requests.get(u, auth=(self.username, self.password), verify=False) |
| assert r.status_code == code, \ |
| f'{r.status_code} == {code} on path {u}\n{r.text}' |
| print(r.text) |
| return r.json() |
| |
| def post(self, path, body, code=RedfishHttpStatus.created): |
| u = self.host_addr + path |
| r = requests.post(u, auth=(self.username, self.password), verify=False, |
| json=body) |
| assert r.status_code == code, \ |
| f'{r.status_code} == {code} on path {u}\n{r.text}' |
| print(r.text) |
| return r.json() |
| |
| def delete(self, path, code=RedfishHttpStatus.no_content): |
| u = self.host_addr + path |
| r = requests.delete(u, auth=(self.username, self.password), |
| verify=False) |
| assert r.status_code == code, \ |
| f'{r.status_code} == {code} on path {u}\n{r.text}' |
| |
| |
| class TelemetryService: |
| def __init__(self, redfish, metric_limit): |
| r = redfish.get(redfish.telemetry_service_path) |
| self.min_interval = Duration.to_seconds(r['MinCollectionInterval']) |
| self.max_reports = r['MaxReports'] |
| self.metrics = [] |
| r = redfish.get(redfish.metric_definition_path) |
| for m in r['Members']: |
| path = m['@odata.id'] |
| metricDef = redfish.get(path) |
| self.metrics += [x for x in metricDef['MetricProperties']] |
| self.metrics = self.metrics[:metric_limit] |
| |
| |
| class ReportDef: |
| def __init__(self, redfish): |
| self.redfish = redfish |
| |
| def get_collection(self): |
| r = self.redfish.get(self.redfish.metric_report_definition_path) |
| return [x['@odata.id'] for x in r['Members']] |
| |
| def add_report(self, id, metrics=None, type='OnRequest', actions=None, |
| interval=None, code=RedfishHttpStatus.created): |
| body = { |
| 'Id': id, |
| 'Metrics': [], |
| 'MetricReportDefinitionType': type, |
| 'ReportActions': ['RedfishEvent', 'LogToMetricReportsCollection'] |
| } |
| if metrics is not None: |
| body['Metrics'] = metrics |
| if actions is not None: |
| body['ReportActions'] = actions |
| if interval is not None: |
| body['Schedule'] = {'RecurrenceInterval': interval} |
| return self.redfish.post(self.redfish.metric_report_definition_path, |
| body, code) |
| |
| def delete_report(self, path): |
| self.redfish.delete(f'{path}') |
| |
| |
| class Duration: |
| def __init__(self): |
| pass |
| |
| def to_iso8061(time): |
| assert time >= 0, 'Invalid argument, time is negative' |
| days = int(time / (24 * 60 * 60)) |
| time = math.fmod(time, (24 * 60 * 60)) |
| hours = int(time / (60 * 60)) |
| time = math.fmod(time, (60 * 60)) |
| minutes = int(time / 60) |
| time = round(math.fmod(time, 60), 3) |
| return f'P{str(days)}DT{str(hours)}H{str(minutes)}M{str(time)}S' |
| |
| def to_seconds(duration): |
| r = re.fullmatch(r'-?P(\d+D)?(T(\d+H)?(\d+M)?(\d+(.\d+)?S)?)?', |
| duration) |
| assert r, 'Invalid argument, not match with regex' |
| days = r.group(1) |
| hours = r.group(3) |
| minutes = r.group(4) |
| seconds = r.group(5) |
| result = 0 |
| if days is not None: |
| result += int(days[:-1]) * 60 * 60 * 24 |
| if hours is not None: |
| result += int(hours[:-1]) * 60 * 60 |
| if minutes is not None: |
| result += int(minutes[:-1]) * 60 |
| if seconds is not None: |
| result += float(seconds[:-1]) |
| return result |