Coverage for watcher/decision_engine/strategy/strategies/dummy_with_scorer.py: 97%
67 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
1# -*- encoding: utf-8 -*-
2# Copyright (c) 2016 Intel
3#
4# Authors: Tomasz Kaczynski <tomasz.kaczynski@intel.com>
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
20import random
22from oslo_log import log
23from oslo_serialization import jsonutils
24from oslo_utils import units
26from watcher._i18n import _
27from watcher.decision_engine.scoring import scoring_factory
28from watcher.decision_engine.strategy.strategies import base
30LOG = log.getLogger(__name__)
33class DummyWithScorer(base.DummyBaseStrategy):
34 """A dummy strategy using dummy scoring engines.
36 This is a dummy strategy demonstrating how to work with scoring
37 engines. One scoring engine is predicting the workload type of a machine
38 based on the telemetry data, the other one is simply calculating the
39 average value for given elements in a list. Results are then passed to the
40 NOP action.
42 The strategy is presenting the whole workflow:
43 - Get a reference to a scoring engine
44 - Prepare input data (features) for score calculation
45 - Perform score calculation
46 - Use scorer's metadata for results interpretation
47 """
49 DEFAULT_NAME = "dummy_with_scorer"
50 DEFAULT_DESCRIPTION = "Dummy Strategy with Scorer"
52 NOP = "nop"
53 SLEEP = "sleep"
55 def __init__(self, config, osc=None):
56 """Constructor: the signature should be identical within the subclasses
58 :param config: Configuration related to this plugin
59 :type config: :py:class:`~.Struct`
60 :param osc: An OpenStackClients instance
61 :type osc: :py:class:`~.OpenStackClients` instance
62 """
64 super(DummyWithScorer, self).__init__(config, osc)
66 # Setup Scoring Engines
67 self._workload_scorer = (scoring_factory
68 .get_scoring_engine('dummy_scorer'))
69 self._avg_scorer = (scoring_factory
70 .get_scoring_engine('dummy_avg_scorer'))
72 # Get metainfo from Workload Scorer for result interpretation
73 metainfo = jsonutils.loads(self._workload_scorer.get_metainfo())
74 self._workloads = {index: workload
75 for index, workload in enumerate(
76 metainfo['workloads'])}
78 def pre_execute(self):
79 self._pre_execute()
81 def do_execute(self, audit=None):
82 # Simple "hello world" from strategy
83 param1 = self.input_parameters.param1
84 param2 = self.input_parameters.param2
85 LOG.debug('DummyWithScorer params: param1=%(p1)f, param2=%(p2)s',
86 {'p1': param1, 'p2': param2})
87 parameters = {'message': 'Hello from Dummy Strategy with Scorer!'}
88 self.solution.add_action(action_type=self.NOP,
89 input_parameters=parameters)
91 # Demonstrate workload scorer
92 features = self._generate_random_telemetry()
93 result_str = self._workload_scorer.calculate_score(features)
94 LOG.debug('Workload Scorer result: %s', result_str)
96 # Parse the result using workloads from scorer's metainfo
97 result = self._workloads[jsonutils.loads(result_str)[0]]
98 LOG.debug('Detected Workload: %s', result)
99 parameters = {'message': 'Detected Workload: %s' % result}
100 self.solution.add_action(action_type=self.NOP,
101 input_parameters=parameters)
103 # Demonstrate AVG scorer
104 features = jsonutils.dumps(random.sample(range(1000), 20))
105 result_str = self._avg_scorer.calculate_score(features)
106 LOG.debug('AVG Scorer result: %s', result_str)
107 result = jsonutils.loads(result_str)[0]
108 LOG.debug('AVG Scorer result (parsed): %d', result)
109 parameters = {'message': 'AVG Scorer result: %s' % result}
110 self.solution.add_action(action_type=self.NOP,
111 input_parameters=parameters)
113 # Sleep action
114 self.solution.add_action(action_type=self.SLEEP,
115 input_parameters={'duration': 5.0})
117 def post_execute(self):
118 pass
120 @classmethod
121 def get_name(cls):
122 return 'dummy_with_scorer'
124 @classmethod
125 def get_display_name(cls):
126 return _('Dummy Strategy using sample Scoring Engines')
128 @classmethod
129 def get_translatable_display_name(cls):
130 return 'Dummy Strategy using sample Scoring Engines'
132 @classmethod
133 def get_schema(cls):
134 # Mandatory default setting for each element
135 return {
136 'properties': {
137 'param1': {
138 'description': 'number parameter example',
139 'type': 'number',
140 'default': 3.2,
141 'minimum': 1.0,
142 'maximum': 10.2,
143 },
144 'param2': {
145 'description': 'string parameter example',
146 'type': "string",
147 'default': "hello"
148 },
149 },
150 }
152 def _generate_random_telemetry(self):
153 processor_time = random.randint(0, 100)
154 mem_total_bytes = 4*units.Gi
155 mem_avail_bytes = random.randint(1*units.Gi, 4*units.Gi)
156 mem_page_reads = random.randint(0, 2000)
157 mem_page_writes = random.randint(0, 2000)
158 disk_read_bytes = random.randint(0*units.Mi, 200*units.Mi)
159 disk_write_bytes = random.randint(0*units.Mi, 200*units.Mi)
160 net_bytes_received = random.randint(0*units.Mi, 20*units.Mi)
161 net_bytes_sent = random.randint(0*units.Mi, 10*units.Mi)
163 return jsonutils.dumps([
164 processor_time, mem_total_bytes, mem_avail_bytes,
165 mem_page_reads, mem_page_writes, disk_read_bytes,
166 disk_write_bytes, net_bytes_received, net_bytes_sent])