Coverage for nova/conductor/rpcapi.py: 77%

148 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-17 15:08 +0000

1# Copyright 2013 IBM Corp. 

2# Copyright 2013 Red Hat, Inc. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16"""Client side of the conductor RPC API.""" 

17 

18import oslo_messaging as messaging 

19from oslo_serialization import jsonutils 

20from oslo_versionedobjects import base as ovo_base 

21 

22import nova.conf 

23from nova import exception 

24from nova.i18n import _ 

25from nova.objects import base as objects_base 

26from nova import profiler 

27from nova import rpc 

28 

29CONF = nova.conf.CONF 

30RPC_TOPIC = 'conductor' 

31 

32 

33@profiler.trace_cls("rpc") 

34class ConductorAPI(object): 

35 """Client side of the conductor RPC API 

36 

37 API version history: 

38 

39 * 1.0 - Initial version. 

40 * 1.1 - Added migration_update 

41 * 1.2 - Added instance_get_by_uuid and instance_get_all_by_host 

42 * 1.3 - Added aggregate_host_add and aggregate_host_delete 

43 * 1.4 - Added migration_get 

44 * 1.5 - Added bw_usage_update 

45 * 1.6 - Added get_backdoor_port() 

46 * 1.7 - Added aggregate_get_by_host, aggregate_metadata_add, 

47 and aggregate_metadata_delete 

48 * 1.8 - Added security_group_get_by_instance and 

49 security_group_rule_get_by_security_group 

50 * 1.9 - Added provider_fw_rule_get_all 

51 * 1.10 - Added agent_build_get_by_triple 

52 * 1.11 - Added aggregate_get 

53 * 1.12 - Added block_device_mapping_update_or_create 

54 * 1.13 - Added block_device_mapping_get_all_by_instance 

55 * 1.14 - Added block_device_mapping_destroy 

56 * 1.15 - Added instance_get_all_by_filters and 

57 instance_get_all_hung_in_rebooting and 

58 instance_get_active_by_window 

59 Deprecated instance_get_all_by_host 

60 * 1.16 - Added instance_destroy 

61 * 1.17 - Added instance_info_cache_delete 

62 * 1.18 - Added instance_type_get 

63 * 1.19 - Added vol_get_usage_by_time and vol_usage_update 

64 * 1.20 - Added migration_get_unconfirmed_by_dest_compute 

65 * 1.21 - Added service_get_all_by 

66 * 1.22 - Added ping 

67 * 1.23 - Added instance_get_all 

68 Un-Deprecate instance_get_all_by_host 

69 * 1.24 - Added instance_get 

70 * 1.25 - Added action_event_start and action_event_finish 

71 * 1.26 - Added instance_info_cache_update 

72 * 1.27 - Added service_create 

73 * 1.28 - Added binary arg to service_get_all_by 

74 * 1.29 - Added service_destroy 

75 * 1.30 - Added migration_create 

76 * 1.31 - Added migration_get_in_progress_by_host_and_node 

77 * 1.32 - Added optional node to instance_get_all_by_host 

78 * 1.33 - Added compute_node_create and compute_node_update 

79 * 1.34 - Added service_update 

80 * 1.35 - Added instance_get_active_by_window_joined 

81 * 1.36 - Added instance_fault_create 

82 * 1.37 - Added task_log_get, task_log_begin_task, task_log_end_task 

83 * 1.38 - Added service name to instance_update 

84 * 1.39 - Added notify_usage_exists 

85 * 1.40 - Added security_groups_trigger_handler and 

86 security_groups_trigger_members_refresh 

87 Remove instance_get_active_by_window 

88 * 1.41 - Added fixed_ip_get_by_instance, network_get, 

89 instance_floating_address_get_all, quota_commit, 

90 quota_rollback 

91 * 1.42 - Added get_ec2_ids, aggregate_metadata_get_by_host 

92 * 1.43 - Added compute_stop 

93 * 1.44 - Added compute_node_delete 

94 * 1.45 - Added project_id to quota_commit and quota_rollback 

95 * 1.46 - Added compute_confirm_resize 

96 * 1.47 - Added columns_to_join to instance_get_all_by_host and 

97 instance_get_all_by_filters 

98 * 1.48 - Added compute_unrescue 

99 

100 ... Grizzly supports message version 1.48. So, any changes to existing 

101 methods in 2.x after that point should be done such that they can 

102 handle the version_cap being set to 1.48. 

103 

104 * 1.49 - Added columns_to_join to instance_get_by_uuid 

105 * 1.50 - Added object_action() and object_class_action() 

106 * 1.51 - Added the 'legacy' argument to 

107 block_device_mapping_get_all_by_instance 

108 * 1.52 - Pass instance objects for compute_confirm_resize 

109 * 1.53 - Added compute_reboot 

110 * 1.54 - Added 'update_cells' argument to bw_usage_update 

111 * 1.55 - Pass instance objects for compute_stop 

112 * 1.56 - Remove compute_confirm_resize and 

113 migration_get_unconfirmed_by_dest_compute 

114 * 1.57 - Remove migration_create() 

115 * 1.58 - Remove migration_get() 

116 

117 ... Havana supports message version 1.58. So, any changes to existing 

118 methods in 1.x after that point should be done such that they can 

119 handle the version_cap being set to 1.58. 

120 

121 * 1.59 - Remove instance_info_cache_update() 

122 * 1.60 - Remove aggregate_metadata_add() and aggregate_metadata_delete() 

123 * ... - Remove security_group_get_by_instance() and 

124 security_group_rule_get_by_security_group() 

125 * 1.61 - Return deleted instance from instance_destroy() 

126 * 1.62 - Added object_backport() 

127 * 1.63 - Changed the format of values['stats'] from a dict to a JSON string 

128 in compute_node_update() 

129 * 1.64 - Added use_slave to instance_get_all_filters() 

130 - Remove instance_type_get() 

131 - Remove aggregate_get() 

132 - Remove aggregate_get_by_host() 

133 - Remove instance_get() 

134 - Remove migration_update() 

135 - Remove block_device_mapping_destroy() 

136 

137 * 2.0 - Drop backwards compatibility 

138 - Remove quota_rollback() and quota_commit() 

139 - Remove aggregate_host_add() and aggregate_host_delete() 

140 - Remove network_migrate_instance_start() and 

141 network_migrate_instance_finish() 

142 - Remove vol_get_usage_by_time 

143 

144 ... Icehouse supports message version 2.0. So, any changes to 

145 existing methods in 2.x after that point should be done such 

146 that they can handle the version_cap being set to 2.0. 

147 

148 * Remove instance_destroy() 

149 * Remove compute_unrescue() 

150 * Remove instance_get_all_by_filters() 

151 * Remove instance_get_active_by_window_joined() 

152 * Remove instance_fault_create() 

153 * Remove action_event_start() and action_event_finish() 

154 * Remove instance_get_by_uuid() 

155 * Remove agent_build_get_by_triple() 

156 

157 ... Juno supports message version 2.0. So, any changes to 

158 existing methods in 2.x after that point should be done such 

159 that they can handle the version_cap being set to 2.0. 

160 

161 * 2.1 - Make notify_usage_exists() take an instance object 

162 * Remove bw_usage_update() 

163 * Remove notify_usage_exists() 

164 

165 ... Kilo supports message version 2.1. So, any changes to 

166 existing methods in 2.x after that point should be done such 

167 that they can handle the version_cap being set to 2.1. 

168 

169 * Remove get_ec2_ids() 

170 * Remove service_get_all_by() 

171 * Remove service_create() 

172 * Remove service_destroy() 

173 * Remove service_update() 

174 * Remove migration_get_in_progress_by_host_and_node() 

175 * Remove aggregate_metadata_get_by_host() 

176 * Remove block_device_mapping_update_or_create() 

177 * Remove block_device_mapping_get_all_by_instance() 

178 * Remove instance_get_all_by_host() 

179 * Remove compute_node_update() 

180 * Remove compute_node_delete() 

181 * Remove security_groups_trigger_handler() 

182 * Remove task_log_get() 

183 * Remove task_log_begin_task() 

184 * Remove task_log_end_task() 

185 * Remove security_groups_trigger_members_refresh() 

186 * Remove vol_usage_update() 

187 * Remove instance_update() 

188 

189 * 2.2 - Add object_backport_versions() 

190 * 2.3 - Add object_class_action_versions() 

191 * Remove compute_node_create() 

192 * Remove object_backport() 

193 

194 * 3.0 - Drop backwards compatibility 

195 

196 ... Liberty, Mitaka, Newton, and Ocata support message version 3.0. So, 

197 any changes to existing methods in 3.x after that point should be done such 

198 that they can handle the version_cap being set to 3.0. 

199 

200 * Remove provider_fw_rule_get_all() 

201 """ 

202 

203 VERSION_ALIASES = { 

204 'grizzly': '1.48', 

205 'havana': '1.58', 

206 'icehouse': '2.0', 

207 'juno': '2.0', 

208 'kilo': '2.1', 

209 'liberty': '3.0', 

210 'mitaka': '3.0', 

211 'newton': '3.0', 

212 'ocata': '3.0', 

213 } 

214 

215 def __init__(self): 

216 super(ConductorAPI, self).__init__() 

217 target = messaging.Target(topic=RPC_TOPIC, version='3.0') 

218 version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.conductor, 

219 CONF.upgrade_levels.conductor) 

220 serializer = objects_base.NovaObjectSerializer() 

221 self.client = rpc.get_client(target, 

222 version_cap=version_cap, 

223 serializer=serializer) 

224 

225 # TODO(hanlind): This method can be removed once oslo.versionedobjects 

226 # has been converted to use version_manifests in remotable_classmethod 

227 # operations, which will use the new class action handler. 

228 def object_class_action(self, context, objname, objmethod, objver, 

229 args, kwargs): 

230 versions = ovo_base.obj_tree_get_versions(objname) 

231 return self.object_class_action_versions(context, 

232 objname, 

233 objmethod, 

234 versions, 

235 args, kwargs) 

236 

237 def object_class_action_versions(self, context, objname, objmethod, 

238 object_versions, args, kwargs): 

