Merge "Fix actions inside instance details view page"
This commit is contained in:
commit
20eaa03174
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
|
import inspect
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
@ -466,6 +467,8 @@ class Column(html.HTMLElement):
|
|||||||
return None
|
return None
|
||||||
obj_id = self.table.get_object_id(datum)
|
obj_id = self.table.get_object_id(datum)
|
||||||
if callable(self.link):
|
if callable(self.link):
|
||||||
|
if 'request' in inspect.getargspec(self.link).args:
|
||||||
|
return self.link(datum, request=self.table.request)
|
||||||
return self.link(datum)
|
return self.link(datum)
|
||||||
try:
|
try:
|
||||||
return urlresolvers.reverse(self.link, args=(obj_id,))
|
return urlresolvers.reverse(self.link, args=(obj_id,))
|
||||||
@ -670,11 +673,19 @@ class Row(html.HTMLElement):
|
|||||||
|
|
||||||
def get_ajax_update_url(self):
|
def get_ajax_update_url(self):
|
||||||
table_url = self.table.get_absolute_url()
|
table_url = self.table.get_absolute_url()
|
||||||
params = urlencode(collections.OrderedDict([
|
marker_name = self.table._meta.pagination_param
|
||||||
|
marker = self.table.request.GET.get(marker_name, None)
|
||||||
|
if not marker:
|
||||||
|
marker_name = self.table._meta.prev_pagination_param
|
||||||
|
marker = self.table.request.GET.get(marker_name, None)
|
||||||
|
request_params = [
|
||||||
("action", self.ajax_action_name),
|
("action", self.ajax_action_name),
|
||||||
("table", self.table.name),
|
("table", self.table.name),
|
||||||
("obj_id", self.table.get_object_id(self.datum))
|
("obj_id", self.table.get_object_id(self.datum)),
|
||||||
]))
|
]
|
||||||
|
if marker:
|
||||||
|
request_params.append((marker_name, marker))
|
||||||
|
params = urlencode(collections.OrderedDict(request_params))
|
||||||
return "%s?%s" % (table_url, params)
|
return "%s?%s" % (table_url, params)
|
||||||
|
|
||||||
def can_be_selected(self, datum):
|
def can_be_selected(self, datum):
|
||||||
|
@ -26,6 +26,7 @@ from openstack_dashboard.dashboards.project.instances import audit_tables
|
|||||||
from openstack_dashboard.dashboards.project.instances \
|
from openstack_dashboard.dashboards.project.instances \
|
||||||
import tables as project_tables
|
import tables as project_tables
|
||||||
from openstack_dashboard import policy
|
from openstack_dashboard import policy
|
||||||
|
from openstack_dashboard.views import get_url_with_pagination
|
||||||
|
|
||||||
|
|
||||||
class AdminEditInstance(project_tables.EditInstance):
|
class AdminEditInstance(project_tables.EditInstance):
|
||||||
@ -109,6 +110,13 @@ class AdminInstanceFilterAction(tables.FilterAction):
|
|||||||
) + project_tables.INSTANCE_FILTER_CHOICES
|
) + project_tables.INSTANCE_FILTER_CHOICES
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_detail_link(obj, request):
|
||||||
|
return get_url_with_pagination(
|
||||||
|
request, AdminInstancesTable._meta.pagination_param,
|
||||||
|
AdminInstancesTable._meta.prev_pagination_param,
|
||||||
|
"horizon:admin:instances:detail", obj.id)
|
||||||
|
|
||||||
|
|
||||||
class AdminInstancesTable(tables.DataTable):
|
class AdminInstancesTable(tables.DataTable):
|
||||||
TASK_STATUS_CHOICES = (
|
TASK_STATUS_CHOICES = (
|
||||||
(None, True),
|
(None, True),
|
||||||
@ -134,7 +142,7 @@ class AdminInstancesTable(tables.DataTable):
|
|||||||
verbose_name=_("Host"),
|
verbose_name=_("Host"),
|
||||||
classes=('nowrap-col',))
|
classes=('nowrap-col',))
|
||||||
name = tables.WrappingColumn("name",
|
name = tables.WrappingColumn("name",
|
||||||
link="horizon:admin:instances:detail",
|
link=get_server_detail_link,
|
||||||
verbose_name=_("Name"))
|
verbose_name=_("Name"))
|
||||||
image_name = tables.Column("image_name",
|
image_name = tables.Column("image_name",
|
||||||
verbose_name=_("Image Name"))
|
verbose_name=_("Image Name"))
|
||||||
|
@ -44,7 +44,7 @@ from openstack_dashboard.dashboards.project.instances.workflows \
|
|||||||
from openstack_dashboard.dashboards.project.instances.workflows \
|
from openstack_dashboard.dashboards.project.instances.workflows \
|
||||||
import update_instance
|
import update_instance
|
||||||
from openstack_dashboard import policy
|
from openstack_dashboard import policy
|
||||||
|
from openstack_dashboard.views import get_url_with_pagination
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -1207,6 +1207,14 @@ def render_locked(instance):
|
|||||||
return mark_safe(locked_status)
|
return mark_safe(locked_status)
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_detail_link(obj, request):
|
||||||
|
return get_url_with_pagination(request,
|
||||||
|
InstancesTable._meta.pagination_param,
|
||||||
|
InstancesTable._meta.prev_pagination_param,
|
||||||
|
'horizon:project:instances:detail',
|
||||||
|
obj.id)
|
||||||
|
|
||||||
|
|
||||||
class InstancesTable(tables.DataTable):
|
class InstancesTable(tables.DataTable):
|
||||||
TASK_STATUS_CHOICES = (
|
TASK_STATUS_CHOICES = (
|
||||||
(None, True),
|
(None, True),
|
||||||
@ -1223,7 +1231,7 @@ class InstancesTable(tables.DataTable):
|
|||||||
("shelved_offloaded", True),
|
("shelved_offloaded", True),
|
||||||
)
|
)
|
||||||
name = tables.WrappingColumn("name",
|
name = tables.WrappingColumn("name",
|
||||||
link="horizon:project:instances:detail",
|
link=get_server_detail_link,
|
||||||
verbose_name=_("Instance Name"))
|
verbose_name=_("Instance Name"))
|
||||||
image_name = tables.WrappingColumn("image_name",
|
image_name = tables.WrappingColumn("image_name",
|
||||||
verbose_name=_("Image Name"))
|
verbose_name=_("Image Name"))
|
||||||
|
@ -44,6 +44,7 @@ from openstack_dashboard.dashboards.project.instances import tabs
|
|||||||
from openstack_dashboard.dashboards.project.instances import workflows
|
from openstack_dashboard.dashboards.project.instances import workflows
|
||||||
from openstack_dashboard.test import helpers
|
from openstack_dashboard.test import helpers
|
||||||
from openstack_dashboard.usage import quotas
|
from openstack_dashboard.usage import quotas
|
||||||
|
from openstack_dashboard.views import get_url_with_pagination
|
||||||
|
|
||||||
|
|
||||||
INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
|
INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
|
||||||
@ -611,12 +612,51 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
|||||||
six.text_type(server.id))
|
six.text_type(server.id))
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'instances__suspend__%s' % server.id}
|
formData = {'action': 'instances__suspend__%s' % server.id}
|
||||||
res = self.client.post(INDEX_URL, formData)
|
url = get_url_with_pagination(
|
||||||
|
self.request, 'next', 'prev', 'horizon:project:instances:index')
|
||||||
|
res = self.client.post(url, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
|
@django.test.utils.override_settings(API_RESULT_PAGE_SIZE=2)
|
||||||
|
@helpers.create_stubs({api.nova: ('server_suspend',
|
||||||
|
'server_list',
|
||||||
|
'flavor_list',
|
||||||
|
'extension_supported',
|
||||||
|
'is_feature_available',),
|
||||||
|
api.glance: ('image_list_detailed',),
|
||||||
|
api.network: ('servers_update_addresses',)})
|
||||||
|
def test_suspend_instance_if_placed_on_2nd_page(self):
|
||||||
|
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 2)
|
||||||
|
servers = self.servers.list()[:3]
|
||||||
|
|
||||||
|
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
|
||||||
|
.MultipleTimes().AndReturn(True)
|
||||||
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
|
.AndReturn(self.flavors.list())
|
||||||
|
api.glance.image_list_detailed(IgnoreArg()) \
|
||||||
|
.AndReturn((self.images.list(), False, False))
|
||||||
|
|
||||||
|
api.nova.server_list(IsA(http.HttpRequest), search_opts={
|
||||||
|
'marker': servers[page_size - 1].id, 'paginate': True}) \
|
||||||
|
.AndReturn([servers[page_size:], False])
|
||||||
|
api.network.servers_update_addresses(
|
||||||
|
IsA(http.HttpRequest), servers[page_size:])
|
||||||
|
api.nova.server_suspend(IsA(http.HttpRequest),
|
||||||
|
six.text_type(servers[-1].id))
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
self.request.GET['marker'] = servers[-2].id
|
||||||
|
params = "=".join([tables.InstancesTable._meta.pagination_param,
|
||||||
|
servers[page_size - 1].id])
|
||||||
|
url = "?".join([reverse('horizon:project:instances:index'),
|
||||||
|
params])
|
||||||
|
formData = {'action': 'instances__suspend__%s' % servers[-1].id}
|
||||||
|
|
||||||
|
self.client.post(url, formData)
|
||||||
|
|
||||||
@helpers.create_stubs({api.nova: ('server_suspend',
|
@helpers.create_stubs({api.nova: ('server_suspend',
|
||||||
'server_list',
|
'server_list',
|
||||||
'flavor_list',
|
'flavor_list',
|
||||||
|
@ -53,6 +53,7 @@ from openstack_dashboard.dashboards.project.instances \
|
|||||||
import tabs as project_tabs
|
import tabs as project_tabs
|
||||||
from openstack_dashboard.dashboards.project.instances \
|
from openstack_dashboard.dashboards.project.instances \
|
||||||
import workflows as project_workflows
|
import workflows as project_workflows
|
||||||
|
from openstack_dashboard.views import get_url_with_pagination
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -322,7 +323,12 @@ class DetailView(tabs.TabView):
|
|||||||
args=[instance.image['id']])
|
args=[instance.image['id']])
|
||||||
instance.volume_url = self.volume_url
|
instance.volume_url = self.volume_url
|
||||||
context["instance"] = instance
|
context["instance"] = instance
|
||||||
context["url"] = reverse(self.redirect_url)
|
context["url"] = get_url_with_pagination(
|
||||||
|
self.request,
|
||||||
|
project_tables.InstancesTable._meta.pagination_param,
|
||||||
|
project_tables.InstancesTable._meta.prev_pagination_param,
|
||||||
|
self.redirect_url)
|
||||||
|
|
||||||
context["actions"] = self._get_actions(instance)
|
context["actions"] = self._get_actions(instance)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
46
openstack_dashboard/test/views.py
Normal file
46
openstack_dashboard/test/views.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from openstack_dashboard.test import helpers as test
|
||||||
|
from openstack_dashboard import views
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardViewsTest(test.TestCase):
|
||||||
|
def test_get_url_with_pagination(self):
|
||||||
|
req = self.request
|
||||||
|
url_string = 'horizon:project:instances:index'
|
||||||
|
url = views.get_url_with_pagination(req, None, None, url_string, None)
|
||||||
|
self.assertEqual(six.text_type('/project/instances/'), url)
|
||||||
|
|
||||||
|
def test_get_url_with_pagination_with_if(self):
|
||||||
|
req = self.request
|
||||||
|
url_string = 'horizon:project:instances:detail'
|
||||||
|
url = views.get_url_with_pagination(req, None, None, url_string, 'id')
|
||||||
|
self.assertEqual(six.text_type('/project/instances/id/'), url)
|
||||||
|
|
||||||
|
def test_get_url_with_pagination_next(self):
|
||||||
|
req = self.request
|
||||||
|
url_string = 'horizon:project:instances:index'
|
||||||
|
req.GET.update({'next': 'id'})
|
||||||
|
url = views.get_url_with_pagination(
|
||||||
|
req, 'next', None, url_string, None)
|
||||||
|
self.assertEqual(six.text_type('/project/instances/?next=id'), url)
|
||||||
|
|
||||||
|
def test_get_url_with_pagination_prev(self):
|
||||||
|
req = self.request
|
||||||
|
url_string = 'horizon:project:instances:index'
|
||||||
|
req.GET.update({'prev': 'id'})
|
||||||
|
url = views.get_url_with_pagination(
|
||||||
|
req, None, 'prev', url_string, None)
|
||||||
|
self.assertEqual(six.text_type('/project/instances/?prev=id'), url)
|
@ -13,8 +13,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core import urlresolvers
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
import django.views.decorators.vary
|
import django.views.decorators.vary
|
||||||
|
from six.moves import urllib
|
||||||
|
|
||||||
import horizon
|
import horizon
|
||||||
from horizon import base
|
from horizon import base
|
||||||
@ -59,3 +61,22 @@ def splash(request):
|
|||||||
if MESSAGES_PATH:
|
if MESSAGES_PATH:
|
||||||
notifications.process_message_notification(request, MESSAGES_PATH)
|
notifications.process_message_notification(request, MESSAGES_PATH)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_with_pagination(request, marker_name, prev_marker_name, url_string,
|
||||||
|
object_id=None):
|
||||||
|
if object_id:
|
||||||
|
url = urlresolvers.reverse(url_string, args=(object_id,))
|
||||||
|
else:
|
||||||
|
url = urlresolvers.reverse(url_string)
|
||||||
|
marker = request.GET.get(marker_name, None)
|
||||||
|
if marker:
|
||||||
|
return "{}?{}".format(url,
|
||||||
|
urllib.parse.urlencode({marker_name: marker}))
|
||||||
|
|
||||||
|
prev_marker = request.GET.get(prev_marker_name, None)
|
||||||
|
if prev_marker:
|
||||||
|
return "{}?{}".format(url,
|
||||||
|
urllib.parse.urlencode({prev_marker_name:
|
||||||
|
prev_marker}))
|
||||||
|
return url
|
||||||
|
Loading…
Reference in New Issue
Block a user