Enable compute shelve/unshelve commands

This change enables shelve/unshelve commands in user/admin views.

Closes-Bug: #1285389
Implements: blueprint horizon-shelving-command
Change-Id: I15e1e5d2fdb3d246a7f3e7e676c4b364694410fb
This commit is contained in:
Cedric Brandily 2015-09-06 20:24:04 +02:00 committed by Timur Sufiev
parent dd812faa69
commit 9bee1c103c
5 changed files with 236 additions and 1 deletions

View File

@ -650,6 +650,14 @@ def server_resume(request, instance_id):
novaclient(request).servers.resume(instance_id)
def server_shelve(request, instance_id):
novaclient(request).servers.shelve(instance_id)
def server_unshelve(request, instance_id):
novaclient(request).servers.unshelve(instance_id)
def server_reboot(request, instance_id, soft_reboot=False):
hardness = nova_servers.REBOOT_HARD
if soft_reboot:

View File

@ -182,6 +182,7 @@ class AdminInstancesTable(tables.DataTable):
project_tables.CreateSnapshot,
project_tables.TogglePause,
project_tables.ToggleSuspend,
project_tables.ToggleShelve,
MigrateInstance,
LiveMigrateInstance,
project_tables.SoftRebootInstance,

View File

@ -39,6 +39,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
tenants = self.tenants.list()
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.keystone.tenant_list(IsA(http.HttpRequest)).\
AndReturn([tenants, False])
search_opts = {'marker': None, 'paginate': True}
@ -73,6 +75,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
all_tenants=True)
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)). \
AndRaise(self.exceptions.nova)
api.keystone.tenant_list(IsA(http.HttpRequest)).\
@ -109,6 +113,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
all_tenants=True)
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)). \
AndReturn(flavors)
api.keystone.tenant_list(IsA(http.HttpRequest)).\
@ -150,6 +156,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_get(IsA(http.HttpRequest),
server.flavor['id']).AndReturn(flavor)
api.keystone.tenant_get(IsA(http.HttpRequest),
@ -191,6 +199,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
all_tenants=True)
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)).\
AndReturn(self.flavors.list())
self.mox.ReplayAll()
@ -215,6 +225,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
search_opts = {'marker': None, 'paginate': True}
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True, search_opts=search_opts) \
.AndReturn([servers, False])

View File

@ -70,6 +70,8 @@ PAUSE = 0
UNPAUSE = 1
SUSPEND = 0
RESUME = 1
SHELVE = 0
UNSHELVE = 1
def is_deleting(instance):
@ -304,6 +306,73 @@ class ToggleSuspend(tables.BatchAction):
self.current_past_action = SUSPEND
class ToggleShelve(tables.BatchAction):
name = "shelve"
icon = "shelve"
@staticmethod
def action_present(count):
return (
ungettext_lazy(
u"Shelve Instance",
u"Shelve Instances",
count
),
ungettext_lazy(
u"Unshelve Instance",
u"Unshelve Instances",
count
),
)
@staticmethod
def action_past(count):
return (
ungettext_lazy(
u"Shelved Instance",
u"Shelved Instances",
count
),
ungettext_lazy(
u"Unshelved Instance",
u"Unshelved Instances",
count
),
)
def allowed(self, request, instance=None):
if not api.nova.extension_supported('Shelve', request):
return False
if not instance:
return False
self.shelved = instance.status == "SHELVED_OFFLOADED"
if self.shelved:
self.current_present_action = UNSHELVE
policy = (("compute", "compute_extension:unshelve"),)
else:
self.current_present_action = SHELVE
policy = (("compute", "compute_extension:shelve"),)
has_permission = True
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None)
if policy_check:
has_permission = policy_check(
policy, request,
target={'project_id': getattr(instance, 'tenant_id', None)})
return (has_permission
and (instance.status in ACTIVE_STATES or self.shelved)
and not is_deleting(instance))
def action(self, request, obj_id):
if self.shelved:
api.nova.server_unshelve(request, obj_id)
self.current_past_action = UNSHELVE
else:
api.nova.server_shelve(request, obj_id)
self.current_past_action = SHELVE
class LaunchLink(tables.LinkAction):
name = "launch"
verbose_name = _("Launch Instance")
@ -1109,6 +1178,6 @@ class InstancesTable(tables.DataTable):
DetachInterface, EditInstance,
DecryptInstancePassword, EditInstanceSecurityGroups,
ConsoleLink, LogLink, TogglePause, ToggleSuspend,
ResizeLink, LockInstance, UnlockInstance,
ToggleShelve, ResizeLink, LockInstance, UnlockInstance,
SoftRebootInstance, RebootInstance,
StopInstance, RebuildInstance, TerminateInstance)

View File

@ -75,6 +75,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -138,6 +140,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
@ -182,6 +186,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -611,6 +617,129 @@ class InstanceTests(helpers.TestCase):
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.nova: ('server_shelve',
'server_list',
'flavor_list',
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_shelve_instance(self):
servers = self.servers.list()
server = servers[0]
api.nova.extension_supported('Shelve', 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))
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.server_shelve(IsA(http.HttpRequest), six.text_type(server.id))
self.mox.ReplayAll()
formData = {'action': 'instances__shelve__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.nova: ('server_shelve',
'server_list',
'flavor_list',
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_shelve_instance_exception(self):
servers = self.servers.list()
server = servers[0]
api.nova.extension_supported('Shelve', 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))
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.server_shelve(IsA(http.HttpRequest),
six.text_type(server.id)) \
.AndRaise(self.exceptions.nova)
self.mox.ReplayAll()
formData = {'action': 'instances__shelve__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.nova: ('server_unshelve',
'server_list',
'flavor_list',
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_unshelve_instance(self):
servers = self.servers.list()
server = servers[0]
server.status = "SHELVED_OFFLOADED"
api.nova.extension_supported('Shelve', 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))
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.server_unshelve(IsA(http.HttpRequest),
six.text_type(server.id))
self.mox.ReplayAll()
formData = {'action': 'instances__shelve__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.nova: ('server_unshelve',
'server_list',
'flavor_list',
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_unshelve_instance_exception(self):
servers = self.servers.list()
server = servers[0]
server.status = "SHELVED_OFFLOADED"
api.nova.extension_supported('Shelve', 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))
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.server_unshelve(IsA(http.HttpRequest),
six.text_type(server.id)) \
.AndRaise(self.exceptions.nova)
self.mox.ReplayAll()
formData = {'action': 'instances__shelve__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@helpers.create_stubs({api.nova: ('server_lock',
'server_list',
'extension_supported',),
@ -771,6 +900,8 @@ class InstanceTests(helpers.TestCase):
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
self.mox.ReplayAll()
@ -1140,6 +1271,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -3298,6 +3431,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -3470,6 +3605,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -4024,6 +4161,8 @@ class InstanceTests(helpers.TestCase):
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
@ -4170,6 +4309,8 @@ class InstanceAjaxTests(helpers.TestCase):
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group')\
.MultipleTimes().AndReturn(True)
@ -4212,6 +4353,8 @@ class InstanceAjaxTests(helpers.TestCase):
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group')\
.MultipleTimes().AndReturn(True)
@ -4250,6 +4393,8 @@ class InstanceAjaxTests(helpers.TestCase):
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group')\
.MultipleTimes().AndReturn(True)