239 cctxt = self.client.prepare() 

240 return cctxt.call(context, 'object_class_action_versions', 

241 objname=objname, objmethod=objmethod, 

242 object_versions=object_versions, 

243 args=args, kwargs=kwargs) 

244 

245 def object_action(self, context, objinst, objmethod, args, kwargs): 

246 cctxt = self.client.prepare() 

247 return cctxt.call(context, 'object_action', objinst=objinst, 

248 objmethod=objmethod, args=args, kwargs=kwargs) 

249 

250 def object_backport_versions(self, context, objinst, object_versions): 

251 cctxt = self.client.prepare() 

252 return cctxt.call(context, 'object_backport_versions', objinst=objinst, 

253 object_versions=object_versions) 

254 

255 

256@profiler.trace_cls("rpc") 

257class ComputeTaskAPI(object): 

258 """Client side of the conductor 'compute' namespaced RPC API 

259 

260 API version history: 

261 

262 1.0 - Initial version (empty). 

263 1.1 - Added unified migrate_server call. 

264 1.2 - Added build_instances 

265 1.3 - Added unshelve_instance 

266 1.4 - Added reservations to migrate_server. 

267 1.5 - Added the legacy_bdm parameter to build_instances 

268 1.6 - Made migrate_server use instance objects 

269 1.7 - Do not send block_device_mapping and legacy_bdm to build_instances 

270 1.8 - Add rebuild_instance 

271 1.9 - Converted requested_networks to NetworkRequestList object 

272 1.10 - Made migrate_server() and build_instances() send flavor objects 

273 1.11 - Added clean_shutdown to migrate_server() 

274 1.12 - Added request_spec to rebuild_instance() 

275 1.13 - Added request_spec to migrate_server() 

276 1.14 - Added request_spec to unshelve_instance() 

277 1.15 - Added live_migrate_instance 

278 1.16 - Added schedule_and_build_instances 

279 1.17 - Added tags to schedule_and_build_instances() 

280 1.18 - Added request_spec to build_instances(). 

281 1.19 - build_instances() now gets a 'host_lists' parameter that represents 

282 potential alternate hosts for retries within a cell for each 

283 instance. 

284 1.20 - migrate_server() now gets a 'host_list' parameter that represents 

285 potential alternate hosts for retries within a cell. 

286 1.21 - Added cache_images() 

287 1.22 - Added confirm_snapshot_based_resize() 

288 1.23 - Added revert_snapshot_based_resize() 

289 1.24 - Add reimage_boot_volume parameter to rebuild_instance() 

290 1.25 - Add target_state parameter to rebuild_instance() 

291 """ 

292 

293 def __init__(self): 

294 super(ComputeTaskAPI, self).__init__() 

295 target = messaging.Target(topic=RPC_TOPIC, 

296 namespace='compute_task', 

297 version='1.0') 

298 serializer = objects_base.NovaObjectSerializer() 

299 self.client = rpc.get_client(target, serializer=serializer) 

300 

301 def live_migrate_instance(self, context, instance, scheduler_hint, 

302 block_migration, disk_over_commit, request_spec): 

303 kw = {'instance': instance, 'scheduler_hint': scheduler_hint, 

304 'block_migration': block_migration, 

305 'disk_over_commit': disk_over_commit, 

306 'request_spec': request_spec, 

307 } 

308 version = '1.15' 

309 cctxt = self.client.prepare(version=version) 

310 cctxt.cast(context, 'live_migrate_instance', **kw) 

