1#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2#    not use this file except in compliance with the License. You may obtain
3#    a copy of the License at
4#
5#         http://www.apache.org/licenses/LICENSE-2.0
6#
7#    Unless required by applicable law or agreed to in writing, software
8#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10#    License for the specific language governing permissions and limitations
11#    under the License.
12
13"""
14This module is a special module to define functions or other resources
15which need to be imported outside of openstack_dashboard.api.nova
16(like cinder.py) to avoid cyclic imports.
17"""
18
19from django.conf import settings
20from glanceclient import exc as glance_exceptions
21from novaclient import api_versions
22from novaclient import client as nova_client
23
24from horizon import exceptions as horizon_exceptions
25from horizon.utils import memoized
26
27from openstack_dashboard.api import base
28from openstack_dashboard.api import glance
29from openstack_dashboard.api import microversions
30from openstack_dashboard.contrib.developer.profiler import api as profiler
31
32
33# Supported compute versions
34VERSIONS = base.APIVersionManager("compute", preferred_version=2)
35VERSIONS.load_supported_version(1.1, {"client": nova_client, "version": 1.1})
36VERSIONS.load_supported_version(2, {"client": nova_client, "version": 2})
37
38INSECURE = settings.OPENSTACK_SSL_NO_VERIFY
39CACERT = settings.OPENSTACK_SSL_CACERT
40
41
42class Server(base.APIResourceWrapper):
43    """Simple wrapper around novaclient.server.Server.
44
45    Preserves the request info so image name can later be retrieved.
46    """
47    _attrs = ['addresses', 'attrs', 'id', 'image', 'links', 'description',
48              'metadata', 'name', 'private_ip', 'public_ip', 'status', 'uuid',
49              'image_name', 'VirtualInterfaces', 'flavor', 'key_name', 'fault',
50              'tenant_id', 'user_id', 'created', 'locked',
51              'OS-EXT-STS:power_state', 'OS-EXT-STS:task_state',
52              'OS-EXT-SRV-ATTR:instance_name', 'OS-EXT-SRV-ATTR:host',
53              'OS-EXT-SRV-ATTR:hostname', 'OS-EXT-SRV-ATTR:kernel_id',
54              'OS-EXT-SRV-ATTR:ramdisk_id', 'OS-EXT-SRV-ATTR:root_device_name',
55              'OS-EXT-SRV-ATTR:root_device_name', 'OS-EXT-SRV-ATTR:user_data',
56              'OS-EXT-SRV-ATTR:reservation_id', 'OS-EXT-SRV-ATTR:launch_index',
57              'OS-EXT-AZ:availability_zone', 'OS-DCF:diskConfig']
58
59    def __init__(self, apiresource, request):
60        super().__init__(apiresource)
61        self.request = request
62
63    # TODO(gabriel): deprecate making a call to Glance as a fallback.
64    @property
65    def image_name(self):
66        if not self.image:
67            return None
68        if hasattr(self.image, 'name'):
69            return self.image.name
70        if 'name' in self.image:
71            return self.image['name']
72        try:
73            image = glance.image_get(self.request, self.image['id'])
74            self.image['name'] = image.name
75            return image.name
76        except (glance_exceptions.ClientException,
77                horizon_exceptions.ServiceCatalogException):
78            self.image['name'] = None
79            return None
80
81    @property
82    def availability_zone(self):
83        return getattr(self, 'OS-EXT-AZ:availability_zone', "")
84
85    @property
86    def has_extended_attrs(self):
87        return any(getattr(self, attr, None) for attr in [
88            'OS-EXT-SRV-ATTR:instance_name', 'OS-EXT-SRV-ATTR:host',
89            'OS-EXT-SRV-ATTR:hostname', 'OS-EXT-SRV-ATTR:kernel_id',
90            'OS-EXT-SRV-ATTR:ramdisk_id', 'OS-EXT-SRV-ATTR:root_device_name',
91            'OS-EXT-SRV-ATTR:root_device_name', 'OS-EXT-SRV-ATTR:user_data',
92            'OS-EXT-SRV-ATTR:reservation_id', 'OS-EXT-SRV-ATTR:launch_index',
93        ])
94
95    @property
96    def internal_name(self):
97        return getattr(self, 'OS-EXT-SRV-ATTR:instance_name', "")
98
99    @property
100    def host_server(self):
101        return getattr(self, 'OS-EXT-SRV-ATTR:host', "")
102
103    @property
104    def instance_name(self):
105        return getattr(self, 'OS-EXT-SRV-ATTR:instance_name', "")
106
107    @property
108    def reservation_id(self):
109        return getattr(self, 'OS-EXT-SRV-ATTR:reservation_id', "")
110
111    @property
112    def launch_index(self):
113        return getattr(self, 'OS-EXT-SRV-ATTR:launch_index', "")
114
115    @property
116    def hostname(self):
117        return getattr(self, 'OS-EXT-SRV-ATTR:hostname', "")
118
119    @property
120    def kernel_id(self):
121        return getattr(self, 'OS-EXT-SRV-ATTR:kernel_id', "")
122
123    @property
124    def ramdisk_id(self):
125        return getattr(self, 'OS-EXT-SRV-ATTR:ramdisk_id', "")
126
127    @property
128    def root_device_name(self):
129        return getattr(self, 'OS-EXT-SRV-ATTR:root_device_name', "")
130
131    @property
132    def user_data(self):
133        return getattr(self, 'OS-EXT-SRV-ATTR:user_data', "")
134
135
136@memoized.memoized
137def get_microversion(request, features):
138    client = novaclient(request)
139    min_ver, max_ver = api_versions._get_server_version_range(client)
140    return (microversions.get_microversion_for_features(
141        'nova', features, api_versions.APIVersion, min_ver, max_ver))
142
143
144def get_auth_params_from_request(request):
145    """Extracts properties needed by novaclient call from the request object.
146
147    These will be used to memoize the calls to novaclient.
148    """
149    return (
150        request.user.username,
151        request.user.token.id,
152        request.user.tenant_id,
153        request.user.token.project.get('domain_id'),
154        base.url_for(request, 'compute'),
155        base.url_for(request, 'identity')
156    )
157
158
159@memoized.memoized
160def cached_novaclient(request, version=None):
161    (
162        username,
163        token_id,
164        project_id,
165        project_domain_id,
166        nova_url,
167        auth_url
168    ) = get_auth_params_from_request(request)
169    if version is None:
170        version = VERSIONS.get_active_version()['version']
171    c = nova_client.Client(version,
172                           username,
173                           token_id,
174                           project_id=project_id,
175                           project_domain_id=project_domain_id,
176                           auth_url=auth_url,
177                           insecure=INSECURE,
178                           cacert=CACERT,
179                           http_log_debug=settings.DEBUG,
180                           auth_token=token_id,
181                           endpoint_override=nova_url)
182    return c
183
184
185def novaclient(request, version=None):
186    if isinstance(version, api_versions.APIVersion):
187        version = version.get_string()
188    return cached_novaclient(request, version)
189
190
191def get_novaclient_with_instance_desc(request):
192    microversion = get_microversion(request, "instance_description")
193    return novaclient(request, version=microversion)
194
195
196@profiler.trace
197def server_get(request, instance_id):
198    return Server(get_novaclient_with_instance_desc(request).servers.get(
199        instance_id), request)
200