GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#5)
by
unknown
05:28
created

_get_sensor_instance()   B

Complexity

Conditions 5

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 31
rs 8.0894
cc 5
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import os
17
import sys
18
import json
19
import atexit
20
import argparse
21
22
import eventlet
23
from oslo_config import cfg
24
25
from st2common import log as logging
26
from st2common.logging.misc import set_log_level_for_all_loggers
27
from st2common.models.api.trace import TraceContext
28
from st2common.persistence.db_init import db_setup_with_retry
29
from st2common.transport.reactor import TriggerDispatcher
30
from st2common.util import loader
31
from st2common.util.config_parser import ContentPackConfigParser
32
from st2common.services.triggerwatcher import TriggerWatcher
33
from st2reactor.sensor.base import Sensor, PollingSensor
34
from st2reactor.sensor import config
35
from st2common.services.datastore import DatastoreService
36
37
__all__ = [
38
    'SensorWrapper'
39
]
40
41
eventlet.monkey_patch(
42
    os=True,
43
    select=True,
44
    socket=True,
45
    thread=False if '--use-debugger' in sys.argv else True,
46
    time=True)
47
48
49
class SensorService(object):
50
    """
51
    Instance of this class is passed to the sensor instance and exposes "public"
52
    methods which can be called by the sensor.
53
    """
54
55
    DATASTORE_NAME_SEPARATOR = ':'
56
57
    def __init__(self, sensor_wrapper):
58
        self._sensor_wrapper = sensor_wrapper
59
        self._logger = self._sensor_wrapper._logger
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _logger was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
60
        self._dispatcher = TriggerDispatcher(self._logger)
61
        self._datastore_service = DatastoreService(logger=self._logger,
62
                                                   pack_name=self._sensor_wrapper._pack,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _pack was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
63
                                                   class_name=self._sensor_wrapper._class_name,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _class_name was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
64
                                                   api_username='sensor_service')
65
66
        self._client = None
67
68
    def get_logger(self, name):
69
        """
70
        Retrieve an instance of a logger to be used by the sensor class.
71
        """
72
        logger_name = '%s.%s' % (self._sensor_wrapper._logger.name, name)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _logger was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
73
        logger = logging.getLogger(logger_name)
74
        logger.propagate = True
75
76
        return logger
77
78
    def dispatch(self, trigger, payload=None, trace_tag=None):
79
        """
80
        Method which dispatches the trigger.
81
82
        :param trigger: Full name / reference of the trigger.
83
        :type trigger: ``str``
84
85
        :param payload: Trigger payload.
86
        :type payload: ``dict``
87
88
        :param trace_tag: Tracer to track the triggerinstance.
89
        :type trace_tags: ``str``
90
        """
91
        # empty strings
92
        trace_context = TraceContext(trace_tag=trace_tag) if trace_tag else None
93
        self.dispatch_with_context(trigger, payload=payload, trace_context=trace_context)
94
95
    def dispatch_with_context(self, trigger, payload=None, trace_context=None):
96
        """
97
        Method which dispatches the trigger.
98
99
        :param trigger: Full name / reference of the trigger.
100
        :type trigger: ``str``
101
102
        :param payload: Trigger payload.
103
        :type payload: ``dict``
104
105
        :param trace_context: Trace context to associate with Trigger.
106
        :type trace_context: ``st2common.api.models.api.trace.TraceContext``
107
        """
108
        self._dispatcher.dispatch(trigger, payload=payload, trace_context=trace_context)
109
110
    ##################################
111
    # Methods for datastore management
112
    ##################################
113
114
    def list_values(self, local=True, prefix=None):
115
        return self._datastore_service.list_values(local, prefix)
116
117
    def get_value(self, name, local=True):
118
        return self._datastore_service.get_value(name, local)
119
120
    def set_value(self, name, value, ttl=None, local=True):
121
        return self._datastore_service.set_value(name, value, ttl, local)
122
123
    def delete_value(self, name, local=True):
124
        return self._datastore_service.delete_value(name, local)
125
126
127
class SensorWrapper(object):
128
    def __init__(self, pack, file_path, class_name, trigger_types,
0 ignored issues
show
Comprehensibility Bug introduced by
trigger_types is re-defining a name which is already available in the outer-scope (previously defined on line 324).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
129
                 poll_interval=None, parent_args=None):