311 

312 # TODO(melwitt): Remove the reservations parameter in version 2.0 of the 

313 # RPC API. 

314 # TODO(mriedem): Make request_spec required *and* a RequestSpec object 

315 # rather than a legacy dict in version 2.0 of the RPC API. 

316 def migrate_server(self, context, instance, scheduler_hint, live, rebuild, 

317 flavor, block_migration, disk_over_commit, 

318 reservations=None, clean_shutdown=True, request_spec=None, 

319 host_list=None, do_cast=False): 

320 kw = {'instance': instance, 'scheduler_hint': scheduler_hint, 

321 'live': live, 'rebuild': rebuild, 'flavor': flavor, 

322 'block_migration': block_migration, 

323 'disk_over_commit': disk_over_commit, 

324 'reservations': reservations, 

325 'clean_shutdown': clean_shutdown, 

326 'request_spec': request_spec, 

327 'host_list': host_list, 

328 } 

329 version = '1.20' 

330 if not self.client.can_send_version(version): 330 ↛ 331line 330 didn't jump to line 331 because the condition on line 330 was never true

331 del kw['host_list'] 

332 version = '1.13' 

333 if not self.client.can_send_version(version): 333 ↛ 334line 333 didn't jump to line 334 because the condition on line 333 was never true

334 del kw['request_spec'] 

335 version = '1.11' 

336 if not self.client.can_send_version(version): 336 ↛ 337line 336 didn't jump to line 337 because the condition on line 336 was never true

337 del kw['clean_shutdown'] 

338 version = '1.10' 

339 if not self.client.can_send_version(version): 339 ↛ 340line 339 didn't jump to line 340 because the condition on line 339 was never true

340 kw['flavor'] = objects_base.obj_to_primitive(flavor) 

341 version = '1.6' 

342 if not self.client.can_send_version(version): 342 ↛ 343line 342 didn't jump to line 343 because the condition on line 342 was never true

343 kw['instance'] = jsonutils.to_primitive( 

344 objects_base.obj_to_primitive(instance)) 

345 version = '1.4' 

346 cctxt = self.client.prepare( 

347 version=version, 

348 call_monitor_timeout=CONF.rpc_response_timeout, 

349 timeout=CONF.long_rpc_timeout) 

350 if do_cast: 

351 return cctxt.cast(context, 'migrate_server', **kw) 

352 return cctxt.call(context, 'migrate_server', **kw) 

353 

354 def build_instances(self, context, instances, image, filter_properties, 

355 admin_password, injected_files, requested_networks, 

356 security_groups, block_device_mapping, legacy_bdm=True, 

357 request_spec=None, host_lists=None): 

358 image_p = jsonutils.to_primitive(image) 

359 kwargs = {"instances": instances, "image": image_p, 

360 "filter_properties": filter_properties, 

361 "admin_password": admin_password, 

362 "injected_files": injected_files, 

363 "requested_networks": requested_networks, 

364 "security_groups": security_groups, 

365 "request_spec": request_spec, 

366 "host_lists": host_lists} 

367 version = '1.19' 

368 if not self.client.can_send_version(version): 

369 version = '1.18' 

370 kwargs.pop("host_lists") 

371 if not self.client.can_send_version(version): 

372 version = '1.10' 

373 kwargs.pop("request_spec") 

374 if not self.client.can_send_version(version): 374 ↛ 375line 374 didn't jump to line 375 because the condition on line 374 was never true

375 version = '1.9' 

376 if 'instance_type' in filter_properties: 

377 flavor = filter_properties['instance_type'] 

378 flavor_p = objects_base.obj_to_primitive(flavor) 

379 kwargs["filter_properties"] = dict(filter_properties, 

380 instance_type=flavor_p) 

381 if not self.client.can_send_version(version): 381 ↛ 382line 381 didn't jump to line 382 because the condition on line 381 was never true

382 version = '1.8' 

383 nets = kwargs['requested_networks'].as_tuples() 

384 kwargs['requested_networks'] = nets 

385 if not self.client.can_send_version('1.7'): 385 ↛ 386line 385 didn't jump to line 386 because the condition on line 385 was never true

386 version = '1.5' 

387 bdm_p = objects_base.obj_to_primitive(block_device_mapping) 

388 kwargs.update({'block_device_mapping': bdm_p, 

389 'legacy_bdm': legacy_bdm}) 

390 

391 cctxt = self.client.prepare(version=version) 

392 cctxt.cast(context, 'build_instances', **kwargs) 

393 

