Commit a1d135bf by Szabolcs Gelencser

Deploy and Shutoff operations work

parent 7cb09284
......@@ -76,20 +76,10 @@ class AclBase(Model):
object_level_set = GenericRelation(ObjectLevel)
def clone_acl(self, other):
"""Clone full ACL from other object."""
assert self.id != other.id or type(self) != type(other)
self.object_level_set.clear()
for i in other.object_level_set.all():
ol = self.object_level_set.create(level=i.level)
for j in i.users.all():
ol.users.add(j)
for j in i.groups.all():
ol.groups.add(j)
pass
@classmethod
def get_level_object(cls, level):
"""Get Level object for this model by codename."""
ct = ContentType.objects.get_for_model(cls)
return Level.objects.get(codename=level, content_type=ct)
......@@ -110,104 +100,28 @@ class AclBase(Model):
raise AttributeError('"whom" must be a User or Group object.')
def set_user_level(self, user, level):
"""Set level of object for a user.
:param whom: user the level is set for
:type whom: User
:param level: codename of level to set, or None
:type level: Level or str or unicode or NoneType
"""
logger.info('%s.set_user_level(%s, %s) called',
*[unicode(p) for p in [self, user, level]])
if level is None:
pk = None
else:
if isinstance(level, basestring):
level = self.get_level_object(level)
if not self.object_level_set.filter(level_id=level.pk).exists():
self.object_level_set.create(level=level)
pk = level.pk
for i in self.object_level_set.all():
if i.level_id != pk:
i.users.remove(user)
else:
i.users.add(user)
i.save()
#TODO: delete
pass
def set_group_level(self, group, level):
"""Set level of object for a user.
:param whom: user the level is set for
:type whom: User or unicode or str
:param level: codename of level to set
:type level: str or unicode
"""
logger.info('%s.set_group_level(%s, %s) called',
*[unicode(p) for p in [self, group, level]])
if level is None:
pk = None
else:
if isinstance(level, basestring):
level = self.get_level_object(level)
if not self.object_level_set.filter(level_id=level.pk).exists():
self.object_level_set.create(level=level)
pk = level.pk
for i in self.object_level_set.all():
if i.level_id != pk:
i.groups.remove(group)
else:
i.groups.add(group)
i.save()
#TODO: delete
pass
def has_level(self, user, level, group_also=True):
return True
#TODO: implement
logger.debug('%s.has_level(%s, %s, %s) called',
*[unicode(p) for p in [self, user, level, group_also]])
if user is None or not user.is_authenticated():
return False
if getattr(user, 'is_superuser', False):
logger.debug('- superuser granted')
return True
if isinstance(level, basestring):
level = self.get_level_object(level)
logger.debug("- level set by str: %s", unicode(level))
object_levels = self.object_level_set.filter(
level__weight__gte=level.weight).all()
groups = user.groups.values_list('id', flat=True) if group_also else []
for i in object_levels:
if i.users.filter(pk=user.pk).exists():
return True
if group_also and i.groups.filter(pk__in=groups).exists():
return True
return False
return True
def get_users_with_level(self, **kwargs):
# TODO: implement
logger.debug('%s.get_users_with_level() called', unicode(self))
object_levels = (self.object_level_set.filter(**kwargs).select_related(
'level').prefetch_related('users').all())
users = []
for object_level in object_levels:
name = object_level.level.codename
olusers = object_level.users.all()
users.extend([(u, name) for u in olusers])
logger.debug('- %s: %s' % (name, [u.username for u in olusers]))
return users
return []
def get_groups_with_level(self):
# TODO: implement
logger.debug('%s.get_groups_with_level() called', unicode(self))
object_levels = (self.object_level_set.select_related(
'level').prefetch_related('groups').all())
groups = []
for object_level in object_levels:
name = object_level.level.codename
olgroups = object_level.groups.all()
groups.extend([(g, name) for g in olgroups])
logger.debug('- %s: %s' % (name, [g.name for g in olgroups]))
return groups
return []
@classmethod
def get_objects_with_level(cls, level, user,
......
No preview for this file type
......@@ -26,32 +26,15 @@ from .models import (activity_context, has_suffix, humanize_exception,
logger = getLogger(__name__)
class SubOperationMixin(object):
required_perms = ()
def create_activity(self, parent, user, kwargs):
if not parent:
raise TypeError("SubOperation can only be called with "
"parent_activity specified.")
return super(SubOperationMixin, self).create_activity(
parent, user, kwargs)
class Operation(object):
"""Base class for VM operations.
"""
async_queue = 'localhost.man'
required_perms = None
superuser_required = False
do_not_call_in_templates = True
abortable = False
has_percentage = False
@classmethod
def get_activity_code_suffix(cls):
return cls.id
def __call__(self, **kwargs):
return self.call(**kwargs)
......@@ -63,10 +46,12 @@ class Operation(object):
def __unicode__(self):
return self.name
def __prelude(self, kwargs):
def __prelude(self, request, kwargs):
"""This method contains the shared prelude of call and async.
"""
defaults = {'parent_activity': None, 'system': False, 'user': None}
self._operation.im_self.get_from_os(request)
defaults = {'system': False, 'user': None}
allargs = dict(defaults, **kwargs) # all arguments
auxargs = allargs.copy() # auxiliary (i.e. only for _operation) args
......@@ -75,11 +60,8 @@ class Operation(object):
skip_auth_check = auxargs.pop('system')
user = auxargs.pop('user')
parent_activity = auxargs.pop('parent_activity')
if parent_activity and user is None and not skip_auth_check:
user = allargs['user'] = parent_activity.user
if user is None: # parent was a system call
skip_auth_check = True
if user is None: # parent was a system call
skip_auth_check = True
# check for unexpected keyword arguments
argspec = getargspec(self._operation)
......@@ -93,12 +75,9 @@ class Operation(object):
self.check_auth(user)
self.check_precond()
activity = self.create_activity(
parent=parent_activity, user=user, kwargs=kwargs)
return activity, allargs, auxargs
return allargs, auxargs
def _exec_op(self, allargs, auxargs):
def _exec_op(self, request, allargs, auxargs):
"""Execute the operation inside the specified activity's context.
"""
# compile arguments for _operation
......@@ -110,13 +89,7 @@ class Operation(object):
if k in argspec.args}
arguments.update(auxargs)
with activity_context(allargs['activity'], on_abort=self.on_abort,
on_commit=self.on_commit) as act:
retval = self._operation(**arguments)
if (act.result is None and isinstance(
retval, (basestring, int, HumanReadableObject))):
act.result = retval
return retval
return self._operation(request, **arguments)
def _operation(self, **kwargs):
"""This method is the operation's particular implementation.
......@@ -125,25 +98,7 @@ class Operation(object):
"""
raise NotImplementedError
def async(self, **kwargs):
"""Execute the operation asynchronously.
Only a quick, preliminary check is ran before creating the associated
activity and queuing the job.
The returned value is the handle for the asynchronous job.
For more information, check the synchronous call's documentation.
"""
logger.info("%s called asynchronously on %s with the following "
"parameters: %r", self.__class__.__name__, self.subject,
kwargs)
activity, allargs, auxargs = self.__prelude(kwargs)
return self.async_operation.apply_async(
args=(self.id, self.subject.pk, activity.pk, allargs, auxargs, ),
queue=self.async_queue)
def call(self, **kwargs):
def call(self, request, **kwargs):
"""Execute the operation (synchronously).
Anticipated keyword arguments:
......@@ -159,9 +114,8 @@ class Operation(object):
logger.info("%s called (synchronously) on %s with the following "
"parameters: %r", self.__class__.__name__, self.subject,
kwargs)
activity, allargs, auxargs = self.__prelude(kwargs)
allargs['activity'] = activity
return self._exec_op(allargs, auxargs)
allargs, auxargs = self.__prelude(request, kwargs)
return self._exec_op(request, allargs, auxargs)
def check_precond(self):
pass
......@@ -185,11 +139,8 @@ class Operation(object):
def check_auth(self, user):
"""Check if user is permitted to run this operation on this instance
"""
self.check_perms(user)
def create_activity(self, parent, user, kwargs):
raise NotImplementedError
#TODO: implement
pass
def on_abort(self, activity, error):
"""This method is called when the operation aborts (i.e. raises an
......@@ -197,18 +148,6 @@ class Operation(object):
"""
pass
def get_activity_name(self, kwargs):
try:
return self.activity_name
except AttributeError:
try:
return self.name._proxy____args[0] # ewww!
except AttributeError:
raise ImproperlyConfigured(
"Set Operation.activity_name to an ugettext_nooped "
"string or a create_readable call, or override "
"get_activity_name to create a name dynamically")
def on_commit(self, activity):
"""This method is called when the operation executes successfully.
"""
......
......@@ -18,7 +18,7 @@
from __future__ import absolute_import
from dashboard.views.autocomplete import AclUserGroupAutocomplete, AclUserAutocomplete
from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops
from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops, FavouriteView
from django.conf.urls import url
from .views import (
......@@ -99,8 +99,8 @@ urlpatterns = [
# url(r'^node/activity/(?P<pk>\d+)/$', NodeActivityDetail.as_view(),
# name='dashboard.views.node-activity'),
#
# url(r'^favourite/$', FavouriteView.as_view(),
# name='dashboard.views.favourite'),
url(r'^favourite/$', FavouriteView.as_view(),
name='dashboard.views.favourite'),
# url(r'^group/delete/(?P<pk>\d+)/$', GroupDelete.as_view(),
# name="dashboard.views.delete-group"),
# url(r'^group/list/$', GroupList.as_view(),
......
......@@ -294,7 +294,7 @@ class OperationView(RedirectToLoginMixin, DetailView):
result = None
done = False
try:
task = self.get_op().async(user=request.user, **extra)
task = self.get_op().call(request, user=request.user, **extra)
except HumanReadableException as e:
e.send_message(request)
logger.exception("Could not start operation")
......
......@@ -21,6 +21,7 @@ import logging
from collections import OrderedDict
import openstack_api
from dashboard.models import Favourite
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
......@@ -110,8 +111,6 @@ class VmDetailView(LoginRequiredMixin, GraphMixin, DetailView):
context = super(VmDetailView, self).get_context_data(**kwargs)
instance = context['instance']
user = self.request.user
is_operator = instance.has_level(user, "operator")
is_owner = instance.has_level(user, "owner")
ops = get_operations(instance, user)
hide_tutorial = self.request.COOKIES.get(
"hide_tutorial_for_%s" % instance.pk) == "True"
......@@ -155,9 +154,7 @@ class VmDetailView(LoginRequiredMixin, GraphMixin, DetailView):
# context['ipv6_port'] = instance.get_connect_port(use_ipv6=True)
# resources forms
can_edit = (
instance.has_level(user, "owner") and
self.request.user.has_perm("vm.change_resources"))
can_edit = True
context['resources_form'] = VmResourcesForm(
can_edit=can_edit, instance=instance)
......@@ -173,11 +170,7 @@ class VmDetailView(LoginRequiredMixin, GraphMixin, DetailView):
context['client_download'] = self.request.COOKIES.get(
'downloaded_client')
# can link template
context['can_link_template'] = instance.template and is_operator
# is operator/owner
context['is_operator'] = is_operator
context['is_owner'] = is_owner
context['can_link_template'] = instance.template
# operation also allows RUNNING (if with_shutdown is present)
context['save_resources_enabled'] = instance.status in (
......@@ -307,7 +300,7 @@ def get_operations(instance, user):
try:
op = v.get_op_by_object(instance)
# op.check_auth(user)
# op.check_precond()
op.check_precond()
except PermissionDenied as e:
logger.debug('Not showing operation %s for %s: %s',
k, instance, unicode(e))
......@@ -743,26 +736,24 @@ vm_ops = OrderedDict([
('deploy', VmDeployView),
('wake_up', VmOperationView.factory(
op='wake_up', icon='sun-o', effect='success')),
# ('sleep', VmOperationView.factory(
# extra_bases=[TokenOperationView],
# op='sleep', icon='moon-o', effect='info')),
('sleep', VmOperationView.factory(
op='sleep', icon='moon-o', effect='info')),
# ('migrate', VmMigrateView),
# ('save_as_template', VmSaveView),
# ('reboot', VmOperationView.factory(
# op='reboot', icon='refresh', effect='warning')),
('reboot', VmOperationView.factory(
op='reboot', icon='refresh', effect='warning')),
# ('reset', VmOperationView.factory(
# op='reset', icon='bolt', effect='warning')),
# ('shutdown', VmOperationView.factory(
# op='shutdown', icon='power-off', effect='warning')),
# ('shut_off', VmOperationView.factory(
# op='shut_off', icon='plug', effect='warning')),
('shut_off', VmOperationView.factory(
op='shut_off', icon='plug', effect='warning')),
# ('recover', VmOperationView.factory(
# op='recover', icon='medkit', effect='warning')),
# ('nostate', VmStateChangeView),
# ('redeploy', RedeployView),
# ('destroy', VmOperationView.factory(
# extra_bases=[TokenOperationView],
# op='destroy', icon='times', effect='danger')),
('destroy', VmOperationView.factory(
op='destroy', icon='times', effect='danger')),
# ('create_disk', VmCreateDiskView),
# ('download_disk', VmDownloadDiskView),
# ('resize_disk', VmDiskModifyView.factory(
......@@ -790,8 +781,8 @@ vm_ops = OrderedDict([
# )),
# ('rename', VmRenameView),
])
#
#
def _get_activity_icon(act):
op = act.get_operation()
if op and op.id in vm_ops:
......@@ -1265,7 +1256,7 @@ def vm_activity(request, pk):
# activities = activities[:10]
response['connect_uri'] = instance.get_connect_uri()
response['human_readable_status'] = instance.get_status_display()
response['human_readable_status'] = '#TODO' #instance.get_status_display()
response['status'] = instance.status
response['icon'] = instance.get_status_icon()
latest = instance.get_latest_activity_in_progress()
......@@ -1275,8 +1266,8 @@ def vm_activity(request, pk):
context = {
'instance': instance,
# 'activities': activities,
# 'show_show_all': show_show_all,
'activities': (),
'show_show_all': False,
'ops': get_operations(instance, request.user),
}
......@@ -1299,19 +1290,19 @@ def vm_activity(request, pk):
)
#
#
# class FavouriteView(TemplateView):
#
# def post(self, *args, **kwargs):
# user = self.request.user
# vm = Instance.objects.get(pk=self.request.POST.get("vm"))
# if not vm.has_level(user, 'user'):
# raise PermissionDenied()
# try:
# Favourite.objects.get(instance=vm, user=user).delete()
# return HttpResponse("Deleted.")
# except Favourite.DoesNotExist:
# Favourite(instance=vm, user=user).save()
# return HttpResponse("Added.")
class FavouriteView(TemplateView):
def post(self, *args, **kwargs):
user = self.request.user
vm = Instance.objects.get(pk=self.request.POST.get("vm"))
if not vm.has_level(user, 'user'):
raise PermissionDenied()
try:
Favourite.objects.get(instance=vm, user=user).delete()
return HttpResponse("Deleted.")
except Favourite.DoesNotExist:
Favourite(instance=vm, user=user).save()
return HttpResponse("Added.")
#
#
# class TransferInstanceOwnershipConfirmView(TransferOwnershipConfirmView):
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-02-22 09:53
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('vm', '0005_remove_instance_owner'),
]
operations = [
migrations.RemoveField(
model_name='instance',
name='name',
),
]
......@@ -209,7 +209,7 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel):
return 'template.%d' % self.pk
class Instance(AclBase, OperatedMixin, TimeStampedModel):
class Instance(OperatedMixin, TimeStampedModel):
"""Virtual machine instance.
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment