Test Failed
Push — master ( 5c8058...0496d3 )
by Tomaz
01:30
created

tools/st2-inject-trigger-instances.py (1 issue)

1
#!/usr/bin/env python
2
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
3
# contributor license agreements.  See the NOTICE file distributed with
4
# this work for additional information regarding copyright ownership.
5
# The ASF licenses this file to You under the Apache License, Version 2.0
6
# (the "License"); you may not use this file except in compliance with
7
# the License.  You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
17
"""
18
19
Tags: Load test, stress test.
20
21
A utility script that injects trigger instances into st2 system.
22
23
This tool is designed with stress/load testing in mind. Trigger
24
instances need appropriate rules to be setup so there is some
25
meaningful work.
26
27
"""
28
29
from __future__ import absolute_import
30
import os
31
import random
32
33
import eventlet
34
from oslo_config import cfg
35
import yaml
36
37
from st2common import config
38
from st2common.util.monkey_patch import monkey_patch
39
from st2common.util import date as date_utils
40
from st2common.transport.reactor import TriggerDispatcher
41
42
43
def do_register_cli_opts(opts, ignore_errors=False):
44
    for opt in opts:
45
        try:
46
            cfg.CONF.register_cli_opt(opt)
47
        except:
48
            if not ignore_errors:
49
                raise
50
51
52
def _inject_instances(trigger, rate_per_trigger, duration, payload={}):
0 ignored issues
show
Bug Best Practice introduced by
The default value {} might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
53
    start = date_utils.get_datetime_utc_now()
54
    elapsed = 0.0
55
    count = 0
56
57
    dispatcher = TriggerDispatcher()
58
    while elapsed < duration:
59
        # print('Dispatching trigger %s at time %s', trigger, date_utils.get_datetime_utc_now())
60
        dispatcher.dispatch(trigger, payload)
61
        delta = random.expovariate(rate_per_trigger)
62
        eventlet.sleep(delta)
63
        elapsed = (date_utils.get_datetime_utc_now() - start).seconds / 60.0
64
        count += 1
65
66
    print('%s: Emitted %d triggers in %d seconds' % (trigger, count, elapsed))
67
68
69
def main():
70
    monkey_patch()
71
72
    cli_opts = [
73
        cfg.IntOpt('rate', default=100,
74
                   help='Rate of trigger injection measured in instances in per sec.' +
75
                   ' Assumes a default exponential distribution in time so arrival is poisson.'),
76
        cfg.ListOpt('triggers', required=False,
77
                    help='List of triggers for which instances should be fired.' +
78
                    ' Uniform distribution will be followed if there is more than one' +
79
                    'trigger.'),
80
        cfg.StrOpt('schema_file', default=None,
81
                   help='Path to schema file defining trigger and payload.'),
82
        cfg.IntOpt('duration', default=1,
83
                   help='Duration of stress test in minutes.')
84
    ]
85
    do_register_cli_opts(cli_opts)
86
    config.parse_args()
87
88
    # Get config values
89
    triggers = cfg.CONF.triggers
90
    trigger_payload_schema = {}
91
92
    if not triggers:
93
        if (cfg.CONF.schema_file is None or cfg.CONF.schema_file == '' or
94
                not os.path.exists(cfg.CONF.schema_file)):
95
            print('Either "triggers" need to be provided or a schema file containing' +
96
                  ' triggers should be provided.')
97
            return
98
        with open(cfg.CONF.schema_file) as fd:
99
            trigger_payload_schema = yaml.safe_load(fd)
100
            triggers = list(trigger_payload_schema.keys())
101
            print('Triggers=%s' % triggers)
102
103
    rate = cfg.CONF.rate
104
    rate_per_trigger = int(rate / len(triggers))
105
    duration = cfg.CONF.duration
106
107
    dispatcher_pool = eventlet.GreenPool(len(triggers))
108
109
    for trigger in triggers:
110
        payload = trigger_payload_schema.get(trigger, {})
111
        dispatcher_pool.spawn(_inject_instances, trigger, rate_per_trigger, duration,
112
                              payload=payload)
113
        eventlet.sleep(random.uniform(0, 1))
114
    dispatcher_pool.waitall()
115
116
117
if __name__ == '__main__':
118
    main()
119