Skip to content

Commit 5840d04

Browse files
committed
Add support for locking and unlocking servers
Partially implements: blueprint lock-unlock-server Change-Id: I6d261cc7fb314bda60f0732f7a7bb3b23ba7c179
1 parent ff09b75 commit 5840d04

3 files changed

Lines changed: 181 additions & 1 deletion

File tree

openstack_dashboard/api/nova.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,14 @@ def server_stop(request, instance_id):
656656
novaclient(request).servers.stop(instance_id)
657657

658658

659+
def server_lock(request, instance_id):
660+
novaclient(request).servers.lock(instance_id)
661+
662+
663+
def server_unlock(request, instance_id):
664+
novaclient(request).servers.unlock(instance_id)
665+
666+
659667
def tenant_quota_get(request, tenant_id):
660668
return base.QuotaSet(novaclient(request).quotas.get(tenant_id))
661669

openstack_dashboard/dashboards/project/instances/tables.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,68 @@ def action(self, request, obj_id):
709709
api.nova.server_stop(request, obj_id)
710710

711711

712+
class LockInstance(policy.PolicyTargetMixin, tables.BatchAction):
713+
name = "lock"
714+
policy_rules = (("compute", "compute_extension:admin_actions:lock"),)
715+
716+
@staticmethod
717+
def action_present(count):
718+
return ungettext_lazy(
719+
u"Lock Instance",
720+
u"Lock Instances",
721+
count
722+
)
723+
724+
@staticmethod
725+
def action_past(count):
726+
return ungettext_lazy(
727+
u"Locked Instance",
728+
u"Locked Instances",
729+
count
730+
)
731+
732+
# TODO(akrivoka): When the lock status is added to nova, revisit this
733+
# to only allow unlocked instances to be locked
734+
def allowed(self, request, instance):
735+
if not api.nova.extension_supported('AdminActions', request):
736+
return False
737+
return True
738+
739+
def action(self, request, obj_id):
740+
api.nova.server_lock(request, obj_id)
741+
742+
743+
class UnlockInstance(policy.PolicyTargetMixin, tables.BatchAction):
744+
name = "unlock"
745+
policy_rules = (("compute", "compute_extension:admin_actions:unlock"),)
746+
747+
@staticmethod
748+
def action_present(count):
749+
return ungettext_lazy(
750+
u"Unlock Instance",
751+
u"Unlock Instances",
752+
count
753+
)
754+
755+
@staticmethod
756+
def action_past(count):
757+
return ungettext_lazy(
758+
u"Unlocked Instance",
759+
u"Unlocked Instances",
760+
count
761+
)
762+
763+
# TODO(akrivoka): When the lock status is added to nova, revisit this
764+
# to only allow locked instances to be unlocked
765+
def allowed(self, request, instance):
766+
if not api.nova.extension_supported('AdminActions', request):
767+
return False
768+
return True
769+
770+
def action(self, request, obj_id):
771+
api.nova.server_unlock(request, obj_id)
772+
773+
712774
def get_ips(instance):
713775
template_name = 'project/instances/_instance_ips.html'
714776
context = {"instance": instance}
@@ -930,5 +992,6 @@ class Meta:
930992
SimpleDisassociateIP, EditInstance,
931993
DecryptInstancePassword, EditInstanceSecurityGroups,
932994
ConsoleLink, LogLink, TogglePause, ToggleSuspend,
933-
ResizeLink, SoftRebootInstance, RebootInstance,
995+
ResizeLink, LockInstance, UnlockInstance,
996+
SoftRebootInstance, RebootInstance,
934997
StopInstance, RebuildInstance, TerminateInstance)

openstack_dashboard/dashboards/project/instances/tests.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,115 @@ def test_resume_instance_exception(self):
653653

654654
self.assertRedirectsNoFollow(res, INDEX_URL)
655655

