Improved VPS backend
This commit is contained in:
parent
760d6956de
commit
d6925abd72
6
TODO.md
6
TODO.md
|
@ -124,16 +124,12 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
|
||||||
|
|
||||||
* make account available on all admin forms
|
* make account available on all admin forms
|
||||||
|
|
||||||
# WPMU blog traffic
|
|
||||||
|
|
||||||
* more robust backend error handling, continue executing but exit code > 0 if failure: failing_cmd || exit_code=1 and don't forget to call super.commit()!!
|
* more robust backend error handling, continue executing but exit code > 0 if failure: failing_cmd || exit_code=1 and don't forget to call super.commit()!!
|
||||||
|
|
||||||
* website directives uniquenes validation on serializers
|
* website directives uniquenes validation on serializers
|
||||||
|
|
||||||
+ is_Active custom filter with support for instance.account.is_Active annotate with F() needed (django 1.8)
|
+ is_Active custom filter with support for instance.account.is_Active annotate with F() needed (django 1.8)
|
||||||
|
|
||||||
# delete apache logs and php logs
|
|
||||||
|
|
||||||
* document service help things: discount/refound/compensation effect and metric table
|
* document service help things: discount/refound/compensation effect and metric table
|
||||||
* Document metric interpretation help_text
|
* Document metric interpretation help_text
|
||||||
* document plugin serialization, data_serializer?
|
* document plugin serialization, data_serializer?
|
||||||
|
@ -385,8 +381,6 @@ Case
|
||||||
# Mailer: mark as sent
|
# Mailer: mark as sent
|
||||||
|
|
||||||
|
|
||||||
# Implement wordpressmu change password or remove password from the form
|
|
||||||
|
|
||||||
# Deprecate orchestra start/stop/restart services management commands?
|
# Deprecate orchestra start/stop/restart services management commands?
|
||||||
|
|
||||||
# Enable/disable ignore period orders list filter
|
# Enable/disable ignore period orders list filter
|
||||||
|
|
|
@ -236,45 +236,50 @@ class ChangePasswordAdminMixin(object):
|
||||||
name='%s_%s_change_password' % info)
|
name='%s_%s_change_password' % info)
|
||||||
] + super(ChangePasswordAdminMixin, self).get_urls()
|
] + super(ChangePasswordAdminMixin, self).get_urls()
|
||||||
|
|
||||||
|
def get_change_password_username(self, obj):
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
@sensitive_post_parameters_m
|
@sensitive_post_parameters_m
|
||||||
def change_password(self, request, id, form_url=''):
|
def change_password(self, request, id, form_url=''):
|
||||||
if not self.has_change_permission(request):
|
if not self.has_change_permission(request):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
# TODO use this insetad of self.get_object(), in other places
|
# TODO use this insetad of self.get_object(), in other places
|
||||||
user = get_object_or_404(self.get_queryset(request), pk=id)
|
obj = get_object_or_404(self.get_queryset(request), pk=id)
|
||||||
|
|
||||||
related = []
|
related = []
|
||||||
|
for obj_name_attr in ('username', 'name', 'hostname'):
|
||||||
try:
|
try:
|
||||||
# don't know why getattr(user, 'username', user.name) doesn't work
|
obj_name = getattr(obj, obj_name_attr)
|
||||||
username = user.username
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
username = user.name
|
pass
|
||||||
if hasattr(user, 'account'):
|
|
||||||
account = user.account
|
|
||||||
if user.account.username == username:
|
|
||||||
related.append(user.account)
|
|
||||||
else:
|
else:
|
||||||
account = user
|
break
|
||||||
if account.username == username:
|
if hasattr(obj, 'account'):
|
||||||
|
account = obj.account
|
||||||
|
if obj.account.username == obj_name:
|
||||||
|
related.append(obj.account)
|
||||||
|
else:
|
||||||
|
account = obj
|
||||||
|
if account.username == obj_name:
|
||||||
for rel in account.get_related_passwords():
|
for rel in account.get_related_passwords():
|
||||||
if not isinstance(user, type(rel)):
|
if not isinstance(obj, type(rel)):
|
||||||
related.append(rel)
|
related.append(rel)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = self.change_password_form(user, request.POST, related=related)
|
form = self.change_password_form(obj, request.POST, related=related)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
change_message = self.construct_change_message(request, form, None)
|
change_message = self.construct_change_message(request, form, None)
|
||||||
self.log_change(request, user, change_message)
|
self.log_change(request, obj, change_message)
|
||||||
msg = _('Password changed successfully.')
|
msg = _('Password changed successfully.')
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
update_session_auth_hash(request, form.user) # This is safe
|
update_session_auth_hash(request, form.user) # This is safe
|
||||||
return HttpResponseRedirect('..')
|
return HttpResponseRedirect('..')
|
||||||
else:
|
else:
|
||||||
form = self.change_password_form(user, related=related)
|
form = self.change_password_form(obj, related=related)
|
||||||
|
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(user._meta.verbose_name.capitalize(), {
|
(obj._meta.verbose_name.capitalize(), {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
'fields': ('password1', 'password2')
|
'fields': ('password1', 'password2')
|
||||||
}),
|
}),
|
||||||
|
@ -285,9 +290,10 @@ class ChangePasswordAdminMixin(object):
|
||||||
'fields': ('password1_%i' % ix, 'password2_%i' % ix)
|
'fields': ('password1_%i' % ix, 'password2_%i' % ix)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
obj_username = self.get_change_password_username(obj)
|
||||||
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
|
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
|
||||||
context = {
|
context = {
|
||||||
'title': _('Change password: %s') % escape(username),
|
'title': _('Change password: %s') % obj_username,
|
||||||
'adminform': adminForm,
|
'adminform': adminForm,
|
||||||
'errors': admin.helpers.AdminErrorList(form, []),
|
'errors': admin.helpers.AdminErrorList(form, []),
|
||||||
'form_url': form_url,
|
'form_url': form_url,
|
||||||
|
@ -299,7 +305,8 @@ class ChangePasswordAdminMixin(object):
|
||||||
'has_change_permission': True,
|
'has_change_permission': True,
|
||||||
'has_absolute_url': False,
|
'has_absolute_url': False,
|
||||||
'opts': self.model._meta,
|
'opts': self.model._meta,
|
||||||
'original': user,
|
'original': obj,
|
||||||
|
'obj_username': obj_username,
|
||||||
'save_as': False,
|
'save_as': False,
|
||||||
'show_save': True,
|
'show_save': True,
|
||||||
'password': random_ascii(10),
|
'password': random_ascii(10),
|
||||||
|
|
|
@ -19,6 +19,7 @@ class WordPressForm(SaaSBaseForm):
|
||||||
help_text = 'Admin URL: <a href="{0}">{0}</a>'.format(admin_url)
|
help_text = 'Admin URL: <a href="{0}">{0}</a>'.format(admin_url)
|
||||||
self.fields['site_url'].help_text = mark_safe(help_text)
|
self.fields['site_url'].help_text = mark_safe(help_text)
|
||||||
|
|
||||||
|
|
||||||
class WordPressDataSerializer(serializers.Serializer):
|
class WordPressDataSerializer(serializers.Serializer):
|
||||||
email = serializers.EmailField(label=_("Email"))
|
email = serializers.EmailField(label=_("Email"))
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.forms import UserCreationForm, NonStoredUserChangeForm
|
from orchestra.forms import UserCreationForm, NonStoredUserChangeForm
|
||||||
|
@ -11,7 +11,7 @@ from orchestra.forms import UserCreationForm, NonStoredUserChangeForm
|
||||||
from .models import VPS
|
from .models import VPS
|
||||||
|
|
||||||
|
|
||||||
class VPSAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
class VPSAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = ('hostname', 'type', 'template', 'account_link')
|
list_display = ('hostname', 'type', 'template', 'account_link')
|
||||||
list_filter = ('type', 'template')
|
list_filter = ('type', 'template')
|
||||||
form = NonStoredUserChangeForm
|
form = NonStoredUserChangeForm
|
||||||
|
@ -40,12 +40,8 @@ class VPSAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
)
|
)
|
||||||
actions = (list_accounts,)
|
actions = (list_accounts,)
|
||||||
|
|
||||||
def get_urls(self):
|
def get_change_password_username(self, obj):
|
||||||
useradmin = UserAdmin(VPS, self.admin_site)
|
return 'root@%s' % obj.hostname
|
||||||
return [
|
|
||||||
url(r'^(\d+)/password/$',
|
|
||||||
self.admin_site.admin_view(useradmin.user_change_password))
|
|
||||||
] + super(VPSAdmin, self).get_urls()
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(VPS, VPSAdmin)
|
admin.site.register(VPS, VPSAdmin)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import textwrap
|
||||||
from orchestra.contrib.orchestration import ServiceController
|
from orchestra.contrib.orchestration import ServiceController
|
||||||
from orchestra.contrib.resources import ServiceMonitor
|
from orchestra.contrib.resources import ServiceMonitor
|
||||||
|
|
||||||
|
from . import settings
|
||||||
|
|
||||||
|
|
||||||
class ProxmoxOVZ(ServiceController):
|
class ProxmoxOVZ(ServiceController):
|
||||||
model = 'vps.VPS'
|
model = 'vps.VPS'
|
||||||
|
@ -34,39 +36,58 @@ class ProxmoxOVZ(ServiceController):
|
||||||
super(ProxmoxOVZ, self).prepare()
|
super(ProxmoxOVZ, self).prepare()
|
||||||
self.append(self.GET_PROXMOX_INFO)
|
self.append(self.GET_PROXMOX_INFO)
|
||||||
|
|
||||||
def get_vzset_args(self, vps):
|
def get_vzset_args(self, context):
|
||||||
args = []
|
args = list(settings.VPS_DEFAULT_VZSET_ARGS)
|
||||||
for resource, arg_name in self.RESOURCES:
|
for resource, arg_name in self.RESOURCES:
|
||||||
try:
|
try:
|
||||||
allocation = getattr(vps.resources, resource).allocated
|
allocation = context[resource]
|
||||||
except AttributeError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
args.append('--%s %i' % (arg_name, allocation))
|
args.append('--%s %i' % (arg_name, allocation))
|
||||||
return ' '.join(args)
|
return ' '.join(args)
|
||||||
|
|
||||||
|
def run_ssh_commands(self, ssh_commands):
|
||||||
|
commands = '\n '.join(ssh_commands)
|
||||||
|
self.append(textwrap.dedent("""\
|
||||||
|
cat << EOF | ssh root@${info[1]}
|
||||||
|
%s
|
||||||
|
EOF""") % commands
|
||||||
|
)
|
||||||
|
|
||||||
def save(self, vps):
|
def save(self, vps):
|
||||||
|
# TODO create the container
|
||||||
context = self.get_context(vps)
|
context = self.get_context(vps)
|
||||||
self.append('info=( $(get_vz_info %(hostname)s) )' % context)
|
self.append(textwrap.dedent("""
|
||||||
vzset_args = self.get_vzset_args(vps)
|
info=( $(get_vz_info %(hostname)s) )
|
||||||
|
echo "Managing ${info[@]}"\
|
||||||
|
""") % context
|
||||||
|
)
|
||||||
|
ssh_commands = []
|
||||||
|
vzset_args = self.get_vzset_args(context)
|
||||||
if vzset_args:
|
if vzset_args:
|
||||||
context['vzset_args'] = vzset_args
|
context['vzset_args'] = vzset_args
|
||||||
self.append(textwrap.dedent("""\
|
ssh_commands.append("pvectl vzset ${info[0]} %(vzset_args)s" % context)
|
||||||
cat << EOF | ssh root@${info[1]}
|
|
||||||
pvectl vzset ${info[0]} %(vzset_args)s
|
|
||||||
EOF""") % context
|
|
||||||
)
|
|
||||||
if hasattr(vps, 'password'):
|
if hasattr(vps, 'password'):
|
||||||
context['password'] = vps.password.replace('$', '\\$')
|
context['password'] = vps.password.replace('$', '\\$')
|
||||||
self.append(textwrap.dedent("""\
|
ssh_commands.append(textwrap.dedent("""\
|
||||||
cat << EOF | ssh root@${info[1]}
|
echo 'root:%(password)s' \\
|
||||||
echo 'root:%(password)s' | vzctl exec ${info[0]} chpasswd -e
|
| chroot /var/lib/vz/private/${info[0]} chpasswd -e""") % context
|
||||||
EOF""") % context
|
|
||||||
)
|
)
|
||||||
|
self.run_ssh_commands(ssh_commands)
|
||||||
|
|
||||||
def get_context(self, vps):
|
def get_context(self, vps):
|
||||||
return {
|
context = {
|
||||||
'hostname': vps.hostname,
|
'hostname': vps.hostname,
|
||||||
}
|
}
|
||||||
|
for resource, __ in self.RESOURCES:
|
||||||
|
try:
|
||||||
|
allocation = getattr(vps.resources, resource).allocated
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
context[resource] = allocation
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
# TODO rename to proxmox
|
# TODO rename to proxmox
|
||||||
|
|
|
@ -27,3 +27,8 @@ VPS_DEFAULT_TEMPLATE = Setting('VPS_DEFAULT_TEMPLATE',
|
||||||
'debian7',
|
'debian7',
|
||||||
choices=VPS_TEMPLATES
|
choices=VPS_TEMPLATES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
VPS_DEFAULT_VZSET_ARGS = Setting('VPS_DEFAULT_VZSET_ARGS',
|
||||||
|
('--onboot yes',),
|
||||||
|
)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div>
|
<div>
|
||||||
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1" />{% endif %}
|
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1" />{% endif %}
|
||||||
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}" />{% endif %}
|
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}" />{% endif %}
|
||||||
<p>{% blocktrans with username=original %}Enter a new password for the user <strong>{{ username }}</strong>, suggestion '{{ password }}'.{% endblocktrans %}</p>
|
<p>{% blocktrans with username=obj_username %}Enter a new password for the user <strong>{{ username }}</strong>, suggestion '{{ password }}'.{% endblocktrans %}</p>
|
||||||
|
|
||||||
{% if errors %}
|
{% if errors %}
|
||||||
<p class="errornote">
|
<p class="errornote">
|
||||||
|
|
Loading…
Reference in New Issue