Fixed account delete/disable
This commit is contained in:
parent
2491367d42
commit
912ee9744e
|
@ -10,7 +10,7 @@ from django.utils import timezone
|
|||
from django.utils.encoding import force_text
|
||||
from django.utils.html import format_html
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||
|
||||
from orchestra.core import services
|
||||
|
||||
|
@ -146,16 +146,24 @@ def delete_related_services(modeladmin, request, queryset):
|
|||
# The user has already confirmed the deletion.
|
||||
# Do the deletion and return a None to display the change list view again.
|
||||
if request.POST.get('post'):
|
||||
n = queryset.count()
|
||||
if n:
|
||||
accounts = len(queryset)
|
||||
msg = _("Related services deleted and account disabled.")
|
||||
for account in queryset:
|
||||
account.is_active = False
|
||||
account.save(update_fields=('is_active',))
|
||||
modeladmin.log_change(request, account, msg)
|
||||
if accounts:
|
||||
relateds = len(to_delete)
|
||||
for obj in to_delete:
|
||||
obj_display = force_text(obj)
|
||||
modeladmin.log_deletion(request, obj, obj_display)
|
||||
# TODO This probably will fail in certain conditions, just capture exception
|
||||
obj.delete()
|
||||
modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
|
||||
"count": n, "items": model_ngettext(modeladmin.opts, n)
|
||||
}, messages.SUCCESS)
|
||||
context = {
|
||||
'accounts': accounts,
|
||||
'relateds': relateds,
|
||||
}
|
||||
msg = _("Successfully disabled %(accounts)d account and deleted %(relateds)d related services.") % context
|
||||
modeladmin.message_user(request, msg, messages.SUCCESS)
|
||||
# Return None to display the change list page again.
|
||||
return None
|
||||
|
||||
|
@ -165,7 +173,7 @@ def delete_related_services(modeladmin, request, queryset):
|
|||
objects_name = force_text(opts.verbose_name_plural)
|
||||
|
||||
context = dict(
|
||||
modeladmin.admin_site.each_context(request),
|
||||
admin_site.each_context(request),
|
||||
title=_("Are you sure?"),
|
||||
objects_name=objects_name,
|
||||
deletable_objects=[related_services],
|
||||
|
@ -174,11 +182,85 @@ def delete_related_services(modeladmin, request, queryset):
|
|||
opts=opts,
|
||||
action_checkbox_name=helpers.ACTION_CHECKBOX_NAME,
|
||||
)
|
||||
request.current_app = modeladmin.admin_site.name
|
||||
request.current_app = admin_site.name
|
||||
# Display the confirmation page
|
||||
return TemplateResponse(request, modeladmin.delete_selected_confirmation_template or [
|
||||
"admin/%s/%s/delete_selected_confirmation.html" % (app_label, opts.model_name),
|
||||
"admin/%s/delete_selected_confirmation.html" % app_label,
|
||||
"admin/delete_selected_confirmation.html"
|
||||
], context)
|
||||
template = 'admin/%s/%s/delete_related_services_confirmation.html' % (app_label, opts.model_name)
|
||||
return TemplateResponse(request, template, context)
|
||||
delete_related_services.short_description = _("Delete related services")
|
||||
|
||||
|
||||
def disable_selected(modeladmin, request, queryset):
|
||||
opts = modeladmin.model._meta
|
||||
app_label = opts.app_label
|
||||
|
||||
# The user has already confirmed the deletion.
|
||||
# Do the disable and return a None to display the change list view again.
|
||||
if request.POST.get('post'):
|
||||
n = 0
|
||||
for account in queryset:
|
||||
account.disable()
|
||||
modeladmin.log_change(request, account, _("Disabled"))
|
||||
n += 1
|
||||
modeladmin.message_user(request, ungettext(
|
||||
_("One account has been successfully disabled."),
|
||||
_("%i accounts have been successfully disabled.") % n,
|
||||
n)
|
||||
)
|
||||
return None
|
||||
|
||||
user = request.user
|
||||
admin_site = modeladmin.admin_site
|
||||
|
||||
def format(obj):
|
||||
has_admin = obj.__class__ in admin_site._registry
|
||||
opts = obj._meta
|
||||
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj))
|
||||
if has_admin:
|
||||
try:
|
||||
admin_url = reverse(
|
||||
'admin:%s_%s_change' % (opts.app_label, opts.model_name),
|
||||
None,
|
||||
(quote(obj._get_pk_val()),)
|
||||
)
|
||||
except NoReverseMatch:
|
||||
# Change url doesn't exist -- don't display link to edit
|
||||
return no_edit_link
|
||||
|
||||
p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts))
|
||||
if not user.has_perm(p):
|
||||
perms_needed.add(opts.verbose_name)
|
||||
# Display a link to the admin page.
|
||||
context = (capfirst(opts.verbose_name), admin_url, obj)
|
||||
return format_html('{}: <a href="{}">{}</a>', *context)
|
||||
else:
|
||||
# Don't display link to edit, because it either has no
|
||||
# admin or is edited inline.
|
||||
return no_edit_link
|
||||
|
||||
display = []
|
||||
for account in queryset:
|
||||
current = []
|
||||
for related in account.get_services_to_disable():
|
||||
current.append(format(related))
|
||||
display.append([format(account), current])
|
||||
|
||||
if len(queryset) == 1:
|
||||
objects_name = force_text(opts.verbose_name)
|
||||
else:
|
||||
objects_name = force_text(opts.verbose_name_plural)
|
||||
|
||||
context = dict(
|
||||
admin_site.each_context(request),
|
||||
title=_("Are you sure?"),
|
||||
objects_name=objects_name,
|
||||
deletable_objects=display,
|
||||
queryset=queryset,
|
||||
opts=opts,
|
||||
action_checkbox_name=helpers.ACTION_CHECKBOX_NAME,
|
||||
)
|
||||
request.current_app = admin_site.name
|
||||
template = 'admin/%s/%s/disable_selected_confirmation.html' % (app_label, opts.model_name)
|
||||
return TemplateResponse(request, template, context)
|
||||
disable_selected.short_description = _("Disable selected accounts")
|
||||
disable_selected.url = 'disable'
|
||||
disable_selected.tool_description = _("Disable")
|
||||
|
|
|
@ -13,12 +13,12 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||
from orchestra.admin.actions import SendEmail, disable
|
||||
from orchestra.admin.actions import SendEmail
|
||||
from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url
|
||||
from orchestra.core import services, accounts
|
||||
from orchestra.forms import UserChangeForm
|
||||
|
||||
from .actions import list_contacts, service_report, delete_related_services
|
||||
from .actions import list_contacts, service_report, delete_related_services, disable_selected
|
||||
from .filters import HasMainUserListFilter
|
||||
from .forms import AccountCreationForm
|
||||
from .models import Account
|
||||
|
@ -61,8 +61,10 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
|||
filter_horizontal = ()
|
||||
change_readonly_fields = ('username', 'main_systemuser_link', 'is_active')
|
||||
change_form_template = 'admin/accounts/account/change_form.html'
|
||||
actions = [disable, list_contacts, service_report, SendEmail(), delete_related_services]
|
||||
change_view_actions = [disable, service_report]
|
||||
actions = (
|
||||
disable_selected, delete_related_services, list_contacts, service_report, SendEmail()
|
||||
)
|
||||
change_view_actions = (disable_selected, service_report)
|
||||
ordering = ()
|
||||
|
||||
main_systemuser_link = admin_link('main_systemuser')
|
||||
|
@ -109,6 +111,12 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
|||
else:
|
||||
super(AccountAdmin, self).save_model(request, obj, form, change)
|
||||
|
||||
def get_actions(self, request):
|
||||
actions = super(AccountAdmin, self).get_actions(request)
|
||||
if 'delete_selected' in actions:
|
||||
del actions['delete_selected']
|
||||
return actions
|
||||
|
||||
|
||||
admin.site.register(Account, AccountAdmin)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from orchestra.contrib.orchestration.middlewares import OperationsMiddleware
|
||||
from orchestra.contrib.orchestration import Operation
|
||||
from orchestra.core import services
|
||||
from orchestra.utils.mail import send_email_template
|
||||
|
||||
from . import settings
|
||||
|
@ -82,13 +83,17 @@ class Account(auth.AbstractBaseUser):
|
|||
self.save(update_fields=['is_active'])
|
||||
self.notify_related()
|
||||
|
||||
def notify_related(self):
|
||||
# Trigger save() on related objects that depend on this account
|
||||
def get_services_to_disable(self):
|
||||
for rel in self._meta.get_all_related_objects():
|
||||
source = getattr(rel, 'related_model', rel.model)
|
||||
if source in services and hasattr(source, 'active'):
|
||||
for obj in getattr(self, rel.get_accessor_name()).all():
|
||||
OperationsMiddleware.collect(Operation.SAVE, instance=obj, update_fields=[])
|
||||
yield obj
|
||||
|
||||
def notify_related(self):
|
||||
""" Trigger save() on related objects that depend on this account """
|
||||
for obj in self.get_services_to_disable():
|
||||
OperationsMiddleware.collect(Operation.SAVE, instance=obj, update_fields=[])
|
||||
|
||||
def send_email(self, template, context, email_from=None, contacts=[], attachments=[], html=None):
|
||||
contacts = self.contacts.filter(email_usages=contacts)
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{% extends "admin/delete_selected_confirmation.html" %}
|
||||
{% load i18n l10n admin_urls %}
|
||||
|
||||
{% block content %}
|
||||
{% if perms_lacking %}
|
||||
<p>{% blocktrans %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for obj in perms_lacking %}
|
||||
<li>{{ obj }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif protected %}
|
||||
<p>{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for obj in protected %}
|
||||
<li>{{ obj }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>{% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}</p>
|
||||
{% include "admin/includes/object_delete_summary.html" %}
|
||||
<h2>{% trans "Objects" %}</h2>
|
||||
{% for deletable_object in deletable_objects %}
|
||||
<ul>{{ deletable_object|unordered_list }}</ul>
|
||||
{% endfor %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<div>
|
||||
{% for obj in queryset %}
|
||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||
{% endfor %}
|
||||
<input type="hidden" name="action" value="delete_related_services" />
|
||||
<input type="hidden" name="post" value="yes" />
|
||||
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
|
||||
<a href="#" onclick="window.history.back(); return false;" class="button cancel-link">{% trans "No, take me back" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n l10n admin_urls %}
|
||||
|
||||
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
› {% trans 'Disable accounts' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if perms_lacking %}
|
||||
<p>{% blocktrans %}Disabling the selected {{ objects_name }} would result in disabling related objects, but your account doesn't have permission to disable the following types of objects:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for obj in perms_lacking %}
|
||||
<li>{{ obj }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif protected %}
|
||||
<p>{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for obj in protected %}
|
||||
<li>{{ obj }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>{% blocktrans %}Are you sure you want to disable the selected {{ objects_name }}? All of the following objects and their related items will be disabled:{% endblocktrans %}</p>
|
||||
<h2>{% trans "Objects" %}</h2>
|
||||
{% for deletable_object in deletable_objects %}
|
||||
<ul>{{ deletable_object|unordered_list }}</ul>
|
||||
{% endfor %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<div>
|
||||
{% for obj in queryset %}
|
||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||
{% endfor %}
|
||||
<input type="hidden" name="action" value="disable_selected" />
|
||||
<input type="hidden" name="post" value="yes" />
|
||||
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
|
||||
<a href="#" onclick="window.history.back(); return false;" class="button cancel-link">{% trans "No, take me back" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -120,10 +120,12 @@ class Resource(models.Model):
|
|||
# This only works on tests (multiprocessing used on real deployments)
|
||||
apps.get_app_config('resources').reload_relations()
|
||||
|
||||
def sync_periodic_task(self):
|
||||
def sync_periodic_task(self, delete=False):
|
||||
""" sync periodic task on save/delete resource operations """
|
||||
name = 'monitor.%s' % str(self)
|
||||
if self.pk and self.crontab and self.is_active:
|
||||
if delete or not self.crontab or not self.is_active:
|
||||
PeriodicTask.objects.filter(name=name).delete()
|
||||
elif self.pk:
|
||||
try:
|
||||
task = PeriodicTask.objects.get(name=name)
|
||||
except PeriodicTask.DoesNotExist:
|
||||
|
@ -138,8 +140,6 @@ class Resource(models.Model):
|
|||
if task.crontab != self.crontab:
|
||||
task.crontab = self.crontab
|
||||
task.save(update_fields=['crontab'])
|
||||
else:
|
||||
PeriodicTask.objects.filter(name=name).delete()
|
||||
|
||||
def get_model_path(self, monitor):
|
||||
""" returns a model path between self.content_type and monitor.model """
|
||||
|
|
|
@ -5,8 +5,14 @@ from .models import Resource
|
|||
|
||||
|
||||
@receiver(post_save, sender=Resource, dispatch_uid="resources.sync_periodic_task")
|
||||
@receiver(post_delete, sender=Resource, dispatch_uid="resources.sync_periodic_task")
|
||||
def sync_periodic_task(sender, **kwargs):
|
||||
""" useing signals instead of Model.delete() override beucause of admin bulk delete() """
|
||||
instance = kwargs['instance']
|
||||
instance.sync_periodic_task()
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Resource, dispatch_uid="resources.delete_periodic_task")
|
||||
def delete_periodic_task(sender, **kwargs):
|
||||
""" useing signals instead of Model.delete() override beucause of admin bulk delete() """
|
||||
instance = kwargs['instance']
|
||||
instance.sync_periodic_task(delete=True)
|
||||
|
|
|
@ -69,8 +69,10 @@ class SaaSWebTraffic(ServiceMonitor):
|
|||
try:
|
||||
with open(access_log, 'r') as handler:
|
||||
for line in handler.readlines():
|
||||
meta, request, response, hostname, __ = line.split('"')
|
||||
line = line.split()
|
||||
meta = line[:4]
|
||||
host, __, __, date, tz = meta.split()
|
||||
size, hostname = line[-2:]
|
||||
if host in {ignore_hosts}:
|
||||
continue
|
||||
try:
|
||||
|
@ -78,7 +80,7 @@ class SaaSWebTraffic(ServiceMonitor):
|
|||
except KeyError:
|
||||
continue
|
||||
else:
|
||||
# [16/Sep/2015:11:40:38 +0200]
|
||||
# [16/Sep/2015:11:40:38
|
||||
day, month, date = date[1:].split('/')
|
||||
year, hour, min, sec = date.split(':')
|
||||
date = year + months[month] + day + hour + min + sec
|
||||
|
|
|
@ -116,25 +116,21 @@ class Apache2Backend(ServiceController):
|
|||
if context['server_name'] and site.active:
|
||||
self.append(textwrap.dedent("""
|
||||
# Enable site %(site_name)s
|
||||
if [[ ! -f %(sites_enabled)s ]]; then
|
||||
a2ensite %(site_unique_name)s.conf
|
||||
UPDATED_APACHE=1
|
||||
fi""") % context
|
||||
[[ $(a2ensite %(site_unique_name)s) =~ "already enabled" ]] || UPDATED_APACHE=1\
|
||||
""") % context
|
||||
)
|
||||
else:
|
||||
self.append(textwrap.dedent("""
|
||||
# Disable site %(site_name)s
|
||||
if [[ -f %(sites_enabled)s ]]; then
|
||||
a2dissite %(site_unique_name)s.conf;
|
||||
UPDATED_APACHE=1
|
||||
fi""") % context
|
||||
[[ $(a2dissite %(site_unique_name)s) =~ "already disabled" ]] || UPDATED_APACHE=1\
|
||||
""") % context
|
||||
)
|
||||
|
||||
def delete(self, site):
|
||||
context = self.get_context(site)
|
||||
self.append(textwrap.dedent("""
|
||||
# Remove site configuration for %(site_name)s
|
||||
a2dissite %(site_unique_name)s.conf && UPDATED_APACHE=1
|
||||
[[ $(a2dissite %(site_unique_name)s) =~ "already disabled" ]] || UPDATED_APACHE=1
|
||||
rm -f %(sites_available)s\
|
||||
""") % context
|
||||
)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
SLICE_ID=1513
|
||||
|
||||
|
||||
slice_req = requests.get('https://controller.community-lab.net/api/slices/%i' % SLICE_ID)
|
||||
slice = json.loads(slice_req.content)
|
||||
for sliver in slice['slivers']:
|
||||
sliver_req = requests.get(sliver['uri'])
|
||||
sliver = json.loads(sliver_req.content)
|
||||
print sliver['mgmt_net']['address']
|
Loading…
Reference in New Issue