Merge "Selected instances are not deleted with pagination"
This commit is contained in:
commit
9dba2cfa41
@ -1161,6 +1161,16 @@ class DataTable(object):
|
||||
"""
|
||||
return self.request.get_full_path().partition('?')[0]
|
||||
|
||||
def get_full_url(self):
|
||||
"""Returns the full URL path for this table.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the table. We use this method to persist the
|
||||
pagination marker.
|
||||
|
||||
"""
|
||||
return self.request.get_full_path()
|
||||
|
||||
def get_empty_message(self):
|
||||
"""Returns the message to be displayed when there is no data."""
|
||||
return self._no_data_message
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% load i18n %}
|
||||
{% with table.needs_form_wrapper as needs_form_wrapper %}
|
||||
<div class="table_wrapper">
|
||||
{% if needs_form_wrapper %}<form action="{{ table.get_absolute_url }}" method="POST">{% csrf_token %}{% endif %}
|
||||
{% if needs_form_wrapper %}<form action="{{ table.get_full_url }}" method="POST">{% csrf_token %}{% endif %}
|
||||
{% with columns=table.get_columns rows=table.get_rows %}
|
||||
{% block table %}
|
||||
<table id="{{ table.name }}" class="table table-bordered table-striped datatable">
|
||||
|
@ -269,6 +269,17 @@ class ContainersTable(tables.DataTable):
|
||||
url = super(ContainersTable, self).get_absolute_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
def get_full_url(self):
|
||||
"""Returns the encoded absolute URL path with its query string.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the table. We use this method to persist the
|
||||
pagination marker.
|
||||
|
||||
"""
|
||||
url = super(ContainersTable, self).get_full_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
|
||||
class ViewObject(tables.LinkAction):
|
||||
name = "view"
|
||||
@ -416,3 +427,14 @@ class ObjectsTable(tables.DataTable):
|
||||
def get_absolute_url(self):
|
||||
url = super(ObjectsTable, self).get_absolute_url()
|
||||
return http.urlquote(url)
|
||||
|
||||
def get_full_url(self):
|
||||
"""Returns the encoded absolute URL path with its query string.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the table. We use this method to persist the
|
||||
pagination marker.
|
||||
|
||||
"""
|
||||
url = super(ObjectsTable, self).get_full_url()
|
||||
return http.urlquote(url)
|
||||
|
@ -21,6 +21,7 @@
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import http
|
||||
from django.test import utils as test_utils
|
||||
@ -2646,6 +2647,96 @@ class InstanceTests(test.TestCase):
|
||||
disk_config='AUTO')
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test_utils.override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@test.create_stubs({api.nova: ('flavor_list',
|
||||
'server_list',
|
||||
'tenant_absolute_limits',
|
||||
'extension_supported',),
|
||||
api.glance: ('image_list_detailed',),
|
||||
api.network:
|
||||
('floating_ip_simple_associate_supported',),
|
||||
})
|
||||
def test_index_form_action_with_pagination(self):
|
||||
"""The form action on the next page should have marker
|
||||
object from the previous page last element.
|
||||
"""
|
||||
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)) \
|
||||
.MultipleTimes().AndReturn(self.flavors.list())
|
||||
api.glance.image_list_detailed(IgnoreArg()) \
|
||||
.MultipleTimes().AndReturn((self.images.list(), False))
|
||||
|
||||
search_opts = {'marker': None, 'paginate': True}
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
|
||||
.AndReturn([servers[:page_size], True])
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts={
|
||||
'marker': servers[page_size - 1].id, 'paginate': True}) \
|
||||
.AndReturn([servers[page_size:], False])
|
||||
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
|
||||
.MultipleTimes().AndReturn(self.limits['absolute'])
|
||||
api.network.floating_ip_simple_associate_supported(
|
||||
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'project/instances/index.html')
|
||||
# get first page with 2 items
|
||||
self.assertEqual(len(res.context['instances_table'].data), page_size)
|
||||
|
||||
# update INDEX_URL with marker object
|
||||
next_page_url = "?".join([reverse('horizon:project:instances:index'),
|
||||
"=".join([tables.InstancesTable._meta.pagination_param,
|
||||
servers[page_size - 1].id])])
|
||||
form_action = 'action="%s"' % next_page_url
|
||||
|
||||
res = self.client.get(next_page_url)
|
||||
# get next page with remaining items (item 3)
|
||||
self.assertEqual(len(res.context['instances_table'].data), 1)
|
||||
# ensure that marker object exists in form action
|
||||
self.assertContains(res, form_action, count=1)
|
||||
|
||||
@test_utils.override_settings(API_RESULT_PAGE_SIZE=2)
|
||||
@test.create_stubs({api.nova: ('server_list',
|
||||
'flavor_list',
|
||||
'server_delete',),
|
||||
api.glance: ('image_list_detailed',),
|
||||
api.network: ('servers_update_addresses',)})
|
||||
def test_terminate_instance_with_pagination(self):
|
||||
"""Instance should be deleted from
|
||||
the next page.
|
||||
"""
|
||||
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 2)
|
||||
servers = self.servers.list()[:3]
|
||||
server = servers[-1]
|
||||
|
||||
search_opts = {'marker': servers[page_size - 1].id, 'paginate': True}
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
|
||||
.AndReturn([servers[page_size:], False])
|
||||
api.network.servers_update_addresses(IsA(http.HttpRequest),
|
||||
servers[page_size:])
|
||||
api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
||||
api.glance.image_list_detailed(IgnoreArg()) \
|
||||
.AndReturn((self.images.list(), False))
|
||||
api.nova.server_delete(IsA(http.HttpRequest), server.id)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# update INDEX_URL with marker object
|
||||
next_page_url = "?".join([reverse('horizon:project:instances:index'),
|
||||
"=".join([tables.InstancesTable._meta.pagination_param,
|
||||
servers[page_size - 1].id])])
|
||||
formData = {'action': 'instances__terminate__%s' % server.id}
|
||||
res = self.client.post(next_page_url, formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, next_page_url)
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
|
||||
class InstanceAjaxTests(test.TestCase):
|
||||
@test.create_stubs({api.nova: ("server_get",
|
||||
|
Loading…
x
Reference in New Issue
Block a user