0 ignored issues
show
Comprehensibility Bug introduced by
parent_args is re-defining a name which is already available in the outer-scope (previously defined on line 326).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
130
        """
131
        :param pack: Name of the pack this sensor belongs to.
132
        :type pack: ``str``
133
134
        :param file_path: Path to the sensor module file.
135
        :type file_path: ``str``
136
137
        :param class_name: Sensor class name.
138
        :type class_name: ``str``
139
140
        :param trigger_types: A list of references to trigger types which
141
                                  belong to this sensor.
142
        :type trigger_types: ``list`` of ``str``
143
144
        :param poll_interval: Sensor poll interval (in seconds).
145
        :type poll_interval: ``int`` or ``None``
146
147
        :param parent_args: Command line arguments passed to the parent process.
148
        :type parse_args: ``list``
149
        """
150
        self._pack = pack
151
        self._file_path = file_path
152
        self._class_name = class_name
153
        self._trigger_types = trigger_types or []
154
        self._poll_interval = poll_interval
155
        self._parent_args = parent_args or []
156
        self._trigger_names = {}
157
158
        # 1. Parse the config with inherited parent args
159
        try:
160
            config.parse_args(args=self._parent_args)
161
        except Exception:
162
            pass
163
164
        # 2. Establish DB connection
165
        username = cfg.CONF.database.username if hasattr(cfg.CONF.database, 'username') else None
166
        password = cfg.CONF.database.password if hasattr(cfg.CONF.database, 'password') else None
167
        db_setup_with_retry(cfg.CONF.database.db_name, cfg.CONF.database.host,
168
                            cfg.CONF.database.port, username=username, password=password)
169
170
        # 3. Instantiate the watcher
171
        self._trigger_watcher = TriggerWatcher(create_handler=self._handle_create_trigger,
172
                                               update_handler=self._handle_update_trigger,
173
                                               delete_handler=self._handle_delete_trigger,
174
                                               trigger_types=self._trigger_types,
175
                                               queue_suffix='sensorwrapper_%s_%s' %
176
                                               (self._pack, self._class_name),
177
                                               exclusive=True)
178
179
        # 4. Set up logging
180
        self._logger = logging.getLogger('SensorWrapper.%s' %
181
                                         (self._class_name))
182
        logging.setup(cfg.CONF.sensorcontainer.logging)
183
184
        if '--debug' in parent_args:
185
            set_log_level_for_all_loggers()
186
187
        self._sensor_instance = self._get_sensor_instance()
188
189
    def run(self):
190
        atexit.register(self.stop)
191
192
        self._trigger_watcher.start()
193
        self._logger.info('Watcher started')
194
195
        self._logger.info('Running sensor initialization code')
196
        self._sensor_instance.setup()
197
198
        if self._poll_interval:
199
            message = ('Running sensor in active mode (poll interval=%ss)' %
200
                       (self._poll_interval))
201
        else:
202
            message = 'Running sensor in passive mode'
203
204
        self._logger.info(message)
205
206
        try:
207
            self._sensor_instance.run()
208
        except Exception as e:
209
            # Include traceback
210
            msg = ('Sensor "%s" run method raised an exception: %s.' %
211
                   (self._class_name, str(e)))
212
            self._logger.warn(msg, exc_info=True)
213
            raise Exception(msg)
214
215
    def stop(self):
216
        # Stop watcher
217
        self._logger.info('Stopping trigger watcher')
218
        self._trigger_watcher.stop()
219
220
        # Run sensor cleanup code
221
        self._logger.info('Invoking cleanup on sensor')
222
        self._sensor_instance.cleanup()
223
224
    ##############################################
225
    # Event handler methods for the trigger events
226
    ##############################################
227
228
    def _handle_create_trigger(self, trigger):
229
        self._logger.debug('Calling sensor "add_trigger" method (trigger.type=%s)' %
230
                           (trigger.type))
231
        self._trigger_names[str(trigger.id)] = trigger
232
233
        trigger = self._sanitize_trigger(trigger=trigger)
234
        self._sensor_instance.add_trigger(trigger=trigger)
235
236
    def _handle_update_trigger(self, trigger):
237
        self._logger.debug('Calling sensor "update_trigger" method (trigger.type=%s)' %
238
                           (trigger.type))
