Coverage for nova/conductor/api.py: 92%

57 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +0000

1# Copyright 2012 IBM Corp. 

2# 

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

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

5# a copy of the License at 

6# 

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

8# 

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

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

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

12# License for the specific language governing permissions and limitations 

13# under the License. 

14 

15"""Handles all requests to the conductor service.""" 

16 

17from oslo_log import log as logging 

18import oslo_messaging as messaging 

19 

20from nova import baserpc 

21from nova.conductor import rpcapi 

22import nova.conf 

23from nova.image import glance 

24 

25CONF = nova.conf.CONF 

26 

27LOG = logging.getLogger(__name__) 

28 

29 

30class API(object): 

31 """Conductor API that does updates via RPC to the ConductorManager.""" 

32 

33 def __init__(self): 

34 self.conductor_rpcapi = rpcapi.ConductorAPI() 

35 self.base_rpcapi = baserpc.BaseAPI(topic=rpcapi.RPC_TOPIC) 

36 

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

38 return self.conductor_rpcapi.object_backport_versions(context, objinst, 

39 object_versions) 

40 

41 def wait_until_ready(self, context, early_timeout=10, early_attempts=10): 

42 '''Wait until a conductor service is up and running. 

43 

44 This method calls the remote ping() method on the conductor topic until 

45 it gets a response. It starts with a shorter timeout in the loop 

46 (early_timeout) up to early_attempts number of tries. It then drops 

47 back to the globally configured timeout for rpc calls for each retry. 

48 ''' 

49 attempt = 0 

50 timeout = early_timeout 

51 # if we show the timeout message, make sure we show a similar 

52 # message saying that everything is now working to avoid 

53 # confusion 

54 has_timedout = False 

55 while True: 

56 # NOTE(danms): Try ten times with a short timeout, and then punt 

57 # to the configured RPC timeout after that 

58 if attempt == early_attempts: 

59 timeout = None 

60 attempt += 1 

61 

62 # NOTE(russellb): This is running during service startup. If we 

63 # allow an exception to be raised, the service will shut down. 

64 # This may fail the first time around if nova-conductor wasn't 

65 # running when this service started. 

66 try: 

67 self.base_rpcapi.ping(context, '1.21 GigaWatts', 

68 timeout=timeout) 

69 if has_timedout: 69 ↛ 72line 69 didn't jump to line 72 because the condition on line 69 was always true

70 LOG.info('nova-conductor connection ' 

71 'established successfully') 

72 break 

73 except messaging.MessagingTimeout: 

74 has_timedout = True 

75 LOG.warning('Timed out waiting for nova-conductor. ' 

76 'Is it running? Or did this service start ' 

77 'before nova-conductor? ' 

78 'Reattempting establishment of ' 

79 'nova-conductor connection...') 

80 

81 

82class ComputeTaskAPI(object): 

83 """ComputeTask API that queues up compute tasks for nova-conductor.""" 

84 

85 def __init__(self): 

86 self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI() 

87 self.image_api = glance.API() 

88 

89 # TODO(stephenfin): Remove the 'reservations' parameter since we don't use 

90 # reservations anymore 

91 def resize_instance(self, context, instance, scheduler_hint, flavor, 

92 reservations=None, clean_shutdown=True, 

93 request_spec=None, host_list=None, do_cast=False): 

94 self.conductor_compute_rpcapi.migrate_server( 

95 context, instance, scheduler_hint, live=False, rebuild=False, 

96 flavor=flavor, block_migration=None, disk_over_commit=None, 

97 reservations=reservations, clean_shutdown=clean_shutdown, 

98 request_spec=request_spec, host_list=host_list, 

99 do_cast=do_cast) 

100 

101 def live_migrate_instance(self, context, instance, host_name, 

102 block_migration, disk_over_commit, 

103 request_spec=None, async_=False): 

104 scheduler_hint = {'host': host_name} 

105 if async_: 105 ↛ 106line 105 didn't jump to line 106 because the condition on line 105 was never true

106 self.conductor_compute_rpcapi.live_migrate_instance( 

107 context, instance, scheduler_hint, block_migration, 

108 disk_over_commit, request_spec) 

109 else: 

110 self.conductor_compute_rpcapi.migrate_server( 

111 context, instance, scheduler_hint, True, False, None, 

112 block_migration, disk_over_commit, None, 

113 request_spec=request_spec) 

114 

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

116 admin_password, injected_files, requested_networks, 

117 security_groups, block_device_mapping, legacy_bdm=True, 

118 request_spec=None, host_lists=None): 

119 self.conductor_compute_rpcapi.build_instances(context, 

120 instances=instances, image=image, 

121 filter_properties=filter_properties, 

122 admin_password=admin_password, injected_files=injected_files, 

123 requested_networks=requested_networks, 

124 security_groups=security_groups, 

125 block_device_mapping=block_device_mapping, 

126 legacy_bdm=legacy_bdm, request_spec=request_spec, 

127 host_lists=host_lists) 

128 

129 def schedule_and_build_instances(self, context, build_requests, 

130 request_spec, image, 

131 admin_password, injected_files, 

132 requested_networks, block_device_mapping, 

133 tags=None): 

134 self.conductor_compute_rpcapi.schedule_and_build_instances( 

135 context, build_requests, request_spec, image, 

136 admin_password, injected_files, requested_networks, 

137 block_device_mapping, tags) 

138 

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

140 self.conductor_compute_rpcapi.unshelve_instance(context, 

141 instance=instance, request_spec=request_spec) 

142 

143 def rebuild_instance(self, context, instance, orig_image_ref, image_ref, 

144 injected_files, new_pass, orig_sys_metadata, 

145 bdms, recreate=False, on_shared_storage=False, 

146 preserve_ephemeral=False, host=None, 

147 request_spec=None, reimage_boot_volume=False, 

148 target_state=None): 

149 self.conductor_compute_rpcapi.rebuild_instance(context, 

150 instance=instance, 

151 new_pass=new_pass, 

152 injected_files=injected_files, 

153 image_ref=image_ref, 

154 orig_image_ref=orig_image_ref, 

155 orig_sys_metadata=orig_sys_metadata, 

156 bdms=bdms, 

157 recreate=recreate, 

158 on_shared_storage=on_shared_storage, 

159 preserve_ephemeral=preserve_ephemeral, 

160 host=host, 

161 request_spec=request_spec, 

162 reimage_boot_volume=reimage_boot_volume, 

163 target_state=target_state) 

164 

165 def cache_images(self, context, aggregate, image_ids): 

166 """Request images be pre-cached on hosts within an aggregate. 

167 

168 :param context: The RequestContext 

169 :param aggregate: The objects.Aggregate representing the hosts to 

170 contact 

171 :param image_ids: A list of image ID strings to send to the hosts 

172 """ 

173 for image_id in image_ids: 

174 # Validate that we can get the image by id before we go 

175 # ask a bunch of hosts to do the same. We let this bubble 

176 # up to the API, which catches NovaException for the 4xx and 

177 # otherwise 500s if this fails in some unexpected way. 

178 self.image_api.get(context, image_id) 

179 self.conductor_compute_rpcapi.cache_images(context, aggregate, 

180 image_ids) 

181 

182 def confirm_snapshot_based_resize( 

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

184 self.conductor_compute_rpcapi.confirm_snapshot_based_resize( 

185 ctxt, instance, migration, do_cast=do_cast) 

186 

187 def revert_snapshot_based_resize( 

188 self, ctxt, instance, migration): 

189 self.conductor_compute_rpcapi.revert_snapshot_based_resize( 

190 ctxt, instance, migration)