394 def schedule_and_build_instances(self, context, build_requests, 

395 request_specs, 

396 image, admin_password, injected_files, 

397 requested_networks, 

398 block_device_mapping, 

399 tags=None): 

400 version = '1.17' 

401 kw = {'build_requests': build_requests, 

402 'request_specs': request_specs, 

403 'image': jsonutils.to_primitive(image), 

404 'admin_password': admin_password, 

405 'injected_files': injected_files, 

406 'requested_networks': requested_networks, 

407 'block_device_mapping': block_device_mapping, 

408 'tags': tags} 

409 

410 if not self.client.can_send_version(version): 

411 version = '1.16' 

412 del kw['tags'] 

413 

414 cctxt = self.client.prepare(version=version) 

415 cctxt.cast(context, 'schedule_and_build_instances', **kw) 

416 

417 def unshelve_instance(self, context, instance, request_spec=None): 

418 version = '1.14' 

419 kw = {'instance': instance, 

420 'request_spec': request_spec 

421 } 

422 if not self.client.can_send_version(version): 422 ↛ 423line 422 didn't jump to line 423 because the condition on line 422 was never true

423 version = '1.3' 

424 del kw['request_spec'] 

425 cctxt = self.client.prepare(version=version) 

426 cctxt.cast(context, 'unshelve_instance', **kw) 

427 

428 def rebuild_instance(self, ctxt, instance, new_pass, injected_files, 

429 image_ref, orig_image_ref, orig_sys_metadata, bdms, 

430 recreate=False, on_shared_storage=False, host=None, 

431 preserve_ephemeral=False, request_spec=None, 

432 reimage_boot_volume=False, target_state=None): 

433 version = '1.25' 

434 kw = {'instance': instance, 

435 'new_pass': new_pass, 

436 'injected_files': injected_files, 

437 'image_ref': image_ref, 

438 'orig_image_ref': orig_image_ref, 

439 'orig_sys_metadata': orig_sys_metadata, 

440 'bdms': bdms, 

441 'recreate': recreate, 

442 'on_shared_storage': on_shared_storage, 

443 'preserve_ephemeral': preserve_ephemeral, 

444 'host': host, 

445 'request_spec': request_spec, 

446 'reimage_boot_volume': reimage_boot_volume, 

447 'target_state': target_state, 

448 } 

449 if not self.client.can_send_version(version): 

450 if kw['target_state']: 

451 raise exception.UnsupportedRPCVersion( 

452 api="rebuild_instance", required="1.25") 

453 else: 

454 del kw['target_state'] 

455 version = '1.24' 

456 if not self.client.can_send_version(version): 

457 if kw['reimage_boot_volume']: 

458 raise exception.NovaException( 

459 'Conductor RPC version does not support ' 

460 'reimage_boot_volume parameter.') 

461 else: 

462 del kw['reimage_boot_volume'] 

463 version = '1.12' 

464 if not self.client.can_send_version(version): 

465 version = '1.8' 

466 del kw['request_spec'] 

467 cctxt = self.client.prepare(version=version) 

468 cctxt.cast(ctxt, 'rebuild_instance', **kw) 

469 

470 def cache_images(self, ctxt, aggregate, image_ids): 

471 version = '1.21' 

472 if not self.client.can_send_version(version): 

473 raise exception.NovaException('Conductor RPC version pin does not ' 

474 'allow cache_images() to be called') 

475 cctxt = self.client.prepare(version=version) 

476 cctxt.cast(ctxt, 'cache_images', aggregate=aggregate, 

477 image_ids=image_ids) 

478 

479 def confirm_snapshot_based_resize( 

480 self, ctxt, instance, migration, do_cast=True): 

481 version = '1.22' 

482 if not self.client.can_send_version(version): 

483 raise exception.ServiceTooOld(_('nova-conductor too old')) 

484 kw = {'instance': instance, 'migration': migration} 

485 cctxt = self.client.prepare(version=version) 

486 if do_cast: 

487 return cctxt.cast(ctxt, 'confirm_snapshot_based_resize', **kw) 

488 return cctxt.call(ctxt, 'confirm_snapshot_based_resize', **kw) 

489 

490 def revert_snapshot_based_resize(self, ctxt, instance, migration): 

491 version = '1.23' 

492 if not self.client.can_send_version(version): 492 ↛ 494line 492 didn't jump to line 494 because the condition on line 492 was always true

493 raise exception.ServiceTooOld(_('nova-conductor too old')) 

494 kw = {'instance': instance, 'migration': migration} 

495 cctxt = self.client.prepare(version=version) 

496 cctxt.cast(ctxt, 'revert_snapshot_based_resize', **kw)