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
« 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.
15"""
16Handles all requests to Nova.
17"""
19import functools
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
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 _
33NOVA_GROUP = 'nova'
34AUTH_OBJ = None
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 ]
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)
58def list_opts():
59 return client_auth.AuthClientLoader.list_opts(NOVA_GROUP)
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)
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', [])
90 return d
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__
102def translate_server_exception(method):
103 """Transforms the exception for the instance.
105 Note: keeps its traceback intact.
106 """
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)
121 return wrapper
124class API(base.Base):
125 """API for interacting with novaclient."""
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 )
144 def server_delete(self, context, instance):
145 novaclient(context).servers.delete(instance)
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 )
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)
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)
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)
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)
188 @translate_server_exception
189 def instance_volumes_list(self, context, instance_id):
191 volumes = novaclient(context).volumes.get_server_volumes(instance_id)
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]
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 )
203 def keypair_import(self, context, name, public_key):
204 return novaclient(context).keypairs.create(name, public_key)
206 def keypair_delete(self, context, keypair_id):
207 novaclient(context).keypairs.delete(keypair_id)
209 def keypair_list(self, context):
210 return novaclient(context).keypairs.list()
212 def add_security_group_to_server(self, context, server, security_group):
213 return novaclient(context).servers.add_security_group(server,
214 security_group)