Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

st2common/st2common/services/coordination.py (1 issue)

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
from __future__ import absolute_import
17
18
import six
19
from oslo_config import cfg
20
from tooz import coordination
21
from tooz import locking
22
23
from st2common import log as logging
24
from st2common.util import system_info
25
26
27
LOG = logging.getLogger(__name__)
28
29
COORDINATOR = None
30
31
__all__ = [
32
    'configured',
33
    'get_coordinator',
34
35
    'coordinator_setup',
36
    'coordinator_teardown'
37
]
38
39
40
class NoOpLock(locking.Lock):
41
    def __init__(self, name='noop'):
42
        super(NoOpLock, self).__init__(name=name)
43
44
    def acquire(self, blocking=True):
45
        return True
46
47
    def release(self):
48
        return True
49
50
    def heartbeat(self):
51
        return True
52
53
54
class NoOpDriver(coordination.CoordinationDriver):
55
    """
56
    Tooz driver where each operation is a no-op.
57
58
    This driver is used if coordination service is not configured.
59
    """
60
61
    def __init__(self, member_id, parsed_url=None, options=None):
62
        super(NoOpDriver, self).__init__(member_id, parsed_url, options)
63
64
    def watch_join_group(self, group_id, callback):
65
        self._hooks_join_group[group_id].append(callback)
66
67
    def unwatch_join_group(self, group_id, callback):
68
        return None
69
70
    def watch_leave_group(self, group_id, callback):
71
        return None
72
73
    def unwatch_leave_group(self, group_id, callback):
74
        return None
75
76
    def watch_elected_as_leader(self, group_id, callback):
77
        return None
78
79
    def unwatch_elected_as_leader(self, group_id, callback):
80
        return None
81
82
    @staticmethod
83
    def stand_down_group_leader(group_id):
84
        return None
85
86
    @staticmethod
87
    def create_group(group_id):
88
        return None
89
90
    @staticmethod
91
    def get_groups():
92
        return None
93
94
    @staticmethod
95
    def join_group(group_id, capabilities=''):
96
        return None
97
98
    @staticmethod
99
    def leave_group(group_id):
100
        return None
101
102
    @staticmethod
103
    def delete_group(group_id):
104
        return None
105
106
    @staticmethod
107
    def get_members(group_id):
108
        return None
109
110
    @staticmethod
111
    def get_member_capabilities(group_id, member_id):
112
        return None
113
114
    @staticmethod
115
    def update_capabilities(group_id, capabilities):
116
        return None
117
118
    @staticmethod
119
    def get_leader(group_id):
120
        return None
121
122
    @staticmethod
123
    def get_lock(name):
124
        return NoOpLock(name='noop')
125
126
127
def configured():
128
    """
129
    Return True if the coordination service is properly configured.
130
131
    :rtype: ``bool``
132
    """
133
    backend_configured = cfg.CONF.coordination.url is not None
134
    mock_backend = backend_configured and (cfg.CONF.coordination.url.startswith('zake') or
135
                                           cfg.CONF.coordination.url.startswith('file'))
136
137
    return backend_configured and not mock_backend
138
139
140
def coordinator_setup():
141
    """
142
    Sets up the client for the coordination service.
143
144
    URL examples for connection:
145
        zake://
146
        file:///tmp
147
        redis://username:password@host:port
148
        mysql://username:password@host:port/dbname
149
    """
150
    url = cfg.CONF.coordination.url
151
    lock_timeout = cfg.CONF.coordination.lock_timeout
152
    proc_info = system_info.get_process_info()
153
    member_id = six.b('%s_%d' % (proc_info['hostname'], proc_info['pid']))
154
155
    if url:
156
        coordinator = coordination.get_coordinator(url, member_id, lock_timeout=lock_timeout)
157
    else:
158
        # Use a no-op backend
159
        # Note: We don't use tooz to obtain a reference since for this to work we would need to
160
        # register a plugin inside setup.py entry_point and use python setup.py develop for tests
161
        # to work
162
        coordinator = NoOpDriver(member_id)
163
164
    coordinator.start()
165
    return coordinator
166
167
168
def coordinator_teardown(coordinator):
169
    coordinator.stop()
170
171
172
def get_coordinator():
173
    global COORDINATOR
0 ignored issues
show
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
174
175
    if not configured():
176
        LOG.warn('Coordination backend is not configured. Code paths which use coordination '
177
                 'service will use best effort approach and race conditions are possible.')
178
179
    if not COORDINATOR:
180
        COORDINATOR = coordinator_setup()
181
182
    return COORDINATOR
183