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 | Andrey Brindeyev <abrindeyev@griddynamics.com> |
6 | +Brian Lamar <brian.lamar@rackspace.com> |
7 | Brian Waldon <brian.waldon@rackspace.com> |
8 | Christopher MacGown <chris@slicehost.com> |
9 | Cory Wright <corywright@gmail.com> |
10 | |
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 | |
16 | from glance import version |
17 | from glance.common import config |
18 | -from glance.common import notifier |
19 | from glance.common import wsgi |
20 | |
21 | |
22 | @@ -60,8 +59,6 @@ |
23 | try: |
24 | conf, app = config.load_paste_app('glance-api', options, args) |
25 | |
26 | - notifier.configure_notifier(conf) |
27 | - |
28 | server = wsgi.Server() |
29 | server.start(app, int(conf['bind_port']), conf['bind_host']) |
30 | server.wait() |
31 | |
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 | |
37 | from glance import version |
38 | from glance.common import config |
39 | -from glance.common import notifier |
40 | from glance.common import wsgi |
41 | |
42 | |
43 | @@ -60,8 +59,6 @@ |
44 | try: |
45 | conf, app = config.load_paste_app('glance-registry', options, args) |
46 | |
47 | - notifier.configure_notifier(conf) |
48 | - |
49 | server = wsgi.Server() |
50 | server.start(app, int(conf['bind_port']), conf['bind_host']) |
51 | server.wait() |
52 | |
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 | |
58 | def __init__(self, options): |
59 | self.options = options |
60 | + self.notifier = notifier.Notifier(options) |
61 | |
62 | def index(self, req): |
63 | """ |
64 | @@ -354,7 +355,7 @@ |
65 | image_id, |
66 | {'checksum': checksum, |
67 | 'size': size}) |
68 | - notifier.notify('image.upload', 'INFO', image_meta) |
69 | + self.notifier.info('image.upload', image_meta) |
70 | |
71 | return location |
72 | |
73 | @@ -362,14 +363,14 @@ |
74 | msg = ("Attempt to upload duplicate image: %s") % str(e) |
75 | logger.error(msg) |
76 | self._safe_kill(req, image_id) |
77 | - notifier.notify('image.upload', 'ERROR', msg) |
78 | + self.notifier.error('image.upload', msg) |
79 | raise HTTPConflict(msg, request=req) |
80 | |
81 | except exception.NotAuthorized, e: |
82 | msg = ("Unauthorized upload attempt: %s") % str(e) |
83 | logger.error(msg) |
84 | self._safe_kill(req, image_id) |
85 | - notifier.notify('image.upload', 'ERROR', msg) |
86 | + self.notifier.error('image.upload', msg) |
87 | raise HTTPForbidden(msg, request=req, |
88 | content_type='text/plain') |
89 | |
90 | @@ -377,7 +378,7 @@ |
91 | msg = ("Error uploading image: %s") % str(e) |
92 | logger.error(msg) |
93 | self._safe_kill(req, image_id) |
94 | - notifier.notify('image.upload', 'ERROR', msg) |
95 | + self.notifier.error('image.upload', msg) |
96 | raise HTTPBadRequest(msg, request=req) |
97 | |
98 | def _activate(self, req, image_id, location): |
99 | @@ -524,10 +525,10 @@ |
100 | % locals()) |
101 | for line in msg.split('\n'): |
102 | logger.error(line) |
103 | - notifier.notify('image.update', 'ERROR', msg) |
104 | + self.notifier.error('image.update', msg) |
105 | raise HTTPBadRequest(msg, request=req, content_type="text/plain") |
106 | else: |
107 | - notifier.notify('image.update', 'INFO', image_meta) |
108 | + self.notifier.info('image.update', image_meta) |
109 | |
110 | return {'image_meta': image_meta} |
111 | |
112 | @@ -559,7 +560,7 @@ |
113 | schedule_delete_from_backend(image['location'], self.options, |
114 | req.context, id) |
115 | registry.delete_image_metadata(self.options, req.context, id) |
116 | - notifier.notify('image.delete', 'INFO', id) |
117 | + self.notifier.info('image.delete', id) |
118 | |
119 | def get_store_or_400(self, request, store_name): |
120 | """ |
121 | |
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 | self.auth_tok = auth_tok |
127 | self.connection = None |
128 | |
129 | + def set_auth_token(self, auth_tok): |
130 | + """ |
131 | + Updates the authentication token for this client connection. |
132 | + """ |
133 | + self.auth_tok = auth_tok |
134 | + |
135 | def get_connection_type(self): |
136 | """ |
137 | Returns the proper connection type |
138 | |
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 | |
144 | class InvalidContentType(GlanceException): |
145 | message = "Invalid content type %(content_type)s" |
146 | + |
147 | + |
148 | +class InvalidNotifierStrategy(GlanceException): |
149 | + message = "'%(strategy)s' is not an available notifier strategy." |
150 | |
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 | import socket |
156 | import uuid |
157 | |
158 | -from kombu.connection import BrokerConnection |
159 | +import kombu.connection |
160 | |
161 | from glance.common import config |
162 | - |
163 | - |
164 | -WARN = 'WARN' |
165 | -INFO = 'INFO' |
166 | -ERROR = 'ERROR' |
167 | -CRITICAL = 'CRITICAL' |
168 | -DEBUG = 'DEBUG' |
169 | - |
170 | -log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL) |
171 | - |
172 | -_DRIVER = None |
173 | - |
174 | - |
175 | -class BadPriorityException(Exception): |
176 | - pass |
177 | - |
178 | - |
179 | -def configure_notifier(options): |
180 | - global _DRIVER |
181 | - notification_driver = config.get_option(options, 'notification_driver', |
182 | - type='str', default='logging') |
183 | - _DRIVER = _get_notifier_driver(notification_driver)(options) |
184 | - |
185 | - |
186 | -def notify(event_type, priority, payload): |
187 | - """ |
188 | - Sends a notification using the specified driver |
189 | - |
190 | - Notify parameters: |
191 | - |
192 | - event_type - the literal type of event (ex. Instance Creation) |
193 | - priority - patterned after the enumeration of Python logging levels in |
194 | - the set (DEBUG, WARN, INFO, ERROR, CRITICAL) |
195 | - payload - A python dictionary of attributes |
196 | - |
197 | - Outgoing message format includes the above parameters, and appends the |
198 | - following: |
199 | - |
200 | - message_id - a UUID representing the id for this notification |
201 | - timestamp - the GMT timestamp the notification was sent at |
202 | - |
203 | - The composite message will be constructed as a dictionary of the above |
204 | - attributes, which will then be sent via the transport mechanism defined |
205 | - by the driver. |
206 | - |
207 | - Message example: |
208 | - |
209 | - {'message_id': str(uuid.uuid4()), |
210 | - 'publisher_id': 'compute.host1', |
211 | - 'timestamp': utils.utcnow(), |
212 | - 'priority': 'WARN', |
213 | - 'event_type': 'compute.create_instance', |
214 | - 'payload': {'instance_id': 12, ... }} |
215 | - |
216 | - """ |
217 | - if priority not in log_levels: |
218 | - raise BadPriorityException( |
219 | - _('%s not in valid priorities' % priority)) |
220 | - |
221 | - msg = dict(message_id=str(uuid.uuid4()), |
222 | - publisher_id=socket.gethostname(), |
223 | - event_type=event_type, |
224 | - priority=priority, |
225 | - payload=payload, |
226 | - timestamp=str(datetime.datetime.utcnow())) |
227 | - |
228 | - _DRIVER.notify(msg) |
229 | - |
230 | - |
231 | -class Notifier(object): |
232 | - def __init__(self, options): |
233 | - self.level = config.get_option(options, 'default_notification_level', |
234 | - type='str', default='INFO') |
235 | - |
236 | - def notify(self, msg): |
237 | - raise NotImplementedError() |
238 | - |
239 | - |
240 | -class NoopNotifier(Notifier): |
241 | - def notify(self, msg): |
242 | - pass |
243 | - |
244 | - |
245 | -class LoggingNotifier(Notifier): |
246 | - def __init__(self, options): |
247 | - super(LoggingNotifier, self).__init__(options) |
248 | - self._setup_logger() |
249 | - |
250 | - def _setup_logger(self): |
251 | - str2log_level = { |
252 | - 'DEBUG': logging.DEBUG, |
253 | - 'INFO': logging.INFO, |
254 | - 'WARN': logging.WARN, |
255 | - 'ERROR': logging.ERROR, |
256 | - 'CRITICAL': logging.CRITICAL} |
257 | - self.level = str2log_level[self.level] |
258 | +from glance.common import exception |
259 | + |
260 | + |
261 | +class NoopStrategy(object): |
262 | + """A notifier that does nothing when called.""" |
263 | + |
264 | + def __init__(self, options): |
265 | + pass |
266 | + |
267 | + def warn(self, msg): |
268 | + pass |
269 | + |
270 | + def info(self, msg): |
271 | + pass |
272 | + |
273 | + def error(self, msg): |
274 | + pass |
275 | + |
276 | + |
277 | +class LoggingStrategy(object): |
278 | + """A notifier that calls logging when called.""" |
279 | + |
280 | + def __init__(self, options): |
281 | self.logger = logging.getLogger('glance.notifier.logging_notifier') |
282 | |
283 | - def notify(self, msg): |
284 | - self.logger.log(self.level, msg) |
285 | - |
286 | - |
287 | -class RabbitNotifier(Notifier): |
288 | + def warn(self, msg): |
289 | + self.logger.warn(msg) |
290 | + |
291 | + def info(self, msg): |
292 | + self.logger.info(msg) |
293 | + |
294 | + def error(self, msg): |
295 | + self.logger.error(msg) |
296 | + |
297 | + |
298 | +class RabbitStrategy(object): |
299 | + """A notifier that puts a message on a queue when called.""" |
300 | + |
301 | def __init__(self, options): |
302 | - super(RabbitNotifier, self).__init__(options) |
303 | - host = config.get_option(options, 'rabbit_host', |
304 | - type='str', default='localhost') |
305 | - port = config.get_option(options, 'rabbit_port', |
306 | - type='int', default=5672) |
307 | - use_ssl = config.get_option(options, 'rabbit_use_ssl', |
308 | - type='bool', default=False) |
309 | - userid = config.get_option(options, 'rabbit_userid', |
310 | - type='str', default='guest') |
311 | - password = config.get_option(options, 'rabbit_password', |
312 | - type='str', default='guest') |
313 | - virtual_host = config.get_option(options, 'rabbit_virtual_host', |
314 | - type='str', default='/') |
315 | - |
316 | - self.connection = BrokerConnection( |
317 | - hostname=host, |
318 | - userid=userid, |
319 | - password=password, |
320 | - virtual_host=virtual_host, |
321 | - ssl=use_ssl) |
322 | - self.topic = config.get_option(options, 'rabbit_notification_topic', |
323 | - type='str', default='glance_notifications') |
324 | - |
325 | - def notify(self, message): |
326 | - priority = message.get('priority', self.level) |
327 | + """Initialize the rabbit notification strategy.""" |
328 | + self._options = options |
329 | + host = self._get_option('rabbit_host', 'str', 'localhost') |
330 | + port = self._get_option('rabbit_port', 'int', 5672) |
331 | + use_ssl = self._get_option('rabbit_use_ssl', 'bool', False) |
332 | + userid = self._get_option('rabbit_userid', 'str', 'guest') |
333 | + password = self._get_option('rabbit_password', 'str', 'guest') |
334 | + virtual_host = self._get_option('rabbit_virtual_host', 'str', '/') |
335 | + |
336 | + self.connection = kombu.connection.BrokerConnection( |
337 | + hostname=host, |
338 | + userid=userid, |
339 | + password=password, |
340 | + virtual_host=virtual_host, |
341 | + ssl=use_ssl) |
342 | + |
343 | + self.topic = self._get_option('rabbit_notification_topic', |
344 | + 'str', |
345 | + 'glance_notifications') |
346 | + |
347 | + def _get_option(self, name, datatype, default): |
348 | + """Retrieve a configuration option.""" |
349 | + return config.get_option(self._options, |
350 | + name, |
351 | + type=datatype, |
352 | + default=default) |
353 | + |
354 | + def _send_message(self, message, priority): |
355 | topic = "%s.%s" % (self.topic, priority) |
356 | queue = self.connection.SimpleQueue(topic) |
357 | queue.put(message, serializer="json") |
358 | queue.close() |
359 | |
360 | - |
361 | -def _get_notifier_driver(driver): |
362 | - if driver == "logging": |
363 | - return LoggingNotifier |
364 | - elif driver == "rabbit": |
365 | - return RabbitNotifier |
366 | - else: |
367 | - return NoopNotifier |
368 | + def warn(self, msg): |
369 | + self._send_message(msg, "WARN") |
370 | + |
371 | + def info(self, msg): |
372 | + self._send_message(msg, "INFO") |
373 | + |
374 | + def error(self, msg): |
375 | + self._send_message(msg, "ERROR") |
376 | + |
377 | + |
378 | +class Notifier(object): |
379 | + """Uses a notification strategy to send out messages about events.""" |
380 | + |
381 | + STRATEGIES = { |
382 | + "logging": LoggingStrategy, |
383 | + "rabbit": RabbitStrategy, |
384 | + "noop": NoopStrategy, |
385 | + "default": NoopStrategy, |
386 | + } |
387 | + |
388 | + def __init__(self, options, strategy=None): |
389 | + strategy = config.get_option(options, "notifier_strategy", |
390 | + type="str", default="default") |
391 | + try: |
392 | + self.strategy = self.STRATEGIES[strategy](options) |
393 | + except KeyError: |
394 | + raise exception.InvalidNotifierStrategy(strategy=strategy) |
395 | + |
396 | + @staticmethod |
397 | + def generate_message(event_type, priority, payload): |
398 | + return { |
399 | + "message_id": str(uuid.uuid4()), |
400 | + "publisher_id": socket.gethostname(), |
401 | + "event_type": event_type, |
402 | + "priority": priority, |
403 | + "payload": payload, |
404 | + "timestamp": str(datetime.datetime.utcnow()), |
405 | + } |
406 | + |
407 | + def warn(self, event_type, payload): |
408 | + msg = self.generate_message(event_type, "WARN", payload) |
409 | + self.strategy.warn(msg) |
410 | + |
411 | + def info(self, event_type, payload): |
412 | + msg = self.generate_message(event_type, "INFO", payload) |
413 | + self.strategy.info(msg) |
414 | + |
415 | + def error(self, event_type, payload): |
416 | + msg = self.generate_message(event_type, "ERROR", payload) |
417 | + self.strategy.error(msg) |
418 | |
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 | for rec in recs: |
424 | self.assertEqual(rec['status'], 'pending_delete') |
425 | |
426 | - # Wait 15 seconds for the scrubber to scrub |
427 | - time.sleep(15) |
428 | - |
429 | - recs = list(self.run_sql_cmd(sql)) |
430 | + # NOTE(jkoelker) The build servers sometimes take longer than |
431 | + # 15 seconds to scrub. Give it up to 5 min, checking |
432 | + # checking every 15 seconds. When/if it flips to |
433 | + # deleted, bail immediatly. |
434 | + deleted = set() |
435 | + recs = [] |
436 | + for _ in xrange(20): |
437 | + time.sleep(15) |
438 | + |
439 | + recs = list(self.run_sql_cmd(sql)) |
440 | + self.assertTrue(recs) |
441 | + |
442 | + # NOTE(jkoelker) Reset the deleted set for this loop |
443 | + deleted = set() |
444 | + for rec in recs: |
445 | + deleted.add(rec['status'] == 'deleted') |
446 | + |
447 | + if False not in deleted: |
448 | + break |
449 | + |
450 | self.assertTrue(recs) |
451 | for rec in recs: |
452 | self.assertEqual(rec['status'], 'deleted') |
453 | |
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 | import glance.common.client |
459 | from glance.common import context |
460 | from glance.common import exception |
461 | -import glance.common.notifier |
462 | from glance.registry import server as rserver |
463 | from glance.api import v1 as server |
464 | import glance.store |
465 | @@ -476,10 +475,3 @@ |
466 | fake_datastore.image_get_all_pending_delete) |
467 | stubs.Set(glance.registry.db.api, 'image_get_all', |
468 | fake_datastore.image_get_all) |
469 | - |
470 | - |
471 | -def stub_out_notifier(stubs): |
472 | - def notify(event_type, priority, payload): |
473 | - pass |
474 | - |
475 | - stubs.Set(glance.common.notifier, 'notify', notify) |
476 | |
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 | self.stubs = stubout.StubOutForTesting() |
482 | stubs.stub_out_registry_and_store_server(self.stubs) |
483 | stubs.stub_out_registry_db_image_api(self.stubs) |
484 | - stubs.stub_out_notifier(self.stubs) |
485 | stubs.stub_out_filesystem_backend() |
486 | sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://") |
487 | options = {'verbose': VERBOSE, |
488 | |
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 | stubs.stub_out_registry_db_image_api(self.stubs) |
494 | stubs.stub_out_registry_and_store_server(self.stubs) |
495 | stubs.stub_out_filesystem_backend() |
496 | - stubs.stub_out_notifier(self.stubs) |
497 | self.client = client.Client("0.0.0.0", doc_root="") |
498 | |
499 | def tearDown(self): |
500 | |
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 | # License for the specific language governing permissions and limitations |
506 | # under the License. |
507 | |
508 | -import os |
509 | -import stubout |
510 | +import logging |
511 | import unittest |
512 | |
513 | -from glance import client |
514 | from glance.common import exception |
515 | -import glance.common.notifier |
516 | - |
517 | -from tests import stubs |
518 | - |
519 | - |
520 | -TEST_IMAGE_META = {'name': 'test_image', |
521 | - 'is_public': False, |
522 | - 'disk_format': 'raw', |
523 | - 'container_format': 'ovf'} |
524 | - |
525 | - |
526 | -class TestNotifier(unittest.TestCase): |
527 | +from glance.common import notifier |
528 | + |
529 | + |
530 | +class TestInvalidNotifier(unittest.TestCase): |
531 | """Test that notifications are generated appropriately""" |
532 | |
533 | - def setUp(self): |
534 | - """Establish a clean test environment""" |
535 | - self.stubs = stubout.StubOutForTesting() |
536 | - stubs.stub_out_registry_db_image_api(self.stubs) |
537 | - stubs.stub_out_registry_and_store_server(self.stubs) |
538 | - stubs.stub_out_filesystem_backend() |
539 | - self.client = client.Client("0.0.0.0", doc_root="") |
540 | - self.notification = (None, None) |
541 | - |
542 | - def notify(event_type, priority, payload): |
543 | - self.notification = (event_type, priority) |
544 | - |
545 | - self.stubs.Set(glance.common.notifier, 'notify', notify) |
546 | - |
547 | - def tearDown(self): |
548 | - """Clear the test environment""" |
549 | - stubs.clean_out_fake_filesystem_backend() |
550 | - self.stubs.UnsetAll() |
551 | - |
552 | - def test_create_notify(self): |
553 | - """Test image create notification.""" |
554 | - |
555 | - self.client.add_image(TEST_IMAGE_META, 'foobar') |
556 | - |
557 | - self.assertEquals(self.notification, ('image.upload', 'INFO')) |
558 | - |
559 | - def test_update_notify(self): |
560 | - """Test image update notification. """ |
561 | - |
562 | - image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') |
563 | - self.client.update_image(image_meta['id']) |
564 | - |
565 | - self.assertEquals(self.notification, ('image.update', 'INFO')) |
566 | - |
567 | - def test_delete_notify(self): |
568 | - """Test image delete notification. """ |
569 | - |
570 | - image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') |
571 | - self.client.delete_image(image_meta['id']) |
572 | - |
573 | - self.assertEquals(self.notification, ('image.delete', 'INFO')) |
574 | - |
575 | - def test_create_error_notify(self): |
576 | - """Test image create error notification.""" |
577 | - |
578 | - def boom(options, context, image_id, image_meta, purge_props=False): |
579 | - if 'checksum' in image_meta: |
580 | - raise Exception('boom') |
581 | - return image_meta |
582 | - |
583 | - self.stubs.Set(glance.registry, 'update_image_metadata', boom) |
584 | - |
585 | - try: |
586 | - self.client.add_image(TEST_IMAGE_META, 'foobar') |
587 | - except exception.Invalid: |
588 | - pass |
589 | - |
590 | - self.assertEquals(self.notification, ('image.upload', 'ERROR')) |
591 | - |
592 | - def test_update_error_notify(self): |
593 | - """Test image update error notification.""" |
594 | - |
595 | - image_meta = self.client.add_image(TEST_IMAGE_META, 'foobar') |
596 | - try: |
597 | - self.client.update_image(image_meta['id'], |
598 | - {'container_format': 'invalid'}) |
599 | - except exception.Invalid: |
600 | - pass |
601 | - |
602 | - self.assertEquals(self.notification, ('image.update', 'ERROR')) |
603 | + def test_cannot_create(self): |
604 | + options = {"notifier_strategy": "invalid_notifier"} |
605 | + self.assertRaises(exception.InvalidNotifierStrategy, |
606 | + notifier.Notifier, |
607 | + options) |
608 | + |
609 | + |
610 | +class TestLoggingNotifier(unittest.TestCase): |
611 | + """Test the logging notifier is selected and works properly.""" |
612 | + |
613 | + def setUp(self): |
614 | + options = {"notifier_strategy": "logging"} |
615 | + self.called = False |
616 | + self.logger = logging.getLogger("glance.notifier.logging_notifier") |
617 | + self.notifier = notifier.Notifier(options) |
618 | + |
619 | + def _called(self, msg): |
620 | + self.called = msg |
621 | + |
622 | + def test_warn(self): |
623 | + self.logger.warn = self._called |
624 | + self.notifier.warn("test_event", "test_message") |
625 | + if self.called is False: |
626 | + self.fail("Did not call logging library correctly.") |
627 | + |
628 | + def test_info(self): |
629 | + self.logger.info = self._called |
630 | + self.notifier.info("test_event", "test_message") |
631 | + if self.called is False: |
632 | + self.fail("Did not call logging library correctly.") |
633 | + |
634 | + def test_erorr(self): |
635 | + self.logger.error = self._called |
636 | + self.notifier.error("test_event", "test_message") |
637 | + if self.called is False: |
638 | + self.fail("Did not call logging library correctly.") |
639 | + |
640 | + |
641 | +class TestNoopNotifier(unittest.TestCase): |
642 | + """Test that the noop notifier works...and does nothing?""" |
643 | + |
644 | + def setUp(self): |
645 | + options = {"notifier_strategy": "noop"} |
646 | + self.notifier = notifier.Notifier(options) |
647 | + |
648 | + def test_warn(self): |
649 | + self.notifier.warn("test_event", "test_message") |
650 | + |
651 | + def test_info(self): |
652 | + self.notifier.info("test_event", "test_message") |
653 | + |
654 | + def test_error(self): |
655 | + self.notifier.error("test_event", "test_message") |
656 | + |
657 | + |
658 | +class TestRabbitNotifier(unittest.TestCase): |
659 | + """Test AMQP/Rabbit notifier works.""" |
660 | + |
661 | + def setUp(self): |
662 | + notifier.RabbitStrategy._send_message = self._send_message |
663 | + self.called = False |
664 | + options = {"notifier_strategy": "rabbit"} |
665 | + self.notifier = notifier.Notifier(options) |
666 | + |
667 | + def _send_message(self, message, priority): |
668 | + self.called = { |
669 | + "message": message, |
670 | + "priority": priority, |
671 | + } |
672 | + |
673 | + def test_warn(self): |
674 | + self.notifier.warn("test_event", "test_message") |
675 | + |
676 | + if self.called is False: |
677 | + self.fail("Did not call _send_message properly.") |
678 | + |
679 | + self.assertEquals("test_message", self.called["message"]["payload"]) |
680 | + self.assertEquals("WARN", self.called["message"]["priority"]) |
681 | + |
682 | + def test_info(self): |
683 | + self.notifier.info("test_event", "test_message") |
684 | + |
685 | + if self.called is False: |
686 | + self.fail("Did not call _send_message properly.") |
687 | + |
688 | + self.assertEquals("test_message", self.called["message"]["payload"]) |
689 | + self.assertEquals("INFO", self.called["message"]["priority"]) |
690 | + |
691 | + def test_error(self): |
692 | + self.notifier.error("test_event", "test_message") |
693 | + |
694 | + if self.called is False: |
695 | + self.fail("Did not call _send_message properly.") |
696 | + |
697 | + self.assertEquals("test_message", self.called["message"]["payload"]) |
698 | + self.assertEquals("ERROR", self.called["message"]["priority"]) |
699 | |
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 | httplib2 |
705 | hashlib |
706 | xattr |
707 | +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.