Improved VPS backend

This commit is contained in:
Marc Aymerich 2015-09-21 13:57:15 +00:00
parent 760d6956de
commit d6925abd72
7 changed files with 74 additions and 50 deletions

View File

@ -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

View File

@ -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),

View File

@ -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"))

View File

@ -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)

View File

@ -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

View File

@ -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',),
)

View File

@ -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">