Merge lp:~blamar/glance/notifier-strategy into lp:~johannes.erdfelt/glance/notifier
- notifier-strategy
- Merge into notifier
Proposed by
Brian Lamar
Status: | Merged |
---|---|
Merged at revision: | 169 |
Proposed branch: | lp:~blamar/glance/notifier-strategy |
Merge into: | lp:~johannes.erdfelt/glance/notifier |
Diff against target: |
707 lines (+256/-247) 13 files modified
Authors (+1/-0) bin/glance-api (+0/-3) bin/glance-registry (+0/-3) glance/api/v1/images.py (+8/-7) glance/common/client.py (+6/-0) glance/common/exception.py (+4/-0) glance/common/notifier.py (+115/-135) tests/functional/test_scrubber.py (+20/-4) tests/stubs.py (+0/-8) tests/unit/test_api.py (+0/-1) tests/unit/test_clients.py (+0/-1) tests/unit/test_notifier.py (+101/-85) tools/pip-requires (+1/-0) |
To merge this branch: | bzr merge lp:~blamar/glance/notifier-strategy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Johannes Erdfelt | Approve | ||
Review via email: mp+69341@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Authors' | |||
2 | --- Authors 2011-07-26 04:24:28 +0000 | |||
3 | +++ Authors 2011-07-26 18:44:34 +0000 | |||
4 | @@ -1,4 +1,5 @@ | |||
5 | 1 | Andrey Brindeyev <abrindeyev@griddynamics.com> | 1 | Andrey Brindeyev <abrindeyev@griddynamics.com> |
6 | 2 | Brian Lamar <brian.lamar@rackspace.com> | ||
7 | 2 | Brian Waldon <brian.waldon@rackspace.com> | 3 | Brian Waldon <brian.waldon@rackspace.com> |
8 | 3 | Christopher MacGown <chris@slicehost.com> | 4 | Christopher MacGown <chris@slicehost.com> |
9 | 4 | Cory Wright <corywright@gmail.com> | 5 | Cory Wright <corywright@gmail.com> |
10 | 5 | 6 | ||
11 | === modified file 'bin/glance-api' | |||
12 | --- bin/glance-api 2011-07-22 17:59:11 +0000 | |||
13 | +++ bin/glance-api 2011-07-26 18:44:34 +0000 | |||
14 | @@ -36,7 +36,6 @@ | |||
15 | 36 | 36 | ||
16 | 37 | from glance import version | 37 | from glance import version |
17 | 38 | from glance.common import config | 38 | from glance.common import config |
18 | 39 | from glance.common import notifier | ||
19 | 40 | from glance.common import wsgi | 39 | from glance.common import wsgi |
20 | 41 | 40 | ||
21 | 42 | 41 | ||
22 | @@ -60,8 +59,6 @@ | |||
23 | 60 | try: | 59 | try: |
24 | 61 | conf, app = config.load_paste_app('glance-api', options, args) | 60 | conf, app = config.load_paste_app('glance-api', options, args) |
25 | 62 | 61 | ||
26 | 63 | notifier.configure_notifier(conf) | ||
27 | 64 | |||
28 | 65 | server = wsgi.Server() | 62 | server = wsgi.Server() |
29 | 66 | server.start(app, int(conf['bind_port']), conf['bind_host']) | 63 | server.start(app, int(conf['bind_port']), conf['bind_host']) |
30 | 67 | server.wait() | 64 | server.wait() |
31 | 68 | 65 | ||
32 | === modified file 'bin/glance-registry' | |||
33 | --- bin/glance-registry 2011-07-22 17:59:11 +0000 | |||
34 | +++ bin/glance-registry 2011-07-26 18:44:34 +0000 | |||
35 | @@ -36,7 +36,6 @@ | |||
36 | 36 | 36 | ||
37 | 37 | from glance import version | 37 | from glance import version |
38 | 38 | from glance.common import config | 38 | from glance.common import config |
39 | 39 | from glance.common import notifier | ||
40 | 40 | from glance.common import wsgi | 39 | from glance.common import wsgi |
41 | 41 | 40 | ||
42 | 42 | 41 | ||
43 | @@ -60,8 +59,6 @@ | |||
44 | 60 | try: | 59 | try: |
45 | 61 | conf, app = config.load_paste_app('glance-registry', options, args) | 60 | conf, app = config.load_paste_app('glance-registry', options, args) |
46 | 62 | 61 | ||
47 | 63 | notifier.configure_notifier(conf) | ||
48 | 64 | |||
49 | 65 | server = wsgi.Server() | 62 | server = wsgi.Server() |
50 | 66 | server.start(app, int(conf['bind_port']), conf['bind_host']) | 63 | server.start(app, int(conf['bind_port']), conf['bind_host']) |
51 | 67 | server.wait() | 64 | server.wait() |
52 | 68 | 65 | ||
53 | === modified file 'glance/api/v1/images.py' | |||
54 | --- glance/api/v1/images.py 2011-07-26 04:48:33 +0000 | |||
55 | +++ glance/api/v1/images.py 2011-07-26 18:44:34 +0000 | |||
56 | @@ -73,6 +73,7 @@ | |||
57 | 73 | 73 | ||
58 | 74 | def __init__(self, options): | 74 | def __init__(self, options): |
59 | 75 | self.options = options | 75 | self.options = options |
60 | 76 | self.notifier = notifier.Notifier(options) | ||
61 | 76 | 77 | ||
62 | 77 | def index(self, req): | 78 | def index(self, req): |
63 | 78 | """ | 79 | """ |
64 | @@ -354,7 +355,7 @@ | |||
65 | 354 | image_id, | 355 | image_id, |
66 | 355 | {'checksum': checksum, | 356 | {'checksum': checksum, |
67 | 356 | 'size': size}) | 357 | 'size': size}) |
69 | 357 | notifier.notify('image.upload', 'INFO', image_meta) | 358 | self.notifier.info('image.upload', image_meta) |
70 | 358 | 359 | ||
71 | 359 | return location | 360 | return location |
72 | 360 | 361 | ||
73 | @@ -362,14 +363,14 @@ | |||
74 | 362 | msg = ("Attempt to upload duplicate image: %s") % str(e) | 363 | msg = ("Attempt to upload duplicate image: %s") % str(e) |
75 | 363 | logger.error(msg) | 364 | logger.error(msg) |
76 | 364 | self._safe_kill(req, image_id) | 365 | self._safe_kill(req, image_id) |
78 | 365 | notifier.notify('image.upload', 'ERROR', msg) | 366 | self.notifier.error('image.upload', msg) |
79 | 366 | raise HTTPConflict(msg, request=req) | 367 | raise HTTPConflict(msg, request=req) |
80 | 367 | 368 | ||
81 | 368 | except exception.NotAuthorized, e: | 369 | except exception.NotAuthorized, e: |
82 | 369 | msg = ("Unauthorized upload attempt: %s") % str(e) | 370 | msg = ("Unauthorized upload attempt: %s") % str(e) |
83 | 370 | logger.error(msg) | 371 | logger.error(msg) |
84 | 371 | self._safe_kill(req, image_id) | 372 | self._safe_kill(req, image_id) |
86 | 372 | notifier.notify('image.upload', 'ERROR', msg) | 373 | self.notifier.error('image.upload', msg) |
87 | 373 | raise HTTPForbidden(msg, request=req, | 374 | raise HTTPForbidden(msg, request=req, |
88 | 374 | content_type='text/plain') | 375 | content_type='text/plain') |
89 | 375 | 376 | ||
90 | @@ -377,7 +378,7 @@ | |||
91 | 377 | msg = ("Error uploading image: %s") % str(e) | 378 | msg = ("Error uploading image: %s") % str(e) |
92 | 378 | logger.error(msg) | 379 | logger.error(msg) |
93 | 379 | self._safe_kill(req, image_id) | 380 | self._safe_kill(req, image_id) |
95 | 380 | notifier.notify('image.upload', 'ERROR', msg) | 381 | self.notifier.error('image.upload', msg) |
96 | 381 | raise HTTPBadRequest(msg, request=req) | 382 | raise HTTPBadRequest(msg, request=req) |
97 | 382 | 383 | ||
98 | 383 | def _activate(self, req, image_id, location): | 384 | def _activate(self, req, image_id, location): |
99 | @@ -524,10 +525,10 @@ | |||
100 | 524 | % locals()) | 525 | % locals()) |
101 | 525 | for line in msg.split('\n'): | 526 | for line in msg.split('\n'): |
102 | 526 | logger.error(line) | 527 | logger.error(line) |
104 | 527 | notifier.notify('image.update', 'ERROR', msg) | 528 | self.notifier.error('image.update', msg) |
105 | 528 | raise HTTPBadRequest(msg, request=req, content_type="text/plain") | 529 | raise HTTPBadRequest(msg, request=req, content_type="text/plain") |
106 | 529 | else: | 530 | else: |
108 | 530 | notifier.notify('image.update', 'INFO', image_meta) | 531 | self.notifier.info('image.update', image_meta) |
109 | 531 | 532 | ||
110 | 532 | return {'image_meta': image_meta} | 533 | return {'image_meta': image_meta} |
111 | 533 | 534 | ||
112 | @@ -559,7 +560,7 @@ | |||
113 | 559 | schedule_delete_from_backend(image['location'], self.options, | 560 | schedule_delete_from_backend(image['location'], self.options, |
114 | 560 | req.context, id) | 561 | req.context, id) |
115 | 561 | registry.delete_image_metadata(self.options, req.context, id) | 562 | registry.delete_image_metadata(self.options, req.context, id) |
117 | 562 | notifier.notify('image.delete', 'INFO', id) | 563 | self.notifier.info('image.delete', id) |
118 | 563 | 564 | ||
119 | 564 | def get_store_or_400(self, request, store_name): | 565 | def get_store_or_400(self, request, store_name): |
120 | 565 | """ | 566 | """ |
121 | 566 | 567 | ||
122 | === modified file 'glance/common/client.py' | |||
123 | --- glance/common/client.py 2011-07-20 22:53:44 +0000 | |||
124 | +++ glance/common/client.py 2011-07-26 18:44:34 +0000 | |||
125 | @@ -56,6 +56,12 @@ | |||
126 | 56 | self.auth_tok = auth_tok | 56 | self.auth_tok = auth_tok |
127 | 57 | self.connection = None | 57 | self.connection = None |
128 | 58 | 58 | ||
129 | 59 | def set_auth_token(self, auth_tok): | ||
130 | 60 | """ | ||
131 | 61 | Updates the authentication token for this client connection. | ||
132 | 62 | """ | ||
133 | 63 | self.auth_tok = auth_tok | ||
134 | 64 | |||
135 | 59 | def get_connection_type(self): | 65 | def get_connection_type(self): |
136 | 60 | """ | 66 | """ |
137 | 61 | Returns the proper connection type | 67 | Returns the proper connection type |
138 | 62 | 68 | ||
139 | === modified file 'glance/common/exception.py' | |||
140 | --- glance/common/exception.py 2011-07-13 20:15:46 +0000 | |||
141 | +++ glance/common/exception.py 2011-07-26 18:44:34 +0000 | |||
142 | @@ -145,3 +145,7 @@ | |||
143 | 145 | 145 | ||
144 | 146 | class InvalidContentType(GlanceException): | 146 | class InvalidContentType(GlanceException): |
145 | 147 | message = "Invalid content type %(content_type)s" | 147 | message = "Invalid content type %(content_type)s" |
146 | 148 | |||
147 | 149 | |||
148 | 150 | class InvalidNotifierStrategy(GlanceException): | ||
149 | 151 | message = "'%(strategy)s' is not an available notifier strategy." | ||
150 | 148 | 152 | ||
151 | === modified file 'glance/common/notifier.py' | |||
152 | --- glance/common/notifier.py 2011-07-26 17:11:27 +0000 | |||
153 | +++ glance/common/notifier.py 2011-07-26 18:44:34 +0000 | |||
154 | @@ -20,148 +20,128 @@ | |||
155 | 20 | import socket | 20 | import socket |
156 | 21 | import uuid | 21 | import uuid |
157 | 22 | 22 | ||
159 | 23 | from kombu.connection import BrokerConnection | 23 | import kombu.connection |
160 | 24 | 24 | ||
161 | 25 | from glance.common import config | 25 | from glance.common import config |
258 | 26 | 26 | from glance.common import exception | |
259 | 27 | 27 | ||
260 | 28 | WARN = 'WARN' | 28 | |
261 | 29 | INFO = 'INFO' | 29 | class NoopStrategy(object): |
262 | 30 | ERROR = 'ERROR' | 30 | """A notifier that does nothing when called.""" |
263 | 31 | CRITICAL = 'CRITICAL' | 31 | |
264 | 32 | DEBUG = 'DEBUG' | 32 | def __init__(self, options): |
265 | 33 | 33 | pass | |
266 | 34 | log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL) | 34 | |
267 | 35 | 35 | def warn(self, msg): | |
268 | 36 | _DRIVER = None | 36 | pass |
269 | 37 | 37 | ||
270 | 38 | 38 | def info(self, msg): | |
271 | 39 | class BadPriorityException(Exception): | 39 | pass |
272 | 40 | pass | 40 | |
273 | 41 | 41 | def error(self, msg): | |
274 | 42 | 42 | pass | |
275 | 43 | def configure_notifier(options): | 43 | |
276 | 44 | global _DRIVER | 44 | |
277 | 45 | notification_driver = config.get_option(options, 'notification_driver', | 45 | class LoggingStrategy(object): |
278 | 46 | type='str', default='logging') | 46 | """A notifier that calls logging when called.""" |
279 | 47 | _DRIVER = _get_notifier_driver(notification_driver)(options) | 47 | |
280 | 48 | 48 | def __init__(self, options): | |
185 | 49 | |||
186 | 50 | def notify(event_type, priority, payload): | ||
187 | 51 | """ | ||
188 | 52 | Sends a notification using the specified driver | ||
189 | 53 | |||
190 | 54 | Notify parameters: | ||
191 | 55 | |||
192 | 56 | event_type - the literal type of event (ex. Instance Creation) | ||
193 | 57 | priority - patterned after the enumeration of Python logging levels in | ||
194 | 58 | the set (DEBUG, WARN, INFO, ERROR, CRITICAL) | ||
195 | 59 | payload - A python dictionary of attributes | ||
196 | 60 | |||
197 | 61 | Outgoing message format includes the above parameters, and appends the | ||
198 | 62 | following: | ||
199 | 63 | |||
200 | 64 | message_id - a UUID representing the id for this notification | ||
201 | 65 | timestamp - the GMT timestamp the notification was sent at | ||
202 | 66 | |||
203 | 67 | The composite message will be constructed as a dictionary of the above | ||
204 | 68 | attributes, which will then be sent via the transport mechanism defined | ||
205 | 69 | by the driver. | ||
206 | 70 | |||
207 | 71 | Message example: | ||
208 | 72 | |||
209 | 73 | {'message_id': str(uuid.uuid4()), | ||
210 | 74 | 'publisher_id': 'compute.host1', | ||
211 | 75 | 'timestamp': utils.utcnow(), | ||
212 | 76 | 'priority': 'WARN', | ||
213 | 77 | 'event_type': 'compute.create_instance', | ||
214 | 78 | 'payload': {'instance_id': 12, ... }} | ||
215 | 79 | |||
216 | 80 | """ | ||
217 | 81 | if priority not in log_levels: | ||
218 | 82 | raise BadPriorityException( | ||
219 | 83 | _('%s not in valid priorities' % priority)) | ||
220 | 84 | |||
221 | 85 | msg = dict(message_id=str(uuid.uuid4()), | ||
222 | 86 | publisher_id=socket.gethostname(), | ||
223 | 87 | event_type=event_type, | ||
224 | 88 | priority=priority, | ||
225 | 89 | payload=payload, | ||
226 | 90 | timestamp=str(datetime.datetime.utcnow())) | ||
227 | 91 | |||
228 | 92 | _DRIVER.notify(msg) | ||
229 | 93 | |||
230 | 94 | |||
231 | 95 | class Notifier(object): | ||
232 | 96 | def __init__(self, options): | ||
233 | 97 | self.level = config.get_option(options, 'default_notification_level', | ||
234 | 98 | type='str', default='INFO') | ||
235 | 99 | |||
236 | 100 | def notify(self, msg): | ||
237 | 101 | raise NotImplementedError() | ||
238 | 102 | |||
239 | 103 | |||
240 | 104 | class NoopNotifier(Notifier): | ||
241 | 105 | def notify(self, msg): | ||
242 | 106 | pass | ||
243 | 107 | |||
244 | 108 | |||
245 | 109 | class LoggingNotifier(Notifier): | ||
246 | 110 | def __init__(self, options): | ||
247 | 111 | super(LoggingNotifier, self).__init__(options) | ||
248 | 112 | self._setup_logger() | ||
249 | 113 | |||
250 | 114 | def _setup_logger(self): | ||
251 | 115 | str2log_level = { | ||
252 | 116 | 'DEBUG': logging.DEBUG, | ||
253 | 117 | 'INFO': logging.INFO, | ||
254 | 118 | 'WARN': logging.WARN, | ||
255 | 119 | 'ERROR': logging.ERROR, | ||
256 | 120 | 'CRITICAL': logging.CRITICAL} | ||
257 | 121 | self.level = str2log_level[self.level] | ||
281 | 122 | self.logger = logging.getLogger('glance.notifier.logging_notifier') | 49 | self.logger = logging.getLogger('glance.notifier.logging_notifier') |
282 | 123 | 50 | ||
288 | 124 | def notify(self, msg): | 51 | def warn(self, msg): |
289 | 125 | self.logger.log(self.level, msg) | 52 | self.logger.warn(msg) |
290 | 126 | 53 | ||
291 | 127 | 54 | def info(self, msg): | |
292 | 128 | class RabbitNotifier(Notifier): | 55 | self.logger.info(msg) |
293 | 56 | |||
294 | 57 | def error(self, msg): | ||
295 | 58 | self.logger.error(msg) | ||
296 | 59 | |||
297 | 60 | |||
298 | 61 | class RabbitStrategy(object): | ||
299 | 62 | """A notifier that puts a message on a queue when called.""" | ||
300 | 63 | |||
301 | 129 | def __init__(self, options): | 64 | def __init__(self, options): |
327 | 130 | super(RabbitNotifier, self).__init__(options) | 65 | """Initialize the rabbit notification strategy.""" |
328 | 131 | host = config.get_option(options, 'rabbit_host', | 66 | self._options = options |
329 | 132 | type='str', default='localhost') | 67 | host = self._get_option('rabbit_host', 'str', 'localhost') |
330 | 133 | port = config.get_option(options, 'rabbit_port', | 68 | port = self._get_option('rabbit_port', 'int', 5672) |
331 | 134 | type='int', default=5672) | 69 | use_ssl = self._get_option('rabbit_use_ssl', 'bool', False) |
332 | 135 | use_ssl = config.get_option(options, 'rabbit_use_ssl', | 70 | userid = self._get_option('rabbit_userid', 'str', 'guest') |
333 | 136 | type='bool', default=False) | 71 | password = self._get_option('rabbit_password', 'str', 'guest') |
334 | 137 | userid = config.get_option(options, 'rabbit_userid', | 72 | virtual_host = self._get_option('rabbit_virtual_host', 'str', '/') |
335 | 138 | type='str', default='guest') | 73 | |
336 | 139 | password = config.get_option(options, 'rabbit_password', | 74 | self.connection = kombu.connection.BrokerConnection( |
337 | 140 | type='str', default='guest') | 75 | hostname=host, |
338 | 141 | virtual_host = config.get_option(options, 'rabbit_virtual_host', | 76 | userid=userid, |
339 | 142 | type='str', default='/') | 77 | password=password, |
340 | 143 | 78 | virtual_host=virtual_host, | |
341 | 144 | self.connection = BrokerConnection( | 79 | ssl=use_ssl) |
342 | 145 | hostname=host, | 80 | |
343 | 146 | userid=userid, | 81 | self.topic = self._get_option('rabbit_notification_topic', |
344 | 147 | password=password, | 82 | 'str', |
345 | 148 | virtual_host=virtual_host, | 83 | 'glance_notifications') |
346 | 149 | ssl=use_ssl) | 84 | |
347 | 150 | self.topic = config.get_option(options, 'rabbit_notification_topic', | 85 | def _get_option(self, name, datatype, default): |
348 | 151 | type='str', default='glance_notifications') | 86 | """Retrieve a configuration option.""" |
349 | 152 | 87 | return config.get_option(self._options, | |
350 | 153 | def notify(self, message): | 88 | name, |
351 | 154 | priority = message.get('priority', self.level) | 89 | type=datatype, |
352 | 90 | default=default) | ||
353 | 91 | |||
354 | 92 | def _send_message(self, message, priority): | ||
355 | 155 | topic = "%s.%s" % (self.topic, priority) | 93 | topic = "%s.%s" % (self.topic, priority) |
356 | 156 | queue = self.connection.SimpleQueue(topic) | 94 | queue = self.connection.SimpleQueue(topic) |
357 | 157 | queue.put(message, serializer="json") | 95 | queue.put(message, serializer="json") |
358 | 158 | queue.close() | 96 | queue.close() |
359 | 159 | 97 | ||
368 | 160 | 98 | def warn(self, msg): | |
369 | 161 | def _get_notifier_driver(driver): | 99 | self._send_message(msg, "WARN") |
370 | 162 | if driver == "logging": | 100 | |
371 | 163 | return LoggingNotifier | 101 | def info(self, msg): |
372 | 164 | elif driver == "rabbit": | 102 | self._send_message(msg, "INFO") |
373 | 165 | return RabbitNotifier | 103 | |
374 | 166 | else: | 104 | def error(self, msg): |
375 | 167 | return NoopNotifier | 105 | self._send_message(msg, "ERROR") |
376 | 106 | |||
377 | 107 | |||
378 | 108 | class Notifier(object): | ||
379 | 109 | """Uses a notification strategy to send out messages about events.""" | ||
380 | 110 | |||
381 | 111 | STRATEGIES = { | ||
382 | 112 | "logging": LoggingStrategy, | ||
383 | 113 | "rabbit": RabbitStrategy, | ||
384 | 114 | "noop": NoopStrategy, | ||
385 | 115 | "default": NoopStrategy, | ||
386 | 116 | } | ||
387 | 117 | |||
388 | 118 | def __init__(self, options, strategy=None): | ||
389 | 119 | strategy = config.get_option(options, "notifier_strategy", | ||
390 | 120 | type="str", default="default") | ||
391 | 121 | try: | ||
392 | 122 | self.strategy = self.STRATEGIES[strategy](options) | ||
393 | 123 | except KeyError: | ||
394 | 124 | raise exception.InvalidNotifierStrategy(strategy=strategy) | ||
395 | 125 | |||
396 | 126 | @staticmethod | ||
397 | 127 | def generate_message(event_type, priority, payload): | ||
398 | 128 | return { | ||
399 | 129 | "message_id": str(uuid.uuid4()), | ||
400 | 130 | "publisher_id": socket.gethostname(), | ||
401 | 131 | "event_type": event_type, | ||
402 | 132 | "priority": priority, | ||
403 | 133 | "payload": payload, | ||
404 | 134 | "timestamp": str(datetime.datetime.utcnow()), | ||
405 | 135 | } | ||
406 | 136 | |||
407 | 137 | def warn(self, event_type, payload): | ||
408 | 138 | msg = self.generate_message(event_type, "WARN", payload) | ||
409 | 139 | self.strategy.warn(msg) | ||
410 | 140 | |||
411 | 141 | def info(self, event_type, payload): | ||
412 | 142 | msg = self.generate_message(event_type, "INFO", payload) | ||
413 | 143 | self.strategy.info(msg) | ||
414 | 144 | |||
415 | 145 | def error(self, event_type, payload): | ||
416 | 146 | msg = self.generate_message(event_type, "ERROR", payload) | ||
417 | 147 | self.strategy.error(msg) | ||
418 | 168 | 148 | ||
419 | === modified file 'tests/functional/test_scrubber.py' | |||
420 | --- tests/functional/test_scrubber.py 2011-07-22 23:08:43 +0000 | |||
421 | +++ tests/functional/test_scrubber.py 2011-07-26 18:44:34 +0000 | |||
422 | @@ -100,10 +100,26 @@ | |||
423 | 100 | for rec in recs: | 100 | for rec in recs: |
424 | 101 | self.assertEqual(rec['status'], 'pending_delete') | 101 | self.assertEqual(rec['status'], 'pending_delete') |
425 | 102 | 102 | ||
430 | 103 | # Wait 15 seconds for the scrubber to scrub | 103 | # NOTE(jkoelker) The build servers sometimes take longer than |
431 | 104 | time.sleep(15) | 104 | # 15 seconds to scrub. Give it up to 5 min, checking |
432 | 105 | 105 | # checking every 15 seconds. When/if it flips to | |
433 | 106 | recs = list(self.run_sql_cmd(sql)) | 106 | # deleted, bail immediatly. |
434 | 107 | deleted = set() | ||
435 | 108 | recs = [] | ||
436 | 109 | for _ in xrange(20): | ||
437 | 110 | time.sleep(15) | ||
438 | 111 | |||
439 | 112 | recs = list(self.run_sql_cmd(sql)) | ||
440 | 113 | self.assertTrue(recs) | ||
441 | 114 | |||
442 | 115 | # NOTE(jkoelker) Reset the deleted set for this loop | ||
443 | 116 | deleted = set() | ||
444 | 117 | for rec in recs: | ||
445 | 118 | deleted.add(rec['status'] == 'deleted') | ||
446 | 119 | |||
447 | 120 | if False not in deleted: | ||
448 | 121 | break | ||
449 | 122 | |||
450 | 107 | self.assertTrue(recs) | 123 | self.assertTrue(recs) |
451 | 108 | for rec in recs: | 124 | for rec in recs: |
452 | 109 | self.assertEqual(rec['status'], 'deleted') | 125 | self.assertEqual(rec['status'], 'deleted') |
453 | 110 | 126 | ||
454 | === modified file 'tests/stubs.py' | |||
455 | --- tests/stubs.py 2011-07-26 04:25:03 +0000 | |||
456 | +++ tests/stubs.py 2011-07-26 18:44:34 +0000 | |||
457 | @@ -31,7 +31,6 @@ | |||
458 | 31 | import glance.common.client | 31 | import glance.common.client |
459 | 32 | from glance.common import context | 32 | from glance.common import context |
460 | 33 | from glance.common import exception | 33 | from glance.common import exception |
461 | 34 | import glance.common.notifier | ||
462 | 35 | from glance.registry import server as rserver | 34 | from glance.registry import server as rserver |
463 | 36 | from glance.api import v1 as server | 35 | from glance.api import v1 as server |
464 | 37 | import glance.store | 36 | import glance.store |
465 | @@ -476,10 +475,3 @@ | |||
466 | 476 | fake_datastore.image_get_all_pending_delete) | 475 | fake_datastore.image_get_all_pending_delete) |
467 | 477 | stubs.Set(glance.registry.db.api, 'image_get_all', | 476 | stubs.Set(glance.registry.db.api, 'image_get_all', |
468 | 478 | fake_datastore.image_get_all) | 477 | fake_datastore.image_get_all) |
469 | 479 | |||
470 | 480 | |||
471 | 481 | def stub_out_notifier(stubs): | ||
472 | 482 | def notify(event_type, priority, payload): | ||
473 | 483 | pass | ||
474 | 484 | |||
475 | 485 | stubs.Set(glance.common.notifier, 'notify', notify) | ||
476 | 486 | 478 | ||
477 | === modified file 'tests/unit/test_api.py' | |||
478 | --- tests/unit/test_api.py 2011-07-26 04:25:03 +0000 | |||
479 | +++ tests/unit/test_api.py 2011-07-26 18:44:34 +0000 | |||
480 | @@ -1451,7 +1451,6 @@ | |||
481 | 1451 | self.stubs = stubout.StubOutForTesting() | 1451 | self.stubs = stubout.StubOutForTesting() |
482 | 1452 | stubs.stub_out_registry_and_store_server(self.stubs) | 1452 | stubs.stub_out_registry_and_store_server(self.stubs) |
483 | 1453 | stubs.stub_out_registry_db_image_api(self.stubs) | 1453 | stubs.stub_out_registry_db_image_api(self.stubs) |
484 | 1454 | stubs.stub_out_notifier(self.stubs) | ||
485 | 1455 | stubs.stub_out_filesystem_backend() | 1454 | stubs.stub_out_filesystem_backend() |
486 | 1456 | sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://") | 1455 | sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://") |
487 | 1457 | options = {'verbose': VERBOSE, | 1456 | options = {'verbose': VERBOSE, |
488 | 1458 | 1457 | ||
489 | === modified file 'tests/unit/test_clients.py' | |||
490 | --- tests/unit/test_clients.py 2011-07-26 04:25:03 +0000 | |||
491 | +++ tests/unit/test_clients.py 2011-07-26 18:44:34 +0000 | |||
492 | @@ -897,7 +897,6 @@ | |||
493 | 897 | stubs.stub_out_registry_db_image_api(self.stubs) | 897 | stubs.stub_out_registry_db_image_api(self.stubs) |
494 | 898 | stubs.stub_out_registry_and_store_server(self.stubs) | 898 | stubs.stub_out_registry_and_store_server(self.stubs) |
495 | 899 | stubs.stub_out_filesystem_backend() | 899 | stubs.stub_out_filesystem_backend() |
496 | 900 | stubs.stub_out_notifier(self.stubs) | ||
497 | 901 | self.client = client.Client("0.0.0.0", doc_root="") | 900 | self.client = client.Client("0.0.0.0", doc_root="") |
498 | 902 | 901 | ||
499 | 903 | def tearDown(self): | 902 | def tearDown(self): |
500 | 904 | 903 | ||
501 | === modified file 'tests/unit/test_notifier.py' | |||
502 | --- tests/unit/test_notifier.py 2011-07-26 13:58:04 +0000 | |||
503 | +++ tests/unit/test_notifier.py 2011-07-26 18:44:34 +0000 | |||
504 | @@ -15,93 +15,109 @@ | |||
505 | 15 | # License for the specific language governing permissions and limitations | 15 | # License for the specific language governing permissions and limitations |
506 | 16 | # under the License. | 16 | # under the License. |
507 | 17 | 17 | ||
510 | 18 | import os | 18 | import logging |
509 | 19 | import stubout | ||
511 | 20 | import unittest | 19 | import unittest |
512 | 21 | 20 | ||
513 | 22 | from glance import client | ||
514 | 23 | from glance.common import exception | 21 | from glance.common import exception |
527 | 24 | import glance.common.notifier | 22 | from glance.common import notifier |
528 | 25 | 23 | ||
529 | 26 | from tests import stubs | 24 | |
530 | 27 | 25 | class TestInvalidNotifier(unittest.TestCase): | |
519 | 28 | |||
520 | 29 | TEST_IMAGE_META = {'name': 'test_image', | ||
521 | 30 | 'is_public': False, | ||
522 | 31 | 'disk_format': 'raw', | ||
523 | 32 | 'container_format': 'ovf'} | ||
524 | 33 | |||
525 | 34 | |||
526 | 35 | class TestNotifier(unittest.TestCase): | ||
531 | 36 | """Test that notifications are generated appropriately""" | 26 | """Test that notifications are generated appropriately""" |
532 | 37 | 27 | ||
603 | 38 | def setUp(self): | 28 | def test_cannot_create(self): |
604 | 39 | """Establish a clean test environment""" | 29 | options = {"notifier_strategy": "invalid_notifier"} |
605 | 40 | self.stubs = stubout.StubOutForTesting() | 30 | self.assertRaises(exception.InvalidNotifierStrategy, |
606 | 41 | stubs.stub_out_registry_db_image_api(self.stubs) | 31 | notifier.Notifier, |
607 | 42 | stubs.stub_out_registry_and_store_server(self.stubs) | 32 | options) |
608 | 43 | stubs.stub_out_filesystem_backend() | 33 | |
609 | 44 | self.client = client.Client("0.0.0.0", doc_root="") | 34 | |
610 | 45 | self.notification = (None, None) | 35 | class TestLoggingNotifier(unittest.TestCase): |
611 | 46 | 36 | """Test the logging notifier is selected and works properly.""" | |
612 | 47 | def notify(event_type, priority, payload): | 37 | |
613 | 48 | self.notification = (event_type, priority) | 38 | def setUp(self): |
614 | 49 | 39 | options = {"notifier_strategy": "logging"} | |
615 | 50 | self.stubs.Set(glance.common.notifier, 'notify', notify) | 40 | self.called = False |
616 | 51 | 41 | self.logger = logging.getLogger("glance.notifier.logging_notifier") | |
617 | 52 | def tearDown(self): | 42 | self.notifier = notifier.Notifier(options) |
618 | 53 | """Clear the test environment""" | 43 | |
619 | 54 | stubs.clean_out_fake_filesystem_backend() | 44 | def _called(self, msg): |
620 | 55 | self.stubs.UnsetAll() | 45 | self.called = msg |
621 | 56 | 46 | ||
622 | 57 | def test_create_notify(self): | 47 | def test_warn(self): |
623 | 58 | """Test image create notification.""" | 48 | self.logger.warn = self._called |
624 | 59 | 49 | self.notifier.warn("test_event", "test_message") | |
625 | 60 | self.client.add_image(TEST_IMAGE_META, 'foobar') | 50 | if self.called is False: |
626 | 61 | 51 | self.fail("Did not call logging library correctly.") | |
627 | 62 | self.assertEquals(self.notification, ('image.upload', 'INFO')) | 52 | |
628 | 63 | 53 | def test_info(self): | |
629 | 64 | def test_update_notify(self): | 54 | self.logger.info = self._called |
630 | 65 | """Test image update notification. """ | 55 | self.notifier.info("test_event", "test_message") |
631 | 66 | 56 | if self.called is False: | |
632 | 67 | image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') | 57 | self.fail("Did not call logging library correctly.") |
633 | 68 | self.client.update_image(image_meta['id']) | 58 | |
634 | 69 | 59 | def test_erorr(self): | |
635 | 70 | self.assertEquals(self.notification, ('image.update', 'INFO')) | 60 | self.logger.error = self._called |
636 | 71 | 61 | self.notifier.error("test_event", "test_message") | |
637 | 72 | def test_delete_notify(self): | 62 | if self.called is False: |
638 | 73 | """Test image delete notification. """ | 63 | self.fail("Did not call logging library correctly.") |
639 | 74 | 64 | ||
640 | 75 | image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') | 65 | |
641 | 76 | self.client.delete_image(image_meta['id']) | 66 | class TestNoopNotifier(unittest.TestCase): |
642 | 77 | 67 | """Test that the noop notifier works...and does nothing?""" | |
643 | 78 | self.assertEquals(self.notification, ('image.delete', 'INFO')) | 68 | |
644 | 79 | 69 | def setUp(self): | |
645 | 80 | def test_create_error_notify(self): | 70 | options = {"notifier_strategy": "noop"} |
646 | 81 | """Test image create error notification.""" | 71 | self.notifier = notifier.Notifier(options) |
647 | 82 | 72 | ||
648 | 83 | def boom(options, context, image_id, image_meta, purge_props=False): | 73 | def test_warn(self): |
649 | 84 | if 'checksum' in image_meta: | 74 | self.notifier.warn("test_event", "test_message") |
650 | 85 | raise Exception('boom') | 75 | |
651 | 86 | return image_meta | 76 | def test_info(self): |
652 | 87 | 77 | self.notifier.info("test_event", "test_message") | |
653 | 88 | self.stubs.Set(glance.registry, 'update_image_metadata', boom) | 78 | |
654 | 89 | 79 | def test_error(self): | |
655 | 90 | try: | 80 | self.notifier.error("test_event", "test_message") |
656 | 91 | self.client.add_image(TEST_IMAGE_META, 'foobar') | 81 | |
657 | 92 | except exception.Invalid: | 82 | |
658 | 93 | pass | 83 | class TestRabbitNotifier(unittest.TestCase): |
659 | 94 | 84 | """Test AMQP/Rabbit notifier works.""" | |
660 | 95 | self.assertEquals(self.notification, ('image.upload', 'ERROR')) | 85 | |
661 | 96 | 86 | def setUp(self): | |
662 | 97 | def test_update_error_notify(self): | 87 | notifier.RabbitStrategy._send_message = self._send_message |
663 | 98 | """Test image update error notification.""" | 88 | self.called = False |
664 | 99 | 89 | options = {"notifier_strategy": "rabbit"} | |
665 | 100 | image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') | 90 | self.notifier = notifier.Notifier(options) |
666 | 101 | try: | 91 | |
667 | 102 | self.client.update_image(image_meta['id'], | 92 | def _send_message(self, message, priority): |
668 | 103 | {'container_format': 'invalid'}) | 93 | self.called = { |
669 | 104 | except exception.Invalid: | 94 | "message": message, |
670 | 105 | pass | 95 | "priority": priority, |
671 | 106 | 96 | } | |
672 | 107 | self.assertEquals(self.notification, ('image.update', 'ERROR')) | 97 | |
673 | 98 | def test_warn(self): | ||
674 | 99 | self.notifier.warn("test_event", "test_message") | ||
675 | 100 | |||
676 | 101 | if self.called is False: | ||
677 | 102 | self.fail("Did not call _send_message properly.") | ||
678 | 103 | |||
679 | 104 | self.assertEquals("test_message", self.called["message"]["payload"]) | ||
680 | 105 | self.assertEquals("WARN", self.called["message"]["priority"]) | ||
681 | 106 | |||
682 | 107 | def test_info(self): | ||
683 | 108 | self.notifier.info("test_event", "test_message") | ||
684 | 109 | |||
685 | 110 | if self.called is False: | ||
686 | 111 | self.fail("Did not call _send_message properly.") | ||
687 | 112 | |||
688 | 113 | self.assertEquals("test_message", self.called["message"]["payload"]) | ||
689 | 114 | self.assertEquals("INFO", self.called["message"]["priority"]) | ||
690 | 115 | |||
691 | 116 | def test_error(self): | ||
692 | 117 | self.notifier.error("test_event", "test_message") | ||
693 | 118 | |||
694 | 119 | if self.called is False: | ||
695 | 120 | self.fail("Did not call _send_message properly.") | ||
696 | 121 | |||
697 | 122 | self.assertEquals("test_message", self.called["message"]["payload"]) | ||
698 | 123 | self.assertEquals("ERROR", self.called["message"]["priority"]) | ||
699 | 108 | 124 | ||
700 | === modified file 'tools/pip-requires' | |||
701 | --- tools/pip-requires 2011-07-12 09:09:36 +0000 | |||
702 | +++ tools/pip-requires 2011-07-26 18:44:34 +0000 | |||
703 | @@ -19,3 +19,4 @@ | |||
704 | 19 | httplib2 | 19 | httplib2 |
705 | 20 | hashlib | 20 | hashlib |
706 | 21 | xattr | 21 | xattr |
707 | 22 | kombu |
I like the direction of your changes. The code was a port of the Tempo notification code, which in turn was a port of the nova notification code, but it was a mistake not to take the opportunity and consider if it could be simpler.