Merge lp:~sandy-walsh/nova/zone-add-uses-zone-name into lp:~hudson-openstack/nova/trunk
- zone-add-uses-zone-name
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Dietz |
Approved revision: | 1534 |
Merged at revision: | 1611 |
Proposed branch: | lp:~sandy-walsh/nova/zone-add-uses-zone-name |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
800 lines (+193/-72) 12 files modified
nova/api/openstack/servers.py (+41/-0) nova/api/openstack/zones.py (+1/-1) nova/compute/api.py (+2/-4) nova/db/sqlalchemy/migrate_repo/versions/048_add_zone_name.py (+35/-0) nova/db/sqlalchemy/models.py (+1/-0) nova/rpc/impl_kombu.py (+0/-1) nova/scheduler/abstract_scheduler.py (+2/-1) nova/scheduler/api.py (+37/-21) nova/scheduler/zone_manager.py (+12/-5) nova/tests/scheduler/test_scheduler.py (+43/-27) nova/tests/test_zones.py (+18/-11) tools/pip-requires (+1/-1) |
To merge this branch: | bzr merge lp:~sandy-walsh/nova/zone-add-uses-zone-name |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Dietz (community) | Approve | ||
Jason Kölker (community) | Approve | ||
Review via email: mp+75651@code.launchpad.net |
Commit message
Keystone support in Nova across Zones.
Description of the change
This branch allows nova to reuse keystone auth tokens in cross-zone situations. Additionally it cleans up some of the exception handling in sub-zones (converting novaclient exceptions to webob-compatible exceptions).
In the case where Nova is acting by itself (like a scheduler polling a child), it will use the admin credentials provided. Otherwise, if a user token exists, the operation will be relayed to the child zones as that user.
Endpoints from the Keystone Service Catalog are now used to identify Zone endpoints. This is a reversal of the direction-
Watch this video for a demo: http://
(the 404 bug is fixed now)
Sandy Walsh (sandy-walsh) wrote : | # |
Jason Kölker (jason-koelker) wrote : | # |
Is Bueno. There is a stray import of webob in scheduler/api.py that is never used.
Sandy Walsh (sandy-walsh) wrote : | # |
Whoopsy ... that was a straggler from an experiment. Thanks.
Don't want to pollute the service.api space with HTTP stuff.
Matt Dietz (cerberus) wrote : | # |
Channeling Rick, here's some femto-nits
17 + def __init__(self, code, title, explaination):
18 + self.code = code
19 + self.title = title
20 + self.explaination = explaination
(Only kind of) Sorry for being an ass, but it's "explanation"
345 + def _wrap_method(
346 """Wrap method to supply an argument."""
347 def _wrap(*args, **kwargs):
348 - return function(arg1, *args, **kwargs)
349 + return function(arg1, arg2, *args, **kwargs)
377 - _wrap_method(
378 + _wrap_method(
i think functools.partial would cover this, though I'm not entirely sure...
442 - return f(*args, **kwargs)
443 + ret = f(*args, **kwargs)
444 + return ret
This looks like a remnant of some debugging?
- 1534. By Sandy Walsh
-
trunk merge fixup
Preview Diff
1 | === modified file 'nova/api/openstack/servers.py' | |||
2 | --- nova/api/openstack/servers.py 2011-09-21 15:54:30 +0000 | |||
3 | +++ nova/api/openstack/servers.py 2011-09-21 20:37:24 +0000 | |||
4 | @@ -17,6 +17,7 @@ | |||
5 | 17 | import os | 17 | import os |
6 | 18 | import traceback | 18 | import traceback |
7 | 19 | 19 | ||
8 | 20 | from novaclient import exceptions as novaclient_exceptions | ||
9 | 20 | from lxml import etree | 21 | from lxml import etree |
10 | 21 | from webob import exc | 22 | from webob import exc |
11 | 22 | import webob | 23 | import webob |
12 | @@ -45,6 +46,27 @@ | |||
13 | 45 | FLAGS = flags.FLAGS | 46 | FLAGS = flags.FLAGS |
14 | 46 | 47 | ||
15 | 47 | 48 | ||
16 | 49 | class ConvertedException(exc.WSGIHTTPException): | ||
17 | 50 | def __init__(self, code, title, explanation): | ||
18 | 51 | self.code = code | ||
19 | 52 | self.title = title | ||
20 | 53 | self.explanation = explanation | ||
21 | 54 | super(ConvertedException, self).__init__() | ||
22 | 55 | |||
23 | 56 | |||
24 | 57 | def novaclient_exception_converter(f): | ||
25 | 58 | """Convert novaclient ClientException HTTP codes to webob exceptions. | ||
26 | 59 | Has to be the outer-most decorator. | ||
27 | 60 | """ | ||
28 | 61 | def new_f(*args, **kwargs): | ||
29 | 62 | try: | ||
30 | 63 | ret = f(*args, **kwargs) | ||
31 | 64 | return ret | ||
32 | 65 | except novaclient_exceptions.ClientException, e: | ||
33 | 66 | raise ConvertedException(e.code, e.message, e.details) | ||
34 | 67 | return new_f | ||
35 | 68 | |||
36 | 69 | |||
37 | 48 | class Controller(object): | 70 | class Controller(object): |
38 | 49 | """ The Server API base controller class for the OpenStack API """ | 71 | """ The Server API base controller class for the OpenStack API """ |
39 | 50 | 72 | ||
40 | @@ -135,6 +157,7 @@ | |||
41 | 135 | 157 | ||
42 | 136 | return dict(servers=servers) | 158 | return dict(servers=servers) |
43 | 137 | 159 | ||
44 | 160 | @novaclient_exception_converter | ||
45 | 138 | @scheduler_api.redirect_handler | 161 | @scheduler_api.redirect_handler |
46 | 139 | def show(self, req, id): | 162 | def show(self, req, id): |
47 | 140 | """ Returns server details by server id """ | 163 | """ Returns server details by server id """ |
48 | @@ -210,6 +233,7 @@ | |||
49 | 210 | def _update(self, context, req, id, inst_dict): | 233 | def _update(self, context, req, id, inst_dict): |
50 | 211 | return exc.HTTPNotImplemented() | 234 | return exc.HTTPNotImplemented() |
51 | 212 | 235 | ||
52 | 236 | @novaclient_exception_converter | ||
53 | 213 | @scheduler_api.redirect_handler | 237 | @scheduler_api.redirect_handler |
54 | 214 | def action(self, req, id, body): | 238 | def action(self, req, id, body): |
55 | 215 | """Multi-purpose method used to take actions on a server""" | 239 | """Multi-purpose method used to take actions on a server""" |
56 | @@ -348,6 +372,7 @@ | |||
57 | 348 | raise exc.HTTPUnprocessableEntity() | 372 | raise exc.HTTPUnprocessableEntity() |
58 | 349 | return webob.Response(status_int=202) | 373 | return webob.Response(status_int=202) |
59 | 350 | 374 | ||
60 | 375 | @novaclient_exception_converter | ||
61 | 351 | @scheduler_api.redirect_handler | 376 | @scheduler_api.redirect_handler |
62 | 352 | def lock(self, req, id): | 377 | def lock(self, req, id): |
63 | 353 | """ | 378 | """ |
64 | @@ -364,6 +389,7 @@ | |||
65 | 364 | raise exc.HTTPUnprocessableEntity() | 389 | raise exc.HTTPUnprocessableEntity() |
66 | 365 | return webob.Response(status_int=202) | 390 | return webob.Response(status_int=202) |
67 | 366 | 391 | ||
68 | 392 | @novaclient_exception_converter | ||
69 | 367 | @scheduler_api.redirect_handler | 393 | @scheduler_api.redirect_handler |
70 | 368 | def unlock(self, req, id): | 394 | def unlock(self, req, id): |
71 | 369 | """ | 395 | """ |
72 | @@ -380,6 +406,7 @@ | |||
73 | 380 | raise exc.HTTPUnprocessableEntity() | 406 | raise exc.HTTPUnprocessableEntity() |
74 | 381 | return webob.Response(status_int=202) | 407 | return webob.Response(status_int=202) |
75 | 382 | 408 | ||
76 | 409 | @novaclient_exception_converter | ||
77 | 383 | @scheduler_api.redirect_handler | 410 | @scheduler_api.redirect_handler |
78 | 384 | def get_lock(self, req, id): | 411 | def get_lock(self, req, id): |
79 | 385 | """ | 412 | """ |
80 | @@ -395,6 +422,7 @@ | |||
81 | 395 | raise exc.HTTPUnprocessableEntity() | 422 | raise exc.HTTPUnprocessableEntity() |
82 | 396 | return webob.Response(status_int=202) | 423 | return webob.Response(status_int=202) |
83 | 397 | 424 | ||
84 | 425 | @novaclient_exception_converter | ||
85 | 398 | @scheduler_api.redirect_handler | 426 | @scheduler_api.redirect_handler |
86 | 399 | def reset_network(self, req, id): | 427 | def reset_network(self, req, id): |
87 | 400 | """ | 428 | """ |
88 | @@ -410,6 +438,7 @@ | |||
89 | 410 | raise exc.HTTPUnprocessableEntity() | 438 | raise exc.HTTPUnprocessableEntity() |
90 | 411 | return webob.Response(status_int=202) | 439 | return webob.Response(status_int=202) |
91 | 412 | 440 | ||
92 | 441 | @novaclient_exception_converter | ||
93 | 413 | @scheduler_api.redirect_handler | 442 | @scheduler_api.redirect_handler |
94 | 414 | def inject_network_info(self, req, id): | 443 | def inject_network_info(self, req, id): |
95 | 415 | """ | 444 | """ |
96 | @@ -425,6 +454,7 @@ | |||
97 | 425 | raise exc.HTTPUnprocessableEntity() | 454 | raise exc.HTTPUnprocessableEntity() |
98 | 426 | return webob.Response(status_int=202) | 455 | return webob.Response(status_int=202) |
99 | 427 | 456 | ||
100 | 457 | @novaclient_exception_converter | ||
101 | 428 | @scheduler_api.redirect_handler | 458 | @scheduler_api.redirect_handler |
102 | 429 | def pause(self, req, id): | 459 | def pause(self, req, id): |
103 | 430 | """ Permit Admins to Pause the server. """ | 460 | """ Permit Admins to Pause the server. """ |
104 | @@ -437,6 +467,7 @@ | |||
105 | 437 | raise exc.HTTPUnprocessableEntity() | 467 | raise exc.HTTPUnprocessableEntity() |
106 | 438 | return webob.Response(status_int=202) | 468 | return webob.Response(status_int=202) |
107 | 439 | 469 | ||
108 | 470 | @novaclient_exception_converter | ||
109 | 440 | @scheduler_api.redirect_handler | 471 | @scheduler_api.redirect_handler |
110 | 441 | def unpause(self, req, id): | 472 | def unpause(self, req, id): |
111 | 442 | """ Permit Admins to Unpause the server. """ | 473 | """ Permit Admins to Unpause the server. """ |
112 | @@ -449,6 +480,7 @@ | |||
113 | 449 | raise exc.HTTPUnprocessableEntity() | 480 | raise exc.HTTPUnprocessableEntity() |
114 | 450 | return webob.Response(status_int=202) | 481 | return webob.Response(status_int=202) |
115 | 451 | 482 | ||
116 | 483 | @novaclient_exception_converter | ||
117 | 452 | @scheduler_api.redirect_handler | 484 | @scheduler_api.redirect_handler |
118 | 453 | def suspend(self, req, id): | 485 | def suspend(self, req, id): |
119 | 454 | """permit admins to suspend the server""" | 486 | """permit admins to suspend the server""" |
120 | @@ -461,6 +493,7 @@ | |||
121 | 461 | raise exc.HTTPUnprocessableEntity() | 493 | raise exc.HTTPUnprocessableEntity() |
122 | 462 | return webob.Response(status_int=202) | 494 | return webob.Response(status_int=202) |
123 | 463 | 495 | ||
124 | 496 | @novaclient_exception_converter | ||
125 | 464 | @scheduler_api.redirect_handler | 497 | @scheduler_api.redirect_handler |
126 | 465 | def resume(self, req, id): | 498 | def resume(self, req, id): |
127 | 466 | """permit admins to resume the server from suspend""" | 499 | """permit admins to resume the server from suspend""" |
128 | @@ -473,6 +506,7 @@ | |||
129 | 473 | raise exc.HTTPUnprocessableEntity() | 506 | raise exc.HTTPUnprocessableEntity() |
130 | 474 | return webob.Response(status_int=202) | 507 | return webob.Response(status_int=202) |
131 | 475 | 508 | ||
132 | 509 | @novaclient_exception_converter | ||
133 | 476 | @scheduler_api.redirect_handler | 510 | @scheduler_api.redirect_handler |
134 | 477 | def migrate(self, req, id): | 511 | def migrate(self, req, id): |
135 | 478 | try: | 512 | try: |
136 | @@ -482,6 +516,7 @@ | |||
137 | 482 | raise exc.HTTPBadRequest() | 516 | raise exc.HTTPBadRequest() |
138 | 483 | return webob.Response(status_int=202) | 517 | return webob.Response(status_int=202) |
139 | 484 | 518 | ||
140 | 519 | @novaclient_exception_converter | ||
141 | 485 | @scheduler_api.redirect_handler | 520 | @scheduler_api.redirect_handler |
142 | 486 | def rescue(self, req, id, body={}): | 521 | def rescue(self, req, id, body={}): |
143 | 487 | """Permit users to rescue the server.""" | 522 | """Permit users to rescue the server.""" |
144 | @@ -500,6 +535,7 @@ | |||
145 | 500 | 535 | ||
146 | 501 | return {'adminPass': password} | 536 | return {'adminPass': password} |
147 | 502 | 537 | ||
148 | 538 | @novaclient_exception_converter | ||
149 | 503 | @scheduler_api.redirect_handler | 539 | @scheduler_api.redirect_handler |
150 | 504 | def unrescue(self, req, id): | 540 | def unrescue(self, req, id): |
151 | 505 | """Permit users to unrescue the server.""" | 541 | """Permit users to unrescue the server.""" |
152 | @@ -512,6 +548,7 @@ | |||
153 | 512 | raise exc.HTTPUnprocessableEntity() | 548 | raise exc.HTTPUnprocessableEntity() |
154 | 513 | return webob.Response(status_int=202) | 549 | return webob.Response(status_int=202) |
155 | 514 | 550 | ||
156 | 551 | @novaclient_exception_converter | ||
157 | 515 | @scheduler_api.redirect_handler | 552 | @scheduler_api.redirect_handler |
158 | 516 | def get_ajax_console(self, req, id): | 553 | def get_ajax_console(self, req, id): |
159 | 517 | """Returns a url to an instance's ajaxterm console.""" | 554 | """Returns a url to an instance's ajaxterm console.""" |
160 | @@ -522,6 +559,7 @@ | |||
161 | 522 | raise exc.HTTPNotFound() | 559 | raise exc.HTTPNotFound() |
162 | 523 | return webob.Response(status_int=202) | 560 | return webob.Response(status_int=202) |
163 | 524 | 561 | ||
164 | 562 | @novaclient_exception_converter | ||
165 | 525 | @scheduler_api.redirect_handler | 563 | @scheduler_api.redirect_handler |
166 | 526 | def get_vnc_console(self, req, id): | 564 | def get_vnc_console(self, req, id): |
167 | 527 | """Returns a url to an instance's ajaxterm console.""" | 565 | """Returns a url to an instance's ajaxterm console.""" |
168 | @@ -532,6 +570,7 @@ | |||
169 | 532 | raise exc.HTTPNotFound() | 570 | raise exc.HTTPNotFound() |
170 | 533 | return webob.Response(status_int=202) | 571 | return webob.Response(status_int=202) |
171 | 534 | 572 | ||
172 | 573 | @novaclient_exception_converter | ||
173 | 535 | @scheduler_api.redirect_handler | 574 | @scheduler_api.redirect_handler |
174 | 536 | def diagnostics(self, req, id): | 575 | def diagnostics(self, req, id): |
175 | 537 | """Permit Admins to retrieve server diagnostics.""" | 576 | """Permit Admins to retrieve server diagnostics.""" |
176 | @@ -574,6 +613,7 @@ | |||
177 | 574 | class ControllerV10(Controller): | 613 | class ControllerV10(Controller): |
178 | 575 | """v1.0 OpenStack API controller""" | 614 | """v1.0 OpenStack API controller""" |
179 | 576 | 615 | ||
180 | 616 | @novaclient_exception_converter | ||
181 | 577 | @scheduler_api.redirect_handler | 617 | @scheduler_api.redirect_handler |
182 | 578 | def delete(self, req, id): | 618 | def delete(self, req, id): |
183 | 579 | """ Destroys a server """ | 619 | """ Destroys a server """ |
184 | @@ -652,6 +692,7 @@ | |||
185 | 652 | class ControllerV11(Controller): | 692 | class ControllerV11(Controller): |
186 | 653 | """v1.1 OpenStack API controller""" | 693 | """v1.1 OpenStack API controller""" |
187 | 654 | 694 | ||
188 | 695 | @novaclient_exception_converter | ||
189 | 655 | @scheduler_api.redirect_handler | 696 | @scheduler_api.redirect_handler |
190 | 656 | def delete(self, req, id): | 697 | def delete(self, req, id): |
191 | 657 | """ Destroys a server """ | 698 | """ Destroys a server """ |
192 | 658 | 699 | ||
193 | === modified file 'nova/api/openstack/zones.py' | |||
194 | --- nova/api/openstack/zones.py 2011-08-05 13:01:55 +0000 | |||
195 | +++ nova/api/openstack/zones.py 2011-09-21 20:37:24 +0000 | |||
196 | @@ -46,7 +46,7 @@ | |||
197 | 46 | 46 | ||
198 | 47 | 47 | ||
199 | 48 | def _exclude_keys(item, keys): | 48 | def _exclude_keys(item, keys): |
201 | 49 | return dict((k, v) for k, v in item.iteritems() if k not in keys) | 49 | return dict((k, v) for k, v in item.iteritems() if k and (k not in keys)) |
202 | 50 | 50 | ||
203 | 51 | 51 | ||
204 | 52 | def _scrub_zone(zone): | 52 | def _scrub_zone(zone): |
205 | 53 | 53 | ||
206 | === modified file 'nova/compute/api.py' | |||
207 | --- nova/compute/api.py 2011-09-21 15:54:30 +0000 | |||
208 | +++ nova/compute/api.py 2011-09-21 20:37:24 +0000 | |||
209 | @@ -993,10 +993,8 @@ | |||
210 | 993 | if not recurse_zones: | 993 | if not recurse_zones: |
211 | 994 | return instances | 994 | return instances |
212 | 995 | 995 | ||
217 | 996 | # Recurse zones. Need admin context for this. Send along | 996 | # Recurse zones. Send along the un-modified search options we received. |
218 | 997 | # the un-modified search options we received.. | 997 | children = scheduler_api.call_zone_method(context, |
215 | 998 | admin_context = context.elevated() | ||
216 | 999 | children = scheduler_api.call_zone_method(admin_context, | ||
219 | 1000 | "list", | 998 | "list", |
220 | 1001 | errors_to_ignore=[novaclient.exceptions.NotFound], | 999 | errors_to_ignore=[novaclient.exceptions.NotFound], |
221 | 1002 | novaclient_collection_name="servers", | 1000 | novaclient_collection_name="servers", |
222 | 1003 | 1001 | ||
223 | === added file 'nova/db/sqlalchemy/migrate_repo/versions/048_add_zone_name.py' | |||
224 | --- nova/db/sqlalchemy/migrate_repo/versions/048_add_zone_name.py 1970-01-01 00:00:00 +0000 | |||
225 | +++ nova/db/sqlalchemy/migrate_repo/versions/048_add_zone_name.py 2011-09-21 20:37:24 +0000 | |||
226 | @@ -0,0 +1,35 @@ | |||
227 | 1 | # Copyright 2011 OpenStack LLC. | ||
228 | 2 | # | ||
229 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
230 | 4 | # not use this file except in compliance with the License. You may obtain | ||
231 | 5 | # a copy of the License at | ||
232 | 6 | # | ||
233 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
234 | 8 | # | ||
235 | 9 | # Unless required by applicable law or agreed to in writing, software | ||
236 | 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
237 | 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
238 | 12 | # License for the specific language governing permissions and limitations | ||
239 | 13 | # under the License. | ||
240 | 14 | |||
241 | 15 | from sqlalchemy import Column, Integer, MetaData, String, Table | ||
242 | 16 | |||
243 | 17 | meta = MetaData() | ||
244 | 18 | |||
245 | 19 | zones = Table('zones', meta, | ||
246 | 20 | Column('id', Integer(), primary_key=True, nullable=False), | ||
247 | 21 | ) | ||
248 | 22 | |||
249 | 23 | name = Column('name', String(255)) | ||
250 | 24 | |||
251 | 25 | |||
252 | 26 | def upgrade(migrate_engine): | ||
253 | 27 | meta.bind = migrate_engine | ||
254 | 28 | |||
255 | 29 | zones.create_column(name) | ||
256 | 30 | |||
257 | 31 | |||
258 | 32 | def downgrade(migrate_engine): | ||
259 | 33 | meta.bind = migrate_engine | ||
260 | 34 | |||
261 | 35 | zones.drop_column(name) | ||
262 | 0 | 36 | ||
263 | === modified file 'nova/db/sqlalchemy/models.py' | |||
264 | --- nova/db/sqlalchemy/models.py 2011-09-14 15:19:03 +0000 | |||
265 | +++ nova/db/sqlalchemy/models.py 2011-09-21 20:37:24 +0000 | |||
266 | @@ -837,6 +837,7 @@ | |||
267 | 837 | """Represents a child zone of this zone.""" | 837 | """Represents a child zone of this zone.""" |
268 | 838 | __tablename__ = 'zones' | 838 | __tablename__ = 'zones' |
269 | 839 | id = Column(Integer, primary_key=True) | 839 | id = Column(Integer, primary_key=True) |
270 | 840 | name = Column(String(255)) | ||
271 | 840 | api_url = Column(String(255)) | 841 | api_url = Column(String(255)) |
272 | 841 | username = Column(String(255)) | 842 | username = Column(String(255)) |
273 | 842 | password = Column(String(255)) | 843 | password = Column(String(255)) |
274 | 843 | 844 | ||
275 | === modified file 'nova/rpc/impl_kombu.py' | |||
276 | --- nova/rpc/impl_kombu.py 2011-08-31 18:54:19 +0000 | |||
277 | +++ nova/rpc/impl_kombu.py 2011-09-21 20:37:24 +0000 | |||
278 | @@ -728,7 +728,6 @@ | |||
279 | 728 | wait_msg = MulticallWaiter(conn) | 728 | wait_msg = MulticallWaiter(conn) |
280 | 729 | conn.declare_direct_consumer(msg_id, wait_msg) | 729 | conn.declare_direct_consumer(msg_id, wait_msg) |
281 | 730 | conn.topic_send(topic, msg) | 730 | conn.topic_send(topic, msg) |
282 | 731 | |||
283 | 732 | return wait_msg | 731 | return wait_msg |
284 | 733 | 732 | ||
285 | 734 | 733 | ||
286 | 735 | 734 | ||
287 | === modified file 'nova/scheduler/abstract_scheduler.py' | |||
288 | --- nova/scheduler/abstract_scheduler.py 2011-09-09 20:27:22 +0000 | |||
289 | +++ nova/scheduler/abstract_scheduler.py 2011-09-21 20:37:24 +0000 | |||
290 | @@ -118,7 +118,8 @@ | |||
291 | 118 | ". ReservationID=%(reservation_id)s") % locals()) | 118 | ". ReservationID=%(reservation_id)s") % locals()) |
292 | 119 | nova = None | 119 | nova = None |
293 | 120 | try: | 120 | try: |
295 | 121 | nova = novaclient.Client(zone.username, zone.password, None, url) | 121 | nova = novaclient.Client(zone.username, zone.password, None, url, |
296 | 122 | token=context.auth_token) | ||
297 | 122 | nova.authenticate() | 123 | nova.authenticate() |
298 | 123 | except novaclient_exceptions.BadRequest, e: | 124 | except novaclient_exceptions.BadRequest, e: |
299 | 124 | raise exception.NotAuthorized(_("Bad credentials attempting " | 125 | raise exception.NotAuthorized(_("Bad credentials attempting " |
300 | 125 | 126 | ||
301 | === modified file 'nova/scheduler/api.py' | |||
302 | --- nova/scheduler/api.py 2011-09-08 06:45:11 +0000 | |||
303 | +++ nova/scheduler/api.py 2011-09-21 20:37:24 +0000 | |||
304 | @@ -17,6 +17,8 @@ | |||
305 | 17 | Handles all requests relating to schedulers. | 17 | Handles all requests relating to schedulers. |
306 | 18 | """ | 18 | """ |
307 | 19 | 19 | ||
308 | 20 | import functools | ||
309 | 21 | |||
310 | 20 | from novaclient import v1_1 as novaclient | 22 | from novaclient import v1_1 as novaclient |
311 | 21 | from novaclient import exceptions as novaclient_exceptions | 23 | from novaclient import exceptions as novaclient_exceptions |
312 | 22 | 24 | ||
313 | @@ -117,13 +119,16 @@ | |||
314 | 117 | zones = db.zone_get_all(context) | 119 | zones = db.zone_get_all(context) |
315 | 118 | for zone in zones: | 120 | for zone in zones: |
316 | 119 | try: | 121 | try: |
317 | 122 | # Do this on behalf of the user ... | ||
318 | 120 | nova = novaclient.Client(zone.username, zone.password, None, | 123 | nova = novaclient.Client(zone.username, zone.password, None, |
320 | 121 | zone.api_url) | 124 | zone.api_url, region_name=zone.name, |
321 | 125 | token=context.auth_token) | ||
322 | 122 | nova.authenticate() | 126 | nova.authenticate() |
323 | 123 | except novaclient_exceptions.BadRequest, e: | 127 | except novaclient_exceptions.BadRequest, e: |
324 | 124 | url = zone.api_url | 128 | url = zone.api_url |
327 | 125 | LOG.warn(_("Failed request to zone; URL=%(url)s: %(e)s") | 129 | name = zone.name |
328 | 126 | % locals()) | 130 | LOG.warn(_("Authentication failed to zone " |
329 | 131 | "'%(name)s' URL=%(url)s: %(e)s") % locals()) | ||
330 | 127 | #TODO (dabo) - add logic for failure counts per zone, | 132 | #TODO (dabo) - add logic for failure counts per zone, |
331 | 128 | # with escalation after a given number of failures. | 133 | # with escalation after a given number of failures. |
332 | 129 | continue | 134 | continue |
333 | @@ -144,25 +149,20 @@ | |||
334 | 144 | return [(zone.id, res.wait()) for zone, res in results] | 149 | return [(zone.id, res.wait()) for zone, res in results] |
335 | 145 | 150 | ||
336 | 146 | 151 | ||
338 | 147 | def child_zone_helper(zone_list, func): | 152 | def child_zone_helper(context, zone_list, func): |
339 | 148 | """Fire off a command to each zone in the list. | 153 | """Fire off a command to each zone in the list. |
340 | 149 | The return is [novaclient return objects] from each child zone. | 154 | The return is [novaclient return objects] from each child zone. |
341 | 150 | For example, if you are calling server.pause(), the list will | 155 | For example, if you are calling server.pause(), the list will |
342 | 151 | be whatever the response from server.pause() is. One entry | 156 | be whatever the response from server.pause() is. One entry |
343 | 152 | per child zone called.""" | 157 | per child zone called.""" |
344 | 153 | 158 | ||
352 | 154 | def _wrap_method(function, arg1): | 159 | def _process(func, context, zone): |
346 | 155 | """Wrap method to supply an argument.""" | ||
347 | 156 | def _wrap(*args, **kwargs): | ||
348 | 157 | return function(arg1, *args, **kwargs) | ||
349 | 158 | return _wrap | ||
350 | 159 | |||
351 | 160 | def _process(func, zone): | ||
353 | 161 | """Worker stub for green thread pool. Give the worker | 160 | """Worker stub for green thread pool. Give the worker |
354 | 162 | an authenticated nova client and zone info.""" | 161 | an authenticated nova client and zone info.""" |
355 | 163 | try: | 162 | try: |
356 | 164 | nova = novaclient.Client(zone.username, zone.password, None, | 163 | nova = novaclient.Client(zone.username, zone.password, None, |
358 | 165 | zone.api_url) | 164 | zone.api_url, region_name=zone.name, |
359 | 165 | token=context.auth_token) | ||
360 | 166 | nova.authenticate() | 166 | nova.authenticate() |
361 | 167 | except novaclient_exceptions.BadRequest, e: | 167 | except novaclient_exceptions.BadRequest, e: |
362 | 168 | url = zone.api_url | 168 | url = zone.api_url |
363 | @@ -174,11 +174,15 @@ | |||
364 | 174 | # there if no other zones had a response. | 174 | # there if no other zones had a response. |
365 | 175 | return exception.ZoneRequestError() | 175 | return exception.ZoneRequestError() |
366 | 176 | else: | 176 | else: |
368 | 177 | return func(nova, zone) | 177 | try: |
369 | 178 | answer = func(nova, zone) | ||
370 | 179 | return answer | ||
371 | 180 | except Exception, e: | ||
372 | 181 | return e | ||
373 | 178 | 182 | ||
374 | 179 | green_pool = greenpool.GreenPool() | 183 | green_pool = greenpool.GreenPool() |
375 | 180 | return [result for result in green_pool.imap( | 184 | return [result for result in green_pool.imap( |
377 | 181 | _wrap_method(_process, func), zone_list)] | 185 | functools.partial(_process, func, context), zone_list)] |
378 | 182 | 186 | ||
379 | 183 | 187 | ||
380 | 184 | def _issue_novaclient_command(nova, zone, collection, | 188 | def _issue_novaclient_command(nova, zone, collection, |
381 | @@ -211,11 +215,11 @@ | |||
382 | 211 | item = args.pop(0) | 215 | item = args.pop(0) |
383 | 212 | try: | 216 | try: |
384 | 213 | result = manager.get(item) | 217 | result = manager.get(item) |
386 | 214 | except novaclient_exceptions.NotFound: | 218 | except novaclient_exceptions.NotFound, e: |
387 | 215 | url = zone.api_url | 219 | url = zone.api_url |
388 | 216 | LOG.debug(_("%(collection)s '%(item)s' not found on '%(url)s'" % | 220 | LOG.debug(_("%(collection)s '%(item)s' not found on '%(url)s'" % |
389 | 217 | locals())) | 221 | locals())) |
391 | 218 | return None | 222 | raise e |
392 | 219 | 223 | ||
393 | 220 | if method_name.lower() != 'get': | 224 | if method_name.lower() != 'get': |
394 | 221 | # if we're doing something other than 'get', call it passing args. | 225 | # if we're doing something other than 'get', call it passing args. |
395 | @@ -278,7 +282,7 @@ | |||
396 | 278 | 282 | ||
397 | 279 | # Ask the children to provide an answer ... | 283 | # Ask the children to provide an answer ... |
398 | 280 | LOG.debug(_("Asking child zones ...")) | 284 | LOG.debug(_("Asking child zones ...")) |
400 | 281 | result = self._call_child_zones(zones, | 285 | result = self._call_child_zones(context, zones, |
401 | 282 | wrap_novaclient_function(_issue_novaclient_command, | 286 | wrap_novaclient_function(_issue_novaclient_command, |
402 | 283 | collection, self.method_name, item_uuid)) | 287 | collection, self.method_name, item_uuid)) |
403 | 284 | # Scrub the results and raise another exception | 288 | # Scrub the results and raise another exception |
404 | @@ -318,10 +322,10 @@ | |||
405 | 318 | 322 | ||
406 | 319 | return wrapped_f | 323 | return wrapped_f |
407 | 320 | 324 | ||
409 | 321 | def _call_child_zones(self, zones, function): | 325 | def _call_child_zones(self, context, zones, function): |
410 | 322 | """Ask the child zones to perform this operation. | 326 | """Ask the child zones to perform this operation. |
411 | 323 | Broken out for testing.""" | 327 | Broken out for testing.""" |
413 | 324 | return child_zone_helper(zones, function) | 328 | return child_zone_helper(context, zones, function) |
414 | 325 | 329 | ||
415 | 326 | def get_collection_context_and_id(self, args, kwargs): | 330 | def get_collection_context_and_id(self, args, kwargs): |
416 | 327 | """Returns a tuple of (novaclient collection name, security | 331 | """Returns a tuple of (novaclient collection name, security |
417 | @@ -369,11 +373,19 @@ | |||
418 | 369 | del server[k] | 373 | del server[k] |
419 | 370 | 374 | ||
420 | 371 | reduced_response.append(dict(server=server)) | 375 | reduced_response.append(dict(server=server)) |
421 | 376 | |||
422 | 377 | # Boil the responses down to a single response. | ||
423 | 378 | # | ||
424 | 379 | # If we get a happy response use that, ignore all the | ||
425 | 380 | # complaint repsonses ... | ||
426 | 372 | if reduced_response: | 381 | if reduced_response: |
427 | 373 | return reduced_response[0] # first for now. | 382 | return reduced_response[0] # first for now. |
428 | 374 | elif found_exception: | 383 | elif found_exception: |
431 | 375 | raise found_exception | 384 | return found_exception |
432 | 376 | raise exception.InstanceNotFound(instance_id=self.item_uuid) | 385 | |
433 | 386 | # Some operations, like delete(), don't send back any results | ||
434 | 387 | # on success. We'll do the same. | ||
435 | 388 | return None | ||
436 | 377 | 389 | ||
437 | 378 | 390 | ||
438 | 379 | def redirect_handler(f): | 391 | def redirect_handler(f): |
439 | @@ -381,5 +393,9 @@ | |||
440 | 381 | try: | 393 | try: |
441 | 382 | return f(*args, **kwargs) | 394 | return f(*args, **kwargs) |
442 | 383 | except RedirectResult, e: | 395 | except RedirectResult, e: |
443 | 396 | # Remember: exceptions are returned, not thrown, in the decorator. | ||
444 | 397 | # At this point it's safe to throw it. | ||
445 | 398 | if isinstance(e.results, BaseException): | ||
446 | 399 | raise e.results | ||
447 | 384 | return e.results | 400 | return e.results |
448 | 385 | return new_f | 401 | return new_f |
449 | 386 | 402 | ||
450 | === modified file 'nova/scheduler/zone_manager.py' | |||
451 | --- nova/scheduler/zone_manager.py 2011-08-11 20:45:55 +0000 | |||
452 | +++ nova/scheduler/zone_manager.py 2011-09-21 20:37:24 +0000 | |||
453 | @@ -51,16 +51,18 @@ | |||
454 | 51 | def update_credentials(self, zone): | 51 | def update_credentials(self, zone): |
455 | 52 | """Update zone credentials from db""" | 52 | """Update zone credentials from db""" |
456 | 53 | self.zone_id = zone.id | 53 | self.zone_id = zone.id |
457 | 54 | self.name = zone.name | ||
458 | 54 | self.api_url = zone.api_url | 55 | self.api_url = zone.api_url |
459 | 55 | self.username = zone.username | 56 | self.username = zone.username |
460 | 56 | self.password = zone.password | 57 | self.password = zone.password |
461 | 58 | self.weight_offset = zone.weight_offset | ||
462 | 59 | self.weight_scale = zone.weight_scale | ||
463 | 57 | 60 | ||
464 | 58 | def update_metadata(self, zone_metadata): | 61 | def update_metadata(self, zone_metadata): |
465 | 59 | """Update zone metadata after successful communications with | 62 | """Update zone metadata after successful communications with |
466 | 60 | child zone.""" | 63 | child zone.""" |
467 | 61 | self.last_seen = utils.utcnow() | 64 | self.last_seen = utils.utcnow() |
468 | 62 | self.attempt = 0 | 65 | self.attempt = 0 |
469 | 63 | self.name = zone_metadata.get("name", "n/a") | ||
470 | 64 | self.capabilities = ", ".join(["%s=%s" % (k, v) | 66 | self.capabilities = ", ".join(["%s=%s" % (k, v) |
471 | 65 | for k, v in zone_metadata.iteritems() if k != 'name']) | 67 | for k, v in zone_metadata.iteritems() if k != 'name']) |
472 | 66 | self.is_active = True | 68 | self.is_active = True |
473 | @@ -68,7 +70,8 @@ | |||
474 | 68 | def to_dict(self): | 70 | def to_dict(self): |
475 | 69 | return dict(name=self.name, capabilities=self.capabilities, | 71 | return dict(name=self.name, capabilities=self.capabilities, |
476 | 70 | is_active=self.is_active, api_url=self.api_url, | 72 | is_active=self.is_active, api_url=self.api_url, |
478 | 71 | id=self.zone_id) | 73 | id=self.zone_id, weight_scale=self.weight_scale, |
479 | 74 | weight_offset=self.weight_offset) | ||
480 | 72 | 75 | ||
481 | 73 | def log_error(self, exception): | 76 | def log_error(self, exception): |
482 | 74 | """Something went wrong. Check to see if zone should be | 77 | """Something went wrong. Check to see if zone should be |
483 | @@ -89,15 +92,19 @@ | |||
484 | 89 | 92 | ||
485 | 90 | 93 | ||
486 | 91 | def _call_novaclient(zone): | 94 | def _call_novaclient(zone): |
488 | 92 | """Call novaclient. Broken out for testing purposes.""" | 95 | """Call novaclient. Broken out for testing purposes. Note that |
489 | 96 | we have to use the admin credentials for this since there is no | ||
490 | 97 | available context.""" | ||
491 | 93 | client = novaclient.Client(zone.username, zone.password, None, | 98 | client = novaclient.Client(zone.username, zone.password, None, |
493 | 94 | zone.api_url) | 99 | zone.api_url, region_name=zone.name) |
494 | 95 | return client.zones.info()._info | 100 | return client.zones.info()._info |
495 | 96 | 101 | ||
496 | 97 | 102 | ||
497 | 98 | def _poll_zone(zone): | 103 | def _poll_zone(zone): |
498 | 99 | """Eventlet worker to poll a zone.""" | 104 | """Eventlet worker to poll a zone.""" |
500 | 100 | logging.debug(_("Polling zone: %s") % zone.api_url) | 105 | name = zone.name |
501 | 106 | url = zone.api_url | ||
502 | 107 | logging.debug(_("Polling zone: %(name)s @ %(url)s") % locals()) | ||
503 | 101 | try: | 108 | try: |
504 | 102 | zone.update_metadata(_call_novaclient(zone)) | 109 | zone.update_metadata(_call_novaclient(zone)) |
505 | 103 | except Exception, e: | 110 | except Exception, e: |
506 | 104 | 111 | ||
507 | === modified file 'nova/tests/scheduler/test_scheduler.py' | |||
508 | --- nova/tests/scheduler/test_scheduler.py 2011-09-08 08:09:22 +0000 | |||
509 | +++ nova/tests/scheduler/test_scheduler.py 2011-09-21 20:37:24 +0000 | |||
510 | @@ -53,6 +53,10 @@ | |||
511 | 53 | FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' | 53 | FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
512 | 54 | 54 | ||
513 | 55 | 55 | ||
514 | 56 | class FakeContext(object): | ||
515 | 57 | auth_token = None | ||
516 | 58 | |||
517 | 59 | |||
518 | 56 | class TestDriver(driver.Scheduler): | 60 | class TestDriver(driver.Scheduler): |
519 | 57 | """Scheduler Driver for Tests""" | 61 | """Scheduler Driver for Tests""" |
520 | 58 | def schedule(context, topic, *args, **kwargs): | 62 | def schedule(context, topic, *args, **kwargs): |
521 | @@ -956,11 +960,12 @@ | |||
522 | 956 | 960 | ||
523 | 957 | 961 | ||
524 | 958 | class FakeZone(object): | 962 | class FakeZone(object): |
526 | 959 | def __init__(self, id, api_url, username, password): | 963 | def __init__(self, id, api_url, username, password, name='child'): |
527 | 960 | self.id = id | 964 | self.id = id |
528 | 961 | self.api_url = api_url | 965 | self.api_url = api_url |
529 | 962 | self.username = username | 966 | self.username = username |
530 | 963 | self.password = password | 967 | self.password = password |
531 | 968 | self.name = name | ||
532 | 964 | 969 | ||
533 | 965 | 970 | ||
534 | 966 | ZONE_API_URL1 = "http://1.example.com" | 971 | ZONE_API_URL1 = "http://1.example.com" |
535 | @@ -986,7 +991,7 @@ | |||
536 | 986 | super(FakeRerouteCompute, self).__init__(method_name) | 991 | super(FakeRerouteCompute, self).__init__(method_name) |
537 | 987 | self.id_to_return = id_to_return | 992 | self.id_to_return = id_to_return |
538 | 988 | 993 | ||
540 | 989 | def _call_child_zones(self, zones, function): | 994 | def _call_child_zones(self, context, zones, function): |
541 | 990 | return [] | 995 | return [] |
542 | 991 | 996 | ||
543 | 992 | def get_collection_context_and_id(self, args, kwargs): | 997 | def get_collection_context_and_id(self, args, kwargs): |
544 | @@ -1071,8 +1076,8 @@ | |||
545 | 1071 | def test_unmarshal_single_server(self): | 1076 | def test_unmarshal_single_server(self): |
546 | 1072 | decorator = api.reroute_compute("foo") | 1077 | decorator = api.reroute_compute("foo") |
547 | 1073 | decorator.item_uuid = 'fake_uuid' | 1078 | decorator.item_uuid = 'fake_uuid' |
550 | 1074 | self.assertRaises(exception.InstanceNotFound, | 1079 | result = decorator.unmarshall_result([]) |
551 | 1075 | decorator.unmarshall_result, []) | 1080 | self.assertEquals(decorator.unmarshall_result([]), None) |
552 | 1076 | self.assertEquals(decorator.unmarshall_result( | 1081 | self.assertEquals(decorator.unmarshall_result( |
553 | 1077 | [FakeResource(dict(a=1, b=2)), ]), | 1082 | [FakeResource(dict(a=1, b=2)), ]), |
554 | 1078 | dict(server=dict(a=1, b=2))) | 1083 | dict(server=dict(a=1, b=2))) |
555 | @@ -1092,7 +1097,8 @@ | |||
556 | 1092 | return None | 1097 | return None |
557 | 1093 | 1098 | ||
558 | 1094 | class FakeNovaClientWithFailure(object): | 1099 | class FakeNovaClientWithFailure(object): |
560 | 1095 | def __init__(self, username, password, method, api_url): | 1100 | def __init__(self, username, password, method, api_url, |
561 | 1101 | token=None, region_name=None): | ||
562 | 1096 | self.api_url = api_url | 1102 | self.api_url = api_url |
563 | 1097 | 1103 | ||
564 | 1098 | def authenticate(self): | 1104 | def authenticate(self): |
565 | @@ -1107,8 +1113,11 @@ | |||
566 | 1107 | def do_get(self, context, uuid): | 1113 | def do_get(self, context, uuid): |
567 | 1108 | pass | 1114 | pass |
568 | 1109 | 1115 | ||
571 | 1110 | self.assertRaises(exception.ZoneRequestError, | 1116 | try: |
572 | 1111 | do_get, None, {}, FAKE_UUID) | 1117 | do_get(None, FakeContext(), FAKE_UUID) |
573 | 1118 | self.fail("Should have got redirect exception.") | ||
574 | 1119 | except api.RedirectResult, e: | ||
575 | 1120 | self.assertTrue(isinstance(e.results, exception.ZoneRequestError)) | ||
576 | 1112 | 1121 | ||
577 | 1113 | def test_one_zone_down_got_instance(self): | 1122 | def test_one_zone_down_got_instance(self): |
578 | 1114 | 1123 | ||
579 | @@ -1120,7 +1129,8 @@ | |||
580 | 1120 | return FakeServer() | 1129 | return FakeServer() |
581 | 1121 | 1130 | ||
582 | 1122 | class FakeNovaClientWithFailure(object): | 1131 | class FakeNovaClientWithFailure(object): |
584 | 1123 | def __init__(self, username, password, method, api_url): | 1132 | def __init__(self, username, password, method, api_url, |
585 | 1133 | token=None, region_name=None): | ||
586 | 1124 | self.api_url = api_url | 1134 | self.api_url = api_url |
587 | 1125 | 1135 | ||
588 | 1126 | def authenticate(self): | 1136 | def authenticate(self): |
589 | @@ -1136,14 +1146,14 @@ | |||
590 | 1136 | pass | 1146 | pass |
591 | 1137 | 1147 | ||
592 | 1138 | try: | 1148 | try: |
594 | 1139 | do_get(None, {}, FAKE_UUID) | 1149 | do_get(None, FakeContext(), FAKE_UUID) |
595 | 1140 | except api.RedirectResult, e: | 1150 | except api.RedirectResult, e: |
596 | 1141 | results = e.results | 1151 | results = e.results |
597 | 1142 | self.assertIn('server', results) | 1152 | self.assertIn('server', results) |
598 | 1143 | self.assertEqual(results['server']['id'], FAKE_UUID) | 1153 | self.assertEqual(results['server']['id'], FAKE_UUID) |
599 | 1144 | self.assertEqual(results['server']['test'], '1234') | 1154 | self.assertEqual(results['server']['test'], '1234') |
600 | 1145 | except Exception, e: | 1155 | except Exception, e: |
602 | 1146 | self.fail(_("RedirectResult should have been raised")) | 1156 | self.fail(_("RedirectResult should have been raised: %s" % e)) |
603 | 1147 | else: | 1157 | else: |
604 | 1148 | self.fail(_("RedirectResult should have been raised")) | 1158 | self.fail(_("RedirectResult should have been raised")) |
605 | 1149 | 1159 | ||
606 | @@ -1153,7 +1163,8 @@ | |||
607 | 1153 | return None | 1163 | return None |
608 | 1154 | 1164 | ||
609 | 1155 | class FakeNovaClientNoFailure(object): | 1165 | class FakeNovaClientNoFailure(object): |
611 | 1156 | def __init__(self, username, password, method, api_url): | 1166 | def __init__(self, username, password, method, api_url, |
612 | 1167 | token=None, region_name=None): | ||
613 | 1157 | pass | 1168 | pass |
614 | 1158 | 1169 | ||
615 | 1159 | def authenticate(self): | 1170 | def authenticate(self): |
616 | @@ -1167,8 +1178,11 @@ | |||
617 | 1167 | def do_get(self, context, uuid): | 1178 | def do_get(self, context, uuid): |
618 | 1168 | pass | 1179 | pass |
619 | 1169 | 1180 | ||
622 | 1170 | self.assertRaises(exception.InstanceNotFound, | 1181 | try: |
623 | 1171 | do_get, None, {}, FAKE_UUID) | 1182 | do_get(None, FakeContext(), FAKE_UUID) |
624 | 1183 | self.fail("Expected redirect exception") | ||
625 | 1184 | except api.RedirectResult, e: | ||
626 | 1185 | self.assertEquals(e.results, None) | ||
627 | 1172 | 1186 | ||
628 | 1173 | 1187 | ||
629 | 1174 | class FakeServerCollection(object): | 1188 | class FakeServerCollection(object): |
630 | @@ -1209,17 +1223,19 @@ | |||
631 | 1209 | 1223 | ||
632 | 1210 | def test_issue_novaclient_command_not_found(self): | 1224 | def test_issue_novaclient_command_not_found(self): |
633 | 1211 | zone = FakeZone(1, 'http://example.com', 'bob', 'xxx') | 1225 | zone = FakeZone(1, 'http://example.com', 'bob', 'xxx') |
645 | 1212 | self.assertEquals(api._issue_novaclient_command( | 1226 | try: |
646 | 1213 | FakeNovaClient(FakeEmptyServerCollection()), | 1227 | api._issue_novaclient_command(FakeNovaClient( |
647 | 1214 | zone, "servers", "get", 100), None) | 1228 | FakeEmptyServerCollection()), zone, "servers", "get", 100) |
648 | 1215 | 1229 | self.fail("Expected NotFound exception") | |
649 | 1216 | self.assertEquals(api._issue_novaclient_command( | 1230 | except novaclient_exceptions.NotFound, e: |
650 | 1217 | FakeNovaClient(FakeEmptyServerCollection()), | 1231 | pass |
651 | 1218 | zone, "servers", "find", name="test"), None) | 1232 | |
652 | 1219 | 1233 | try: | |
653 | 1220 | self.assertEquals(api._issue_novaclient_command( | 1234 | api._issue_novaclient_command(FakeNovaClient( |
654 | 1221 | FakeNovaClient(FakeEmptyServerCollection()), | 1235 | FakeEmptyServerCollection()), zone, "servers", "any", "name") |
655 | 1222 | zone, "servers", "any", "name"), None) | 1236 | self.fail("Expected NotFound exception") |
656 | 1237 | except novaclient_exceptions.NotFound, e: | ||
657 | 1238 | pass | ||
658 | 1223 | 1239 | ||
659 | 1224 | 1240 | ||
660 | 1225 | class FakeZonesProxy(object): | 1241 | class FakeZonesProxy(object): |
661 | @@ -1250,7 +1266,7 @@ | |||
662 | 1250 | super(CallZoneMethodTest, self).tearDown() | 1266 | super(CallZoneMethodTest, self).tearDown() |
663 | 1251 | 1267 | ||
664 | 1252 | def test_call_zone_method(self): | 1268 | def test_call_zone_method(self): |
666 | 1253 | context = {} | 1269 | context = FakeContext() |
667 | 1254 | method = 'do_something' | 1270 | method = 'do_something' |
668 | 1255 | results = api.call_zone_method(context, method) | 1271 | results = api.call_zone_method(context, method) |
669 | 1256 | self.assertEqual(len(results), 2) | 1272 | self.assertEqual(len(results), 2) |
670 | @@ -1258,12 +1274,12 @@ | |||
671 | 1258 | self.assertIn((2, 42), results) | 1274 | self.assertIn((2, 42), results) |
672 | 1259 | 1275 | ||
673 | 1260 | def test_call_zone_method_not_present(self): | 1276 | def test_call_zone_method_not_present(self): |
675 | 1261 | context = {} | 1277 | context = FakeContext() |
676 | 1262 | method = 'not_present' | 1278 | method = 'not_present' |
677 | 1263 | self.assertRaises(AttributeError, api.call_zone_method, | 1279 | self.assertRaises(AttributeError, api.call_zone_method, |
678 | 1264 | context, method) | 1280 | context, method) |
679 | 1265 | 1281 | ||
680 | 1266 | def test_call_zone_method_generates_exception(self): | 1282 | def test_call_zone_method_generates_exception(self): |
682 | 1267 | context = {} | 1283 | context = FakeContext() |
683 | 1268 | method = 'raises_exception' | 1284 | method = 'raises_exception' |
684 | 1269 | self.assertRaises(Exception, api.call_zone_method, context, method) | 1285 | self.assertRaises(Exception, api.call_zone_method, context, method) |
685 | 1270 | 1286 | ||
686 | === modified file 'nova/tests/test_zones.py' | |||
687 | --- nova/tests/test_zones.py 2011-08-04 17:43:42 +0000 | |||
688 | +++ nova/tests/test_zones.py 2011-09-21 20:37:24 +0000 | |||
689 | @@ -63,7 +63,8 @@ | |||
690 | 63 | self.mox.StubOutWithMock(db, 'zone_get_all') | 63 | self.mox.StubOutWithMock(db, 'zone_get_all') |
691 | 64 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ | 64 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ |
692 | 65 | FakeZone(id=1, api_url='http://foo.com', username='user1', | 65 | FakeZone(id=1, api_url='http://foo.com', username='user1', |
694 | 66 | password='pass1'), | 66 | password='pass1', name='child', weight_offset=0.0, |
695 | 67 | weight_scale=1.0), | ||
696 | 67 | ]) | 68 | ]) |
697 | 68 | 69 | ||
698 | 69 | self.assertEquals(len(zm.zone_states), 0) | 70 | self.assertEquals(len(zm.zone_states), 0) |
699 | @@ -107,13 +108,15 @@ | |||
700 | 107 | zm = zone_manager.ZoneManager() | 108 | zm = zone_manager.ZoneManager() |
701 | 108 | zone_state = zone_manager.ZoneState() | 109 | zone_state = zone_manager.ZoneState() |
702 | 109 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', | 110 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', |
704 | 110 | username='user1', password='pass1')) | 111 | username='user1', password='pass1', name='child', |
705 | 112 | weight_offset=0.0, weight_scale=1.0)) | ||
706 | 111 | zm.zone_states[1] = zone_state | 113 | zm.zone_states[1] = zone_state |
707 | 112 | 114 | ||
708 | 113 | self.mox.StubOutWithMock(db, 'zone_get_all') | 115 | self.mox.StubOutWithMock(db, 'zone_get_all') |
709 | 114 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ | 116 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ |
710 | 115 | FakeZone(id=1, api_url='http://foo.com', username='user2', | 117 | FakeZone(id=1, api_url='http://foo.com', username='user2', |
712 | 116 | password='pass2'), | 118 | password='pass2', name='child', |
713 | 119 | weight_offset=0.0, weight_scale=1.0), | ||
714 | 117 | ]) | 120 | ]) |
715 | 118 | 121 | ||
716 | 119 | self.assertEquals(len(zm.zone_states), 1) | 122 | self.assertEquals(len(zm.zone_states), 1) |
717 | @@ -129,7 +132,8 @@ | |||
718 | 129 | zm = zone_manager.ZoneManager() | 132 | zm = zone_manager.ZoneManager() |
719 | 130 | zone_state = zone_manager.ZoneState() | 133 | zone_state = zone_manager.ZoneState() |
720 | 131 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', | 134 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', |
722 | 132 | username='user1', password='pass1')) | 135 | username='user1', password='pass1', name='child', |
723 | 136 | weight_offset=0.0, weight_scale=1.0)) | ||
724 | 133 | zm.zone_states[1] = zone_state | 137 | zm.zone_states[1] = zone_state |
725 | 134 | 138 | ||
726 | 135 | self.mox.StubOutWithMock(db, 'zone_get_all') | 139 | self.mox.StubOutWithMock(db, 'zone_get_all') |
727 | @@ -147,14 +151,16 @@ | |||
728 | 147 | zm = zone_manager.ZoneManager() | 151 | zm = zone_manager.ZoneManager() |
729 | 148 | zone_state = zone_manager.ZoneState() | 152 | zone_state = zone_manager.ZoneState() |
730 | 149 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', | 153 | zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com', |
732 | 150 | username='user1', password='pass1')) | 154 | username='user1', password='pass1', name='child', |
733 | 155 | weight_offset=2.0, weight_scale=3.0)) | ||
734 | 151 | zm.zone_states[1] = zone_state | 156 | zm.zone_states[1] = zone_state |
735 | 152 | 157 | ||
736 | 153 | self.mox.StubOutWithMock(db, 'zone_get_all') | 158 | self.mox.StubOutWithMock(db, 'zone_get_all') |
737 | 154 | 159 | ||
738 | 155 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ | 160 | db.zone_get_all(mox.IgnoreArg()).AndReturn([ |
739 | 156 | FakeZone(id=2, api_url='http://foo.com', username='user2', | 161 | FakeZone(id=2, api_url='http://foo.com', username='user2', |
741 | 157 | password='pass2'), | 162 | password='pass2', name='child', weight_offset=2.0, |
742 | 163 | weight_scale=3.0), | ||
743 | 158 | ]) | 164 | ]) |
744 | 159 | self.assertEquals(len(zm.zone_states), 1) | 165 | self.assertEquals(len(zm.zone_states), 1) |
745 | 160 | 166 | ||
746 | @@ -168,19 +174,20 @@ | |||
747 | 168 | def test_poll_zone(self): | 174 | def test_poll_zone(self): |
748 | 169 | self.mox.StubOutWithMock(zone_manager, '_call_novaclient') | 175 | self.mox.StubOutWithMock(zone_manager, '_call_novaclient') |
749 | 170 | zone_manager._call_novaclient(mox.IgnoreArg()).AndReturn( | 176 | zone_manager._call_novaclient(mox.IgnoreArg()).AndReturn( |
751 | 171 | dict(name='zohan', capabilities='hairdresser')) | 177 | dict(name='child', capabilities='hairdresser')) |
752 | 172 | 178 | ||
753 | 173 | zone_state = zone_manager.ZoneState() | 179 | zone_state = zone_manager.ZoneState() |
754 | 174 | zone_state.update_credentials(FakeZone(id=2, | 180 | zone_state.update_credentials(FakeZone(id=2, |
755 | 175 | api_url='http://foo.com', username='user2', | 181 | api_url='http://foo.com', username='user2', |
757 | 176 | password='pass2')) | 182 | password='pass2', name='child', |
758 | 183 | weight_offset=0.0, weight_scale=1.0)) | ||
759 | 177 | zone_state.attempt = 1 | 184 | zone_state.attempt = 1 |
760 | 178 | 185 | ||
761 | 179 | self.mox.ReplayAll() | 186 | self.mox.ReplayAll() |
762 | 180 | zone_manager._poll_zone(zone_state) | 187 | zone_manager._poll_zone(zone_state) |
763 | 181 | self.mox.VerifyAll() | 188 | self.mox.VerifyAll() |
764 | 182 | self.assertEquals(zone_state.attempt, 0) | 189 | self.assertEquals(zone_state.attempt, 0) |
766 | 183 | self.assertEquals(zone_state.name, 'zohan') | 190 | self.assertEquals(zone_state.name, 'child') |
767 | 184 | 191 | ||
768 | 185 | def test_poll_zone_fails(self): | 192 | def test_poll_zone_fails(self): |
769 | 186 | self.stubs.Set(zone_manager, "_call_novaclient", exploding_novaclient) | 193 | self.stubs.Set(zone_manager, "_call_novaclient", exploding_novaclient) |
770 | @@ -188,7 +195,8 @@ | |||
771 | 188 | zone_state = zone_manager.ZoneState() | 195 | zone_state = zone_manager.ZoneState() |
772 | 189 | zone_state.update_credentials(FakeZone(id=2, | 196 | zone_state.update_credentials(FakeZone(id=2, |
773 | 190 | api_url='http://foo.com', username='user2', | 197 | api_url='http://foo.com', username='user2', |
775 | 191 | password='pass2')) | 198 | password='pass2', name='child', |
776 | 199 | weight_offset=0.0, weight_scale=1.0)) | ||
777 | 192 | zone_state.attempt = FLAGS.zone_failures_to_offline - 1 | 200 | zone_state.attempt = FLAGS.zone_failures_to_offline - 1 |
778 | 193 | 201 | ||
779 | 194 | self.mox.ReplayAll() | 202 | self.mox.ReplayAll() |
780 | @@ -196,7 +204,6 @@ | |||
781 | 196 | self.mox.VerifyAll() | 204 | self.mox.VerifyAll() |
782 | 197 | self.assertEquals(zone_state.attempt, 3) | 205 | self.assertEquals(zone_state.attempt, 3) |
783 | 198 | self.assertFalse(zone_state.is_active) | 206 | self.assertFalse(zone_state.is_active) |
784 | 199 | self.assertEquals(zone_state.name, None) | ||
785 | 200 | 207 | ||
786 | 201 | def test_host_service_caps_stale_no_stale_service(self): | 208 | def test_host_service_caps_stale_no_stale_service(self): |
787 | 202 | zm = zone_manager.ZoneManager() | 209 | zm = zone_manager.ZoneManager() |
788 | 203 | 210 | ||
789 | === modified file 'tools/pip-requires' | |||
790 | --- tools/pip-requires 2011-09-02 05:39:31 +0000 | |||
791 | +++ tools/pip-requires 2011-09-21 20:37:24 +0000 | |||
792 | @@ -11,7 +11,7 @@ | |||
793 | 11 | kombu | 11 | kombu |
794 | 12 | lockfile==0.8 | 12 | lockfile==0.8 |
795 | 13 | lxml==2.3 | 13 | lxml==2.3 |
797 | 14 | python-novaclient==2.6.0 | 14 | python-novaclient==2.6.5 |
798 | 15 | python-daemon==1.5.5 | 15 | python-daemon==1.5.5 |
799 | 16 | python-gflags==1.3 | 16 | python-gflags==1.3 |
800 | 17 | redis==2.0.0 | 17 | redis==2.0.0 |
I should add that this merge prop requires the following keystone branch /review. openstack. org/#change, 359
https:/
and the python-novaclient 2.6.5 pull request /github. com/rackspace/ python- novaclient/ pull/118
https:/
to land first.