Coverage for manila/compute/nova.py: 84%

104 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright 2014 Mirantis Inc. 

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""" 

16Handles all requests to Nova. 

17""" 

18 

19import functools 

20 

21from keystoneauth1 import loading as ks_loading 

22from novaclient import client as nova_client 

23from novaclient import exceptions as nova_exception 

24from novaclient import utils 

25from oslo_config import cfg 

26 

27from manila.common import client_auth 

28from manila.common.config import core_opts 

29from manila.db import base 

30from manila import exception 

31from manila.i18n import _ 

32 

33NOVA_GROUP = 'nova' 

34AUTH_OBJ = None 

35 

36 

37nova_opts = [ 

38 cfg.StrOpt('api_microversion', 

39 default='2.10', 

40 help='Version of Nova API to be used.'), 

41 cfg.StrOpt('endpoint_type', 

42 default='publicURL', 

43 choices=['publicURL', 'internalURL', 'adminURL', 

44 'public', 'internal', 'admin'], 

45 help='Endpoint type to be used with nova client calls.'), 

46 cfg.StrOpt('region_name', 

47 help='Region name for connecting to nova.'), 

48 ] 

49 

50CONF = cfg.CONF 

51CONF.register_opts(core_opts) 

52CONF.register_opts(nova_opts, NOVA_GROUP) 

53ks_loading.register_session_conf_options(CONF, 

54 NOVA_GROUP) 

55ks_loading.register_auth_conf_options(CONF, NOVA_GROUP) 

56 

57 

58def list_opts(): 

59 return client_auth.AuthClientLoader.list_opts(NOVA_GROUP) 

60 

61 

62def novaclient(context): 

63 global AUTH_OBJ 

64 if not AUTH_OBJ: 

65 AUTH_OBJ = client_auth.AuthClientLoader( 

66 client_class=nova_client.Client, cfg_group=NOVA_GROUP) 

67 return AUTH_OBJ.get_client(context, 

68 version=CONF[NOVA_GROUP].api_microversion, 

69 endpoint_type=CONF[NOVA_GROUP].endpoint_type, 

70 region_name=CONF[NOVA_GROUP].region_name) 

71 

72 

73def _untranslate_server_summary_view(server): 

74 """Maps keys for servers summary view.""" 

75 d = {} 

76 d['id'] = server.id 

77 d['status'] = server.status 

78 d['flavor'] = server.flavor.get('id') or server.flavor['original_name'] 

79 d['name'] = server.name 

80 d['image'] = server.image['id'] if server.image else None 

81 d['volume_attached'] = getattr( 

82 server, "os-extended-volumes:volumes_attached", []) 

83 d['created'] = server.created 

84 d['addresses'] = server.addresses 

85 d['networks'] = server.networks 

86 d['tenant_id'] = server.tenant_id 

87 d['user_id'] = server.user_id 

88 d['security_groups'] = getattr(server, 'security_groups', []) 

89 

90 return d 

91 

92 

93def _to_dict(obj): 

94 if isinstance(obj, dict): 

95 return obj 

96 elif hasattr(obj, 'to_dict'): 

97 return obj.to_dict() 

98 else: 

99 return obj.__dict__ 

100 

101 

102def translate_server_exception(method): 

103 """Transforms the exception for the instance. 

104 

105 Note: keeps its traceback intact. 

106 """ 

107 

108 @functools.wraps(method) 

109 def wrapper(self, ctx, instance_id, *args, **kwargs): 

110 try: 

111 res = method(self, ctx, instance_id, *args, **kwargs) 

112 return res 

113 except nova_exception.ClientException as e: 

114 if isinstance(e, nova_exception.NotFound): 

115 raise exception.InstanceNotFound(instance_id=instance_id) 

116 elif isinstance(e, nova_exception.BadRequest): 

117 raise exception.InvalidInput(reason=str(e)) 

118 else: 

119 raise exception.ManilaException(e) 

120 

121 return wrapper 

122 

123 

124class API(base.Base): 

125 """API for interacting with novaclient.""" 

126 

127 def server_create(self, context, name, image, flavor, key_name=None, 

128 user_data=None, security_groups=None, 

129 block_device_mapping=None, 

130 block_device_mapping_v2=None, nics=None, 

131 availability_zone=None, instance_count=1, 

132 admin_pass=None, meta=None): 

133 return _untranslate_server_summary_view( 

134 novaclient(context).servers.create( 

135 name, image, flavor, userdata=user_data, 

136 security_groups=security_groups, key_name=key_name, 

137 block_device_mapping=block_device_mapping, 

138 block_device_mapping_v2=block_device_mapping_v2, 

139 nics=nics, availability_zone=availability_zone, 

140 min_count=instance_count, admin_pass=admin_pass, 

141 meta=meta) 

142 ) 

143 

144 def server_delete(self, context, instance): 

145 novaclient(context).servers.delete(instance) 

146 

147 @translate_server_exception 

148 def server_get(self, context, instance_id): 

149 return _untranslate_server_summary_view( 

150 novaclient(context).servers.get(instance_id) 

151 ) 

152 

153 def server_get_by_name_or_id(self, context, instance_name_or_id): 

154 try: 

155 server = utils.find_resource( 

156 novaclient(context).servers, instance_name_or_id) 

157 except nova_exception.CommandError: 

158 # we did not find the server in the current tenant, 

159 # and proceed searching in all tenants 

160 try: 

161 server = utils.find_resource( 

162 novaclient(context).servers, instance_name_or_id, 

163 all_tenants=True) 

164 except nova_exception.CommandError as e: 

165 msg = _("Failed to get Nova VM. %s") % e 

166 raise exception.ManilaException(msg) 

167 return _untranslate_server_summary_view(server) 

168 

169 @translate_server_exception 

170 def server_reboot(self, context, instance_id, soft_reboot=False): 

171 hardness = 'SOFT' if soft_reboot else 'HARD' 

172 novaclient(context).servers.reboot(instance_id, hardness) 

173 

174 @translate_server_exception 

175 def instance_volume_attach(self, context, instance_id, volume_id, 

176 device=None): 

177 if device == 'auto': 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true

178 device = None 

179 return novaclient(context).volumes.create_server_volume(instance_id, 

180 volume_id, 

181 device) 

182 

183 @translate_server_exception 

184 def instance_volume_detach(self, context, instance_id, att_id): 

185 return novaclient(context).volumes.delete_server_volume(instance_id, 

186 att_id) 

187 

188 @translate_server_exception 

189 def instance_volumes_list(self, context, instance_id): 

190 

191 volumes = novaclient(context).volumes.get_server_volumes(instance_id) 

192 

193 # NOTE(pas-ha): Nova API 2.89 dropped 'id' field of the volume object, 

194 # so we use 'volumeId' field that is present in all API versions. 

195 return [volume.volumeId for volume in volumes] 

196 

197 @translate_server_exception 

198 def server_update(self, context, instance_id, name): 

199 return _untranslate_server_summary_view( 

200 novaclient(context).servers.update(instance_id, name=name) 

201 ) 

202 

203 def keypair_import(self, context, name, public_key): 

204 return novaclient(context).keypairs.create(name, public_key) 

205 

206 def keypair_delete(self, context, keypair_id): 

207 novaclient(context).keypairs.delete(keypair_id) 

208 

209 def keypair_list(self, context): 

210 return novaclient(context).keypairs.list() 

211 

212 def add_security_group_to_server(self, context, server, security_group): 

213 return novaclient(context).servers.add_security_group(server, 

214 security_group)