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.

tables.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. from django.core import urlresolvers
  13. from django.http import Http404
  14. from django.template.defaultfilters import title
  15. from django.utils.translation import pgettext_lazy
  16. from django.utils.translation import ugettext_lazy as _
  17. from django.utils.translation import ungettext_lazy
  18. from heatclient import exc
  19. from horizon import exceptions
  20. from horizon import messages
  21. from horizon import tables
  22. from horizon.utils import filters
  23. from openstack_dashboard import api
  24. from openstack_dashboard.dashboards.project.stacks import mappings
  25. class LaunchStack(tables.LinkAction):
  26. name = "launch"
  27. verbose_name = _("Launch Stack")
  28. url = "horizon:project:stacks:select_template"
  29. classes = ("ajax-modal",)
  30. icon = "plus"
  31. policy_rules = (("orchestration", "stacks:validate_template"),
  32. ("orchestration", "stacks:create"),)
  33. class PreviewStack(tables.LinkAction):
  34. name = "preview"
  35. verbose_name = _("Preview Stack")
  36. url = "horizon:project:stacks:preview_template"
  37. classes = ("ajax-modal",)
  38. icon = "eye"
  39. policy_rules = (("orchestration", "stacks:validate_template"),
  40. ("orchestration", "stacks:preview"),)
  41. class CheckStack(tables.BatchAction):
  42. name = "check"
  43. verbose_name = _("Check Stack")
  44. policy_rules = (("orchestration", "actions:action"),)
  45. icon = "check-square"
  46. @staticmethod
  47. def action_present(count):
  48. return ungettext_lazy(
  49. u"Check Stack",
  50. u"Check Stacks",
  51. count
  52. )
  53. @staticmethod
  54. def action_past(count):
  55. return ungettext_lazy(
  56. u"Checked Stack",
  57. u"Checked Stacks",
  58. count
  59. )
  60. def action(self, request, stack_id):
  61. api.heat.action_check(request, stack_id)
  62. class SuspendStack(tables.BatchAction):
  63. name = "suspend"
  64. verbose_name = _("Suspend Stack")
  65. policy_rules = (("orchestration", "actions:action"),)
  66. icon = "pause"
  67. @staticmethod
  68. def action_present(count):
  69. return ungettext_lazy(
  70. u"Suspend Stack",
  71. u"Suspend Stacks",
  72. count
  73. )
  74. @staticmethod
  75. def action_past(count):
  76. return ungettext_lazy(
  77. u"Suspended Stack",
  78. u"Suspended Stacks",
  79. count
  80. )
  81. def action(self, request, stack_id):
  82. try:
  83. api.heat.action_suspend(request, stack_id)
  84. except Exception:
  85. msg = _('Failed to suspend stack.')
  86. exceptions.handle(request, msg)
  87. class ResumeStack(tables.BatchAction):
  88. name = "resume"
  89. verbose_name = _("Resume Stack")
  90. policy_rules = (("orchestration", "actions:action"),)
  91. icon = "play"
  92. @staticmethod
  93. def action_present(count):
  94. return ungettext_lazy(
  95. u"Resume Stack",
  96. u"Resume Stacks",
  97. count
  98. )
  99. @staticmethod
  100. def action_past(count):
  101. return ungettext_lazy(
  102. u"Resumed Stack",
  103. u"Resumed Stacks",
  104. count
  105. )
  106. def action(self, request, stack_id):
  107. try:
  108. api.heat.action_resume(request, stack_id)
  109. except Exception:
  110. msg = _('Failed to resume stack.')
  111. exceptions.handle(request, msg)
  112. class ChangeStackTemplate(tables.LinkAction):
  113. name = "edit"
  114. verbose_name = _("Change Stack Template")
  115. url = "horizon:project:stacks:change_template"
  116. classes = ("ajax-modal",)
  117. icon = "pencil"
  118. def get_link_url(self, stack):
  119. return urlresolvers.reverse(self.url, args=[stack.id])
  120. class DeleteStack(tables.DeleteAction):
  121. @staticmethod
  122. def action_present(count):
  123. return ungettext_lazy(
  124. u"Delete Stack",
  125. u"Delete Stacks",
  126. count
  127. )
  128. @staticmethod
  129. def action_past(count):
  130. return ungettext_lazy(
  131. u"Deleted Stack",
  132. u"Deleted Stacks",
  133. count
  134. )
  135. policy_rules = (("orchestration", "stacks:delete"),)
  136. def delete(self, request, stack_id):
  137. try:
  138. api.heat.stack_delete(request, stack_id)
  139. except Exception:
  140. msg = _('Failed to delete stack.')
  141. exceptions.handle(request, msg)
  142. def allowed(self, request, stack):
  143. if stack is not None:
  144. return stack.stack_status != 'DELETE_COMPLETE'
  145. return True
  146. class StacksUpdateRow(tables.Row):
  147. ajax = True
  148. def can_be_selected(self, datum):
  149. return datum.stack_status != 'DELETE_COMPLETE'
  150. def get_data(self, request, stack_id):
  151. try:
  152. stack = api.heat.stack_get(request, stack_id)
  153. if stack.stack_status == 'DELETE_COMPLETE':
  154. # returning 404 to the ajax call removes the
  155. # row from the table on the ui
  156. raise Http404
  157. return stack
  158. except Http404:
  159. raise
  160. except Exception as e:
  161. messages.error(request, e)
  162. raise
  163. class StacksFilterAction(tables.FilterAction):
  164. filter_type = 'server'
  165. filter_choices = (('name', _('Stack Name ='), True, _('Case-sensitive')),
  166. ('id', _('Stack ID ='), True),
  167. ('status', _('Status ='), True))
  168. class StacksTable(tables.DataTable):
  169. STATUS_CHOICES = (
  170. ("Complete", True),
  171. ("Failed", False),
  172. )
  173. STACK_STATUS_DISPLAY_CHOICES = (
  174. ("init_in_progress", pgettext_lazy("current status of stack",
  175. u"Init In Progress")),
  176. ("init_complete", pgettext_lazy("current status of stack",
  177. u"Init Complete")),
  178. ("init_failed", pgettext_lazy("current status of stack",
  179. u"Init Failed")),
  180. ("create_in_progress", pgettext_lazy("current status of stack",
  181. u"Create In Progress")),
  182. ("create_complete", pgettext_lazy("current status of stack",
  183. u"Create Complete")),
  184. ("create_failed", pgettext_lazy("current status of stack",
  185. u"Create Failed")),
  186. ("delete_in_progress", pgettext_lazy("current status of stack",
  187. u"Delete In Progress")),
  188. ("delete_complete", pgettext_lazy("current status of stack",
  189. u"Delete Complete")),
  190. ("delete_failed", pgettext_lazy("current status of stack",
  191. u"Delete Failed")),
  192. ("update_in_progress", pgettext_lazy("current status of stack",
  193. u"Update In Progress")),
  194. ("update_complete", pgettext_lazy("current status of stack",
  195. u"Update Complete")),
  196. ("update_failed", pgettext_lazy("current status of stack",
  197. u"Update Failed")),
  198. ("rollback_in_progress", pgettext_lazy("current status of stack",
  199. u"Rollback In Progress")),
  200. ("rollback_complete", pgettext_lazy("current status of stack",
  201. u"Rollback Complete")),
  202. ("rollback_failed", pgettext_lazy("current status of stack",
  203. u"Rollback Failed")),
  204. ("suspend_in_progress", pgettext_lazy("current status of stack",
  205. u"Suspend In Progress")),
  206. ("suspend_complete", pgettext_lazy("current status of stack",
  207. u"Suspend Complete")),
  208. ("suspend_failed", pgettext_lazy("current status of stack",
  209. u"Suspend Failed")),
  210. ("resume_in_progress", pgettext_lazy("current status of stack",
  211. u"Resume In Progress")),
  212. ("resume_complete", pgettext_lazy("current status of stack",
  213. u"Resume Complete")),
  214. ("resume_failed", pgettext_lazy("current status of stack",
  215. u"Resume Failed")),
  216. ("adopt_in_progress", pgettext_lazy("current status of stack",
  217. u"Adopt In Progress")),
  218. ("adopt_complete", pgettext_lazy("current status of stack",
  219. u"Adopt Complete")),
  220. ("adopt_failed", pgettext_lazy("current status of stack",
  221. u"Adopt Failed")),
  222. ("snapshot_in_progress", pgettext_lazy("current status of stack",
  223. u"Snapshot In Progress")),
  224. ("snapshot_complete", pgettext_lazy("current status of stack",
  225. u"Snapshot Complete")),
  226. ("snapshot_failed", pgettext_lazy("current status of stack",
  227. u"Snapshot Failed")),
  228. ("check_in_progress", pgettext_lazy("current status of stack",
  229. u"Check In Progress")),
  230. ("check_complete", pgettext_lazy("current status of stack",
  231. u"Check Complete")),
  232. ("check_failed", pgettext_lazy("current status of stack",
  233. u"Check Failed")),
  234. )
  235. name = tables.Column("stack_name",
  236. verbose_name=_("Stack Name"),
  237. link="horizon:project:stacks:detail",)
  238. created = tables.Column("creation_time",
  239. verbose_name=_("Created"),
  240. filters=(filters.parse_isotime,
  241. filters.timesince_sortable),
  242. attrs={'data-type': 'timesince'})
  243. updated = tables.Column("updated_time",
  244. verbose_name=_("Updated"),
  245. filters=(filters.parse_isotime,
  246. filters.timesince_or_never))
  247. status = tables.Column("status",
  248. hidden=True,
  249. status=True,
  250. status_choices=STATUS_CHOICES)
  251. stack_status = tables.Column("stack_status",
  252. verbose_name=_("Status"),
  253. display_choices=STACK_STATUS_DISPLAY_CHOICES)
  254. def get_object_display(self, stack):
  255. return stack.stack_name
  256. class Meta(object):
  257. name = "stacks"
  258. verbose_name = _("Stacks")
  259. pagination_param = 'stack_marker'
  260. status_columns = ["status", ]
  261. row_class = StacksUpdateRow
  262. table_actions_menu = (CheckStack,
  263. SuspendStack,
  264. ResumeStack,)
  265. table_actions = (LaunchStack,
  266. PreviewStack,
  267. DeleteStack,
  268. StacksFilterAction,)
  269. row_actions = (CheckStack,
  270. SuspendStack,
  271. ResumeStack,
  272. ChangeStackTemplate,
  273. DeleteStack,)
  274. def get_resource_url(obj):
  275. if obj.physical_resource_id == obj.stack_id:
  276. return None
  277. return urlresolvers.reverse('horizon:project:stacks:resource',
  278. args=(obj.stack_id, obj.resource_name))
  279. class EventsTable(tables.DataTable):
  280. logical_resource = tables.Column('resource_name',
  281. verbose_name=_("Stack Resource"),
  282. link=get_resource_url)
  283. physical_resource = tables.Column('physical_resource_id',
  284. verbose_name=_("Resource"))
  285. timestamp = tables.Column('event_time',
  286. verbose_name=_("Time Since Event"),
  287. filters=(filters.parse_isotime,
  288. filters.timesince_or_never))
  289. status = tables.Column("resource_status",
  290. filters=(title, filters.replace_underscores),
  291. verbose_name=_("Status"),)
  292. statusreason = tables.Column("resource_status_reason",
  293. verbose_name=_("Status Reason"),)
  294. class Meta(object):
  295. name = "events"
  296. verbose_name = _("Stack Events")
  297. class ResourcesUpdateRow(tables.Row):
  298. ajax = True
  299. def get_data(self, request, resource_name):
  300. try:
  301. stack = self.table.stack
  302. stack_identifier = '%s/%s' % (stack.stack_name, stack.id)
  303. return api.heat.resource_get(
  304. request, stack_identifier, resource_name)
  305. except exc.HTTPNotFound:
  306. # returning 404 to the ajax call removes the
  307. # row from the table on the ui
  308. raise Http404
  309. except Exception as e:
  310. messages.error(request, e)
  311. class ResourcesTable(tables.DataTable):
  312. class StatusColumn(tables.Column):
  313. def get_raw_data(self, datum):
  314. return datum.resource_status.partition("_")[2]
  315. STATUS_CHOICES = (
  316. ("Complete", True),
  317. ("Failed", False),
  318. )
  319. STATUS_DISPLAY_CHOICES = StacksTable.STACK_STATUS_DISPLAY_CHOICES
  320. logical_resource = tables.Column('resource_name',
  321. verbose_name=_("Stack Resource"),
  322. link=get_resource_url)
  323. physical_resource = tables.Column('physical_resource_id',
  324. verbose_name=_("Resource"),
  325. link=mappings.resource_to_url)
  326. resource_type = tables.Column("resource_type",
  327. verbose_name=_("Stack Resource Type"),)
  328. updated_time = tables.Column('updated_time',
  329. verbose_name=_("Date Updated"),
  330. filters=(filters.parse_isotime,
  331. filters.timesince_or_never))
  332. status = tables.Column("resource_status",
  333. verbose_name=_("Status"),
  334. display_choices=STATUS_DISPLAY_CHOICES)
  335. statusreason = tables.Column("resource_status_reason",
  336. verbose_name=_("Status Reason"),)
  337. status_hidden = StatusColumn("status",
  338. hidden=True,
  339. status=True,
  340. status_choices=STATUS_CHOICES)
  341. def __init__(self, request, data=None,
  342. needs_form_wrapper=None, **kwargs):
  343. super(ResourcesTable, self).__init__(
  344. request, data, needs_form_wrapper, **kwargs)
  345. self.stack = kwargs['stack']
  346. def get_object_id(self, datum):
  347. return datum.resource_name
  348. class Meta(object):
  349. name = "resources"
  350. verbose_name = _("Stack Resources")
  351. status_columns = ["status_hidden", ]
  352. row_class = ResourcesUpdateRow