656+
@helpers.create_stubs({api.nova: ('server_lock',
657+
'server_list',
658+
'extension_supported',),
659+
api.glance: ('image_list_detailed',),
660+
api.network: ('servers_update_addresses',)})
661+
def test_lock_instance(self):
662+
servers = self.servers.list()
663+
server = servers[0]
664+
665+
api.nova.extension_supported('AdminActions', IsA(
666+
http.HttpRequest)).MultipleTimes().AndReturn(True)
667+
api.glance.image_list_detailed(IgnoreArg()).AndReturn((
668+
self.images.list(), False, False))
669+
search_opts = {'marker': None, 'paginate': True}
670+
api.nova.server_list(
671+
IsA(http.HttpRequest),
672+
search_opts=search_opts).AndReturn([servers, False])
673+
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
674+
api.nova.server_lock(IsA(http.HttpRequest), server.id)
675+
676+
self.mox.ReplayAll()
677+
678+
formData = {'action': 'instances__lock__%s' % server.id}
679+
res = self.client.post(INDEX_URL, formData)
680+
681+
self.assertRedirectsNoFollow(res, INDEX_URL)
682+
683+
@helpers.create_stubs({api.nova: ('server_lock',
684+
'server_list',
685+
'extension_supported',),
686+
api.glance: ('image_list_detailed',),
687+
api.network: ('servers_update_addresses',)})
688+
def test_lock_instance_exception(self):
689+
servers = self.servers.list()
690+
server = servers[0]
691+
692+
api.nova.extension_supported('AdminActions', IsA(
693+
http.HttpRequest)).MultipleTimes().AndReturn(True)
694+
api.glance.image_list_detailed(IgnoreArg()).AndReturn((
695+
self.images.list(), False, False))
696+
search_opts = {'marker': None, 'paginate': True}
697+
api.nova.server_list(
698+
IsA(http.HttpRequest),
699+
search_opts=search_opts).AndReturn([servers, False])
700+
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
701+
api.nova.server_lock(IsA(http.HttpRequest), server.id).AndRaise(
702+
self.exceptions.nova)
703+
704+
self.mox.ReplayAll()
705+
706+
formData = {'action': 'instances__lock__%s' % server.id}
707+
res = self.client.post(INDEX_URL, formData)
708+
709+
self.assertRedirectsNoFollow(res, INDEX_URL)
710+
711+
@helpers.create_stubs({api.nova: ('server_unlock',
712+
'server_list',
713+
'extension_supported',),
714+
api.glance: ('image_list_detailed',),
715+
api.network: ('servers_update_addresses',)})
716+
def test_unlock_instance(self):
717+
servers = self.servers.list()
718+
server = servers[0]
719+
api.nova.extension_supported('AdminActions', IsA(
720+
http.HttpRequest)).MultipleTimes().AndReturn(True)
721+
api.glance.image_list_detailed(IgnoreArg()).AndReturn((
722+
self.images.list(), False, False))
723+
search_opts = {'marker': None, 'paginate': True}
724+
api.nova.server_list(
725+
IsA(http.HttpRequest),
726+
search_opts=search_opts).AndReturn([servers, False])
727+
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
728+
api.nova.server_unlock(IsA(http.HttpRequest), server.id)
729+
730+
self.mox.ReplayAll()
731+
732+
formData = {'action': 'instances__unlock__%s' % server.id}
733+
res = self.client.post(INDEX_URL, formData)
734+
735+
self.assertRedirectsNoFollow(res, INDEX_URL)
736+
737+
@helpers.create_stubs({api.nova: ('server_unlock',
738+
'server_list',
739+
'extension_supported',),
740+
api.glance: ('image_list_detailed',),
741+
api.network: ('servers_update_addresses',)})
742+
def test_unlock_instance_exception(self):
743+
servers = self.servers.list()
744+
server = servers[0]
745+
746+
api.nova.extension_supported('AdminActions', IsA(
747+
http.HttpRequest)).MultipleTimes().AndReturn(True)
748+
api.glance.image_list_detailed(IgnoreArg()).AndReturn((
749+
self.images.list(), False, False))
750+
search_opts = {'marker': None, 'paginate': True}
751+
api.nova.server_list(
752+
IsA(http.HttpRequest),
753+
search_opts=search_opts).AndReturn([servers, False])
754+
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
755+
api.nova.server_unlock(IsA(http.HttpRequest), server.id).AndRaise(
756+
self.exceptions.nova)
757+
758+
self.mox.ReplayAll()
759+
760+
formData = {'action': 'instances__unlock__%s' % server.id}
761+
res = self.client.post(INDEX_URL, formData)
762+
763+
self.assertRedirectsNoFollow(res, INDEX_URL)
764+
656765
@helpers.create_stubs({
657766
api.nova: (
658767
"server_get",

0 commit comments

Comments
 (0)