OpenStack Dashboard (Horizon)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

views.py 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. # Copyright 2012 United States Government as represented by the
  2. # Administrator of the National Aeronautics and Space Administration.
  3. # All Rights Reserved.
  4. #
  5. # Copyright 2012 OpenStack Foundation
  6. # Copyright 2012 Nebula, Inc.
  7. #
  8. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  9. # not use this file except in compliance with the License. You may obtain
  10. # a copy of the License at
  11. #
  12. # http://www.apache.org/licenses/LICENSE-2.0
  13. #
  14. # Unless required by applicable law or agreed to in writing, software
  15. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  16. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  17. # License for the specific language governing permissions and limitations
  18. # under the License.
  19. from django.conf import settings
  20. from django.urls import reverse
  21. from django.urls import reverse_lazy
  22. from django.utils.translation import ugettext_lazy as _
  23. from horizon import exceptions
  24. from horizon import forms
  25. from horizon import tables
  26. from horizon.utils import memoized
  27. from openstack_dashboard import api
  28. from openstack_dashboard.dashboards.admin.instances \
  29. import forms as project_forms
  30. from openstack_dashboard.dashboards.admin.instances \
  31. import tables as project_tables
  32. from openstack_dashboard.dashboards.admin.instances import tabs
  33. from openstack_dashboard.dashboards.project.instances import views
  34. from openstack_dashboard.dashboards.project.instances.workflows \
  35. import update_instance
  36. from openstack_dashboard.utils import futurist_utils
  37. # re-use console from project.instances.views to make reflection work
  38. def console(args, **kvargs):
  39. return views.console(args, **kvargs)
  40. # re-use vnc from project.instances.views to make reflection work
  41. def vnc(args, **kvargs):
  42. return views.vnc(args, **kvargs)
  43. # re-use spice from project.instances.views to make reflection work
  44. def spice(args, **kvargs):
  45. return views.spice(args, **kvargs)
  46. # re-use rdp from project.instances.views to make reflection work
  47. def rdp(args, **kvargs):
  48. return views.rdp(args, **kvargs)
  49. # re-use mks from project.instances.views to make reflection work
  50. def mks(args, **kvargs):
  51. return views.mks(args, **kvargs)
  52. class AdminUpdateView(views.UpdateView):
  53. workflow_class = update_instance.AdminUpdateInstance
  54. success_url = reverse_lazy("horizon:admin:instances:index")
  55. class AdminIndexView(tables.PagedTableMixin, tables.DataTableView):
  56. table_class = project_tables.AdminInstancesTable
  57. page_title = _("Instances")
  58. def has_prev_data(self, table):
  59. return getattr(self, "_prev", False)
  60. def has_more_data(self, table):
  61. return self._more
  62. def needs_filter_first(self, table):
  63. return self._needs_filter_first
  64. def _get_tenants(self):
  65. # Gather our tenants to correlate against IDs
  66. try:
  67. tenants, __ = api.keystone.tenant_list(self.request)
  68. return dict([(t.id, t) for t in tenants])
  69. except Exception:
  70. msg = _('Unable to retrieve instance project information.')
  71. exceptions.handle(self.request, msg)
  72. return {}
  73. def _get_images(self, instances=()):
  74. # Gather our images to correlate our instances to them
  75. try:
  76. # NOTE(aarefiev): request images, instances was booted from.
  77. img_ids = (instance.image.get('id') for instance in
  78. instances if isinstance(instance.image, dict))
  79. real_img_ids = list(filter(None, img_ids))
  80. images = api.glance.image_list_detailed_by_ids(
  81. self.request, real_img_ids)
  82. image_map = dict((image.id, image) for image in images)
  83. return image_map
  84. except Exception:
  85. exceptions.handle(self.request, ignore=True)
  86. return {}
  87. def _get_flavors(self):
  88. # Gather our flavors to correlate against IDs
  89. try:
  90. flavors = api.nova.flavor_list(self.request)
  91. return dict([(str(flavor.id), flavor) for flavor in flavors])
  92. except Exception:
  93. msg = _("Unable to retrieve flavor list.")
  94. exceptions.handle(self.request, msg)
  95. return {}
  96. def _get_instances(self, search_opts, sort_dir):
  97. try:
  98. instances, self._more, self._prev = api.nova.server_list_paged(
  99. self.request,
  100. search_opts=search_opts,
  101. sort_dir=sort_dir)
  102. except Exception:
  103. self._more = self._prev = False
  104. instances = []
  105. exceptions.handle(self.request,
  106. _('Unable to retrieve instance list.'))
  107. return instances
  108. def get_data(self):
  109. marker, sort_dir = self._get_marker()
  110. default_search_opts = {'marker': marker,
  111. 'paginate': True,
  112. 'all_tenants': True}
  113. search_opts = self.get_filters(default_search_opts.copy())
  114. # If filter_first is set and if there are not other filters
  115. # selected, then search criteria must be provided and return an empty
  116. # list
  117. filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
  118. if (filter_first.get('admin.instances', False) and
  119. len(search_opts) == len(default_search_opts)):
  120. self._needs_filter_first = True
  121. self._more = False
  122. return []
  123. self._needs_filter_first = False
  124. instances = self._get_instances(search_opts, sort_dir)
  125. results = futurist_utils.call_functions_parallel(
  126. (self._get_images, [tuple(instances)]),
  127. self._get_flavors,
  128. self._get_tenants)
  129. image_dict, flavor_dict, tenant_dict = results
  130. non_api_filter_info = (
  131. ('project', 'tenant_id', tenant_dict.values()),
  132. ('image_name', 'image', image_dict.values()),
  133. ('flavor_name', 'flavor', flavor_dict.values()),
  134. )
  135. if not views.process_non_api_filters(search_opts, non_api_filter_info):
  136. self._more = False
  137. return []
  138. # Loop through instances to get image, flavor and tenant info.
  139. for inst in instances:
  140. if hasattr(inst, 'image') and isinstance(inst.image, dict):
  141. image_id = inst.image.get('id')
  142. if image_id in image_dict:
  143. inst.image = image_dict[image_id]
  144. # In case image not found in image_map, set name to empty
  145. # to avoid fallback API call to Glance in api/nova.py
  146. # until the call is deprecated in api itself
  147. else:
  148. inst.image['name'] = _("-")
  149. flavor_id = inst.flavor["id"]
  150. try:
  151. if flavor_id in flavor_dict:
  152. inst.full_flavor = flavor_dict[flavor_id]
  153. else:
  154. # If the flavor_id is not in flavor_dict list,
  155. # gets it via nova api.
  156. inst.full_flavor = api.nova.flavor_get(
  157. self.request, flavor_id)
  158. except Exception:
  159. msg = _('Unable to retrieve instance size information.')
  160. exceptions.handle(self.request, msg)
  161. tenant = tenant_dict.get(inst.tenant_id, None)
  162. inst.tenant_name = getattr(tenant, "name", None)
  163. return instances
  164. class LiveMigrateView(forms.ModalFormView):
  165. form_class = project_forms.LiveMigrateForm
  166. template_name = 'admin/instances/live_migrate.html'
  167. context_object_name = 'instance'
  168. success_url = reverse_lazy("horizon:admin:instances:index")
  169. page_title = _("Live Migrate")
  170. success_label = page_title
  171. def get_context_data(self, **kwargs):
  172. context = super(LiveMigrateView, self).get_context_data(**kwargs)
  173. context["instance_id"] = self.kwargs['instance_id']
  174. return context
  175. @memoized.memoized_method
  176. def get_hosts(self, *args, **kwargs):
  177. try:
  178. services = api.nova.service_list(self.request,
  179. binary='nova-compute')
  180. return [s.host for s in services]
  181. except Exception:
  182. redirect = reverse("horizon:admin:instances:index")
  183. msg = _('Unable to retrieve host information.')
  184. exceptions.handle(self.request, msg, redirect=redirect)
  185. @memoized.memoized_method
  186. def get_object(self, *args, **kwargs):
  187. instance_id = self.kwargs['instance_id']
  188. try:
  189. return api.nova.server_get(self.request, instance_id)
  190. except Exception:
  191. redirect = reverse("horizon:admin:instances:index")
  192. msg = _('Unable to retrieve instance details.')
  193. exceptions.handle(self.request, msg, redirect=redirect)
  194. def get_initial(self):
  195. initial = super(LiveMigrateView, self).get_initial()
  196. _object = self.get_object()
  197. if _object:
  198. current_host = getattr(_object, 'OS-EXT-SRV-ATTR:host', '')
  199. initial.update({'instance_id': self.kwargs['instance_id'],
  200. 'current_host': current_host,
  201. 'hosts': self.get_hosts()})
  202. return initial
  203. class DetailView(views.DetailView):
  204. tab_group_class = tabs.AdminInstanceDetailTabs
  205. redirect_url = 'horizon:admin:instances:index'
  206. image_url = 'horizon:admin:images:detail'
  207. volume_url = 'horizon:admin:volumes:detail'
  208. def _get_actions(self, instance):
  209. table = project_tables.AdminInstancesTable(self.request)
  210. return table.render_row_actions(instance)
  211. class RescueView(views.RescueView):
  212. form_class = project_forms.RescueInstanceForm
  213. submit_url = "horizon:admin:instances:rescue"
  214. success_url = reverse_lazy('horizon:admin:instances:index')
  215. template_name = 'admin/instances/rescue.html'
  216. def get_initial(self):
  217. return {'instance_id': self.kwargs["instance_id"]}