File: cdmc-plugin.rst

package info (click to toggle)
watcher 14.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,764 kB
  • sloc: python: 48,904; xml: 312; sh: 265; makefile: 75
file content (282 lines) | stat: -rw-r--r-- 9,795 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
..
      Except where otherwise noted, this document is licensed under Creative
      Commons Attribution 3.0 License.  You can view the license at:

          https://creativecommons.org/licenses/by/3.0/

.. _implement_cluster_data_model_collector_plugin:

========================================
Build a new cluster data model collector
========================================

Watcher Decision Engine has an external cluster data model (CDM) plugin
interface which gives anyone the ability to integrate an external cluster data
model collector (CDMC) in order to extend the initial set of cluster data model
collectors Watcher provides.

This section gives some guidelines on how to implement and integrate custom
cluster data model collectors within Watcher.


Creating a new plugin
=====================

In order to create a new cluster data model collector, you have to:

- Extend the :py:class:`~.base.BaseClusterDataModelCollector` class.
- Implement its :py:meth:`~.BaseClusterDataModelCollector.execute` abstract
  method to return your entire cluster data model that this method should
  build.
- Implement its :py:meth:`~.BaseClusterDataModelCollector.audit_scope_handler`
  abstract property to return your audit scope handler.
- Implement its :py:meth:`~.Goal.notification_endpoints` abstract property to
  return the list of all the :py:class:`~.base.NotificationEndpoint` instances
  that will be responsible for handling incoming notifications in order to
  incrementally update your cluster data model.

First of all, you have to extend the :class:`~.BaseClusterDataModelCollector`
base class which defines the :py:meth:`~.BaseClusterDataModelCollector.execute`
abstract method you will have to implement. This method is responsible for
building an entire cluster data model.

Here is an example showing how you can write a plugin called
``DummyClusterDataModelCollector``:

.. code-block:: python

    # Filepath = <PROJECT_DIR>/thirdparty/dummy.py
    # Import path = thirdparty.dummy

    from watcher.decision_engine.model import model_root
    from watcher.decision_engine.model.collector import base


    class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):

        def execute(self):
            model = model_root.ModelRoot()
            # Do something here...
            return model

        @property
        def audit_scope_handler(self):
            return None

        @property
        def notification_endpoints(self):
            return []

This implementation is the most basic one. So in order to get a better
understanding on how to implement a more advanced cluster data model collector,
have a look at the :py:class:`~.NovaClusterDataModelCollector` class.

Define a custom model
=====================

As you may have noticed in the above example, we are reusing an existing model
provided by Watcher. However, this model can be easily customized by
implementing a new class that would implement the :py:class:`~.Model` abstract
base class. Here below is simple example on how to proceed in implementing a
custom Model:

.. code-block:: python

    # Filepath = <PROJECT_DIR>/thirdparty/dummy.py
    # Import path = thirdparty.dummy

    from watcher.decision_engine.model import base as modelbase
    from watcher.decision_engine.model.collector import base


    class MyModel(modelbase.Model):

        def to_string(self):
            return 'MyModel'


    class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):

        def execute(self):
            model = MyModel()
            # Do something here...
            return model

        @property
        def notification_endpoints(self):
            return []

Here below is the abstract ``Model`` class that every single cluster data model
should implement:

.. autoclass:: watcher.decision_engine.model.base.Model
    :members:
    :special-members: __init__
    :noindex:

Define configuration parameters
===============================

At this point, you have a fully functional cluster data model collector.
By default, cluster data model collectors define a ``period`` option (see
:py:meth:`~.BaseClusterDataModelCollector.get_config_opts`) that corresponds
to the interval of time between each synchronization of the in-memory model.

However, in more complex implementation, you may want to define some
configuration options so one can tune the cluster data model collector to your
needs. To do so, you can implement the :py:meth:`~.Loadable.get_config_opts`
class method as followed:

.. code-block:: python

    from oslo_config import cfg
    from watcher.decision_engine.model import model_root
    from watcher.decision_engine.model.collector import base


    class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):

        def execute(self):
            model = model_root.ModelRoot()
            # Do something here...
            return model

        @property
        def audit_scope_handler(self):
            return None

        @property
        def notification_endpoints(self):
            return []

        @classmethod
        def get_config_opts(cls):
            return super(
                DummyClusterDataModelCollector, cls).get_config_opts() + [
                cfg.StrOpt('test_opt', help="Demo Option.", default=0),
                # Some more options ...
            ]

The configuration options defined within this class method will be included
within the global ``watcher.conf`` configuration file under a section named by
convention: ``{namespace}.{plugin_name}`` (see section :ref:`Register a new
entry point <register_new_cdmc_entrypoint>`). The namespace for CDMC plugins is
``watcher_cluster_data_model_collectors``, so in our case, the ``watcher.conf``
configuration would have to be modified as followed:

.. code-block:: ini

    [watcher_cluster_data_model_collectors.dummy]
    # Option used for testing.
    test_opt = test_value

Then, the configuration options you define within this method will then be
injected in each instantiated object via the  ``config`` parameter of the
:py:meth:`~.BaseClusterDataModelCollector.__init__` method.


Abstract Plugin Class
=====================

Here below is the abstract ``BaseClusterDataModelCollector`` class that every
single cluster data model collector should implement:

.. autoclass:: watcher.decision_engine.model.collector.base.BaseClusterDataModelCollector
    :members:
    :special-members: __init__
    :noindex:


.. _register_new_cdmc_entrypoint:

Register a new entry point
==========================

In order for the Watcher Decision Engine to load your new cluster data model
collector, the latter must be registered as a named entry point under the
``watcher_cluster_data_model_collectors`` entry point namespace of your
``setup.py`` file. If you are using pbr_, this entry point should be placed in
your ``setup.cfg`` file.

The name you give to your entry point has to be unique.

Here below is how to register ``DummyClusterDataModelCollector`` using pbr_:

.. code-block:: ini

    [entry_points]
    watcher_cluster_data_model_collectors =
        dummy = thirdparty.dummy:DummyClusterDataModelCollector

.. _pbr: https://docs.openstack.org/pbr/latest/


Add new notification endpoints
==============================

At this point, you have a fully functional cluster data model collector.
However, this CDMC is only refreshed periodically via a background scheduler.
As you may sometimes execute a strategy with a stale CDM due to a high activity
on your infrastructure, you can define some notification endpoints that will be
responsible for incrementally updating the CDM based on notifications emitted
by other services such as Nova. To do so, you can implement and register a new
``DummyEndpoint`` notification endpoint regarding a ``dummy`` event as shown
below:

.. code-block:: python

    from watcher.decision_engine.model import model_root
    from watcher.decision_engine.model.collector import base


    class DummyNotification(base.NotificationEndpoint):

        @property
        def filter_rule(self):
            return filtering.NotificationFilter(
                publisher_id=r'.*',
                event_type=r'^dummy$',
            )

        def info(self, ctxt, publisher_id, event_type, payload, metadata):
            # Do some CDM modifications here...
            pass


    class DummyClusterDataModelCollector(base.BaseClusterDataModelCollector):

        def execute(self):
            model = model_root.ModelRoot()
            # Do something here...
            return model

        @property
        def notification_endpoints(self):
            return [DummyNotification(self)]


Note that if the event you are trying to listen to is published by a new
service, you may have to also add a new topic Watcher will have to subscribe to
in the ``notification_topics`` option of the ``[watcher_decision_engine]``
section.


Using cluster data model collector plugins
==========================================

The Watcher Decision Engine service will automatically discover any installed
plugins when it is restarted. If a Python package containing a custom plugin is
installed within the same environment as Watcher, Watcher will automatically
make that plugin available for use.

At this point, you can use your new cluster data model plugin in your
:ref:`strategy plugin <implement_strategy_plugin>` by using the
:py:attr:`~.BaseStrategy.collector_manager` property as followed:

.. code-block:: python

    # [...]
    dummy_collector = self.collector_manager.get_cluster_model_collector(
        "dummy")  # "dummy" is the name of the entry point we declared earlier
    dummy_model = dummy_collector.get_latest_cluster_data_model()
    # Do some stuff with this model