diff --git a/src/common/perf_eval_method_wrapper/Decorator.py b/src/common/perf_eval_method_wrapper/Decorator.py new file mode 100644 index 0000000000000000000000000000000000000000..2f5c9b952a487e707d8fdc037a36864dba425725 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/Decorator.py @@ -0,0 +1,72 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import threading +from enum import Enum +from typing import Dict, Tuple +from prometheus_client import Counter, Histogram +from prometheus_client.metrics import MetricWrapperBase, INF + +class MetricTypeEnum(Enum): + COUNTER_STARTED = '{:s}_counter_requests_started' + COUNTER_COMPLETED = '{:s}_counter_requests_completed' + COUNTER_FAILED = '{:s}_counter_requests_failed' + HISTOGRAM_DURATION = '{:s}_histogram_duration' + +METRIC_TO_CLASS_PARAMS = { + MetricTypeEnum.COUNTER_STARTED : (Counter, {}), + MetricTypeEnum.COUNTER_COMPLETED : (Counter, {}), + MetricTypeEnum.COUNTER_FAILED : (Counter, {}), + MetricTypeEnum.HISTOGRAM_DURATION: (Histogram, { + 'buckets': (.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF) + }) +} + +class MetricsPool: + def __init__(self) -> None: + self._metrics : Dict[str, MetricWrapperBase] = dict() + self._lock = threading.Lock() + + def get_or_create(self, function_name : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: + metric_name = str(metric_type.value).format(function_name).upper() + with self._lock: + if metric_name not in self._metrics: + metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) + metric_class, default_metric_params = metric_tuple + if len(metric_params) == 0: metric_params = default_metric_params + self._metrics[metric_name] = metric_class(metric_name, '', **metric_params) + return self._metrics[metric_name] + +def meter_method(metrics_pool : MetricsPool): + def outer_wrapper(func): + func_name = func.__name__ + histogram_duration : Histogram = metrics_pool.get_or_create(func_name, MetricTypeEnum.HISTOGRAM_DURATION) + counter_started : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_STARTED) + counter_completed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_COMPLETED) + counter_failed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_FAILED) + + @histogram_duration.time() + def inner_wrapper(self, *args, **kwargs): + counter_started.inc() + try: + reply = func(self, *args, **kwargs) + counter_completed.inc() + return reply + except KeyboardInterrupt: # pylint: disable=try-except-raise + raise + except Exception: # pylint: disable=broad-except + counter_failed.inc() + + return inner_wrapper + return outer_wrapper diff --git a/src/common/perf_eval_method_wrapper/__init__.py b/src/common/perf_eval_method_wrapper/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py new file mode 100644 index 0000000000000000000000000000000000000000..b44c830d98d3860135bde53c7d0ca5ec5d8811ec --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py @@ -0,0 +1,39 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random, time +from common.perf_eval_method_wrapper.Decorator import MetricsPool, meter_method + +EXCEPTION_RATIO = 0.05 + +METRICS_POOL = MetricsPool() + +class DummyDeviceDriver: + def __init__(self) -> None: + pass + + @meter_method(METRICS_POOL) + def get_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) + + @meter_method(METRICS_POOL) + def set_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) + + @meter_method(METRICS_POOL) + def del_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) diff --git a/src/common/perf_eval_method_wrapper/tests/__init__.py b/src/common/perf_eval_method_wrapper/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh b/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh new file mode 100644 index 0000000000000000000000000000000000000000..2a1484d5504c69f08b23d652879f4c6bace44548 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +docker create -it --name=prometheus -p 9090:9090 \ + -v /home/tfs/tfs-ctrl/test-prom-cli/prometheus.yml:/etc/prometheus/prometheus.yml \ + prom/prometheus + +docker create -it --name=grafana -p 3000:3000 \ + grafana/grafana diff --git a/src/common/perf_eval_method_wrapper/tests/prometheus.yml b/src/common/perf_eval_method_wrapper/tests/prometheus.yml new file mode 100644 index 0000000000000000000000000000000000000000..af2849209ab75eef57d41d0489bf695baa6d5fde --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/prometheus.yml @@ -0,0 +1,23 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +global: + scrape_interval: 15s # when Prometheus is pulling data from exporters etc + evaluation_interval: 30s # time between each evaluation of Prometheus' alerting rules + +scrape_configs: +- job_name: ddd # your project name + static_configs: + - targets: + - 172.17.0.1:8000 diff --git a/src/common/perf_eval_method_wrapper/tests/test_unitary.py b/src/common/perf_eval_method_wrapper/tests/test_unitary.py new file mode 100644 index 0000000000000000000000000000000000000000..e34f750164956c3f206fe35498a5b0726279e4ac --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/test_unitary.py @@ -0,0 +1,62 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import grpc, logging, time +from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def test_database_instantiation(): + SERVICE_NAME = 'Context' + METHOD_NAMES = [ + 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', + 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', + 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', + 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', + 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', + ] + METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) + + class TestServiceServicerImpl: + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetTopology(self, request, grpc_context : grpc.ServicerContext): + print('doing funny things') + time.sleep(0.1) + return 'done' + + tssi = TestServiceServicerImpl() + tssi.GetTopology(1, 2) + + for metric_name,metric in METRICS.items(): + if 'GETTOPOLOGY_' not in metric_name: continue + print(metric_name, metric._child_samples()) # pylint: disable=protected-access + + + +import random +from prometheus_client import start_http_server +from DummyDeviceDriver import DummyDeviceDriver + +def main(): + # Start up the server to expose the metrics + start_http_server(8000) + + ddd = DummyDeviceDriver() + while True: + func = random.choice([ddd.get_config, ddd.set_config, ddd.del_config]) + func() + +if __name__ == '__main__': + main()