239
        self._trigger_names[str(trigger.id)] = trigger
240
241
        trigger = self._sanitize_trigger(trigger=trigger)
242
        self._sensor_instance.update_trigger(trigger=trigger)
243
244
    def _handle_delete_trigger(self, trigger):
245
        trigger_id = str(trigger.id)
246
        if trigger_id not in self._trigger_names:
247
            return
248
249
        self._logger.debug('Calling sensor "remove_trigger" method (trigger.type=%s)' %
250
                           (trigger.type))
251
        del self._trigger_names[trigger_id]
252
253
        trigger = self._sanitize_trigger(trigger=trigger)
254
        self._sensor_instance.remove_trigger(trigger=trigger)
255
256
    def _get_sensor_instance(self):
257
        """
258
        Retrieve instance of a sensor class.
259
        """
260
        _, filename = os.path.split(self._file_path)
261
        module_name, _ = os.path.splitext(filename)
262
263
        sensor_class = loader.register_plugin_class(base_class=Sensor,
264
                                                    file_path=self._file_path,
265
                                                    class_name=self._class_name)
266
267
        if not sensor_class:
268
            raise ValueError('Sensor module is missing a class with name "%s"' %
269
                             (self._class_name))
270
271
        sensor_class_kwargs = {}
272
        sensor_class_kwargs['sensor_service'] = SensorService(sensor_wrapper=self)
273
274
        sensor_config = self._get_sensor_config()
275
        sensor_class_kwargs['config'] = sensor_config
276
277
        if self._poll_interval and issubclass(sensor_class, PollingSensor):
278
            sensor_class_kwargs['poll_interval'] = self._poll_interval
279
280
        try:
281
            sensor_instance = sensor_class(**sensor_class_kwargs)
282
        except Exception:
283
            self._logger.exception('Failed to instantiate "%s" sensor class' % (self._class_name))
284
            raise Exception('Failed to instantiate "%s" sensor class' % (self._class_name))
285
286
        return sensor_instance
287
288
    def _get_sensor_config(self):
289
        config_parser = ContentPackConfigParser(pack_name=self._pack)
290
        config = config_parser.get_sensor_config(sensor_file_path=self._file_path)
0 ignored issues
show
Comprehensibility Bug introduced by
config is re-defining a name which is already available in the outer-scope (previously defined on line 34).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
291
292
        if config:
293
            self._logger.info('Using config "%s" for sensor "%s"' % (config.file_path,
294
                                                                     self._class_name))
295
            return config.config
296
        else:
297
            self._logger.info('No config found for sensor "%s"' % (self._class_name))
298
            return {}
299
300
    def _sanitize_trigger(self, trigger):
301
        sanitized = trigger._data
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _data was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
302
        if 'id' in sanitized:
303
            # Friendly objectid rather than the MongoEngine representation.
304
            sanitized['id'] = str(sanitized['id'])
305
        return sanitized
306
307
308
if __name__ == '__main__':
309
    parser = argparse.ArgumentParser(description='Sensor runner wrapper')
310
    parser.add_argument('--pack', required=True,
311
                        help='Name of the pack this sensor belongs to')
312
    parser.add_argument('--file-path', required=True,
313
                        help='Path to the sensor module')
314
    parser.add_argument('--class-name', required=True,
315
                        help='Name of the sensor class')
316
    parser.add_argument('--trigger-type-refs', required=False,
317
                        help='Comma delimited string of trigger type references')
318
    parser.add_argument('--poll-interval', type=int, default=None, required=False,
319
                        help='Sensor poll interval')
320
    parser.add_argument('--parent-args', required=False,
321
                        help='Command line arguments passed to the parent process')
322
    args = parser.parse_args()
323
324
    trigger_types = args.trigger_type_refs
325
    trigger_types = trigger_types.split(',') if trigger_types else []
326
    parent_args = json.loads(args.parent_args) if args.parent_args else []
327
    assert isinstance(parent_args, list)
328
329
    obj = SensorWrapper(pack=args.pack,
330
                        file_path=args.file_path,
331
                        class_name=args.class_name,
332
                        trigger_types=trigger_types,
333
                        poll_interval=args.poll_interval,
334
                        parent_args=parent_args)
335
    obj.run()
336