Commit 1bdb6f86 authored by Vangelis Koukis's avatar Vangelis Koukis
Browse files

Merge branch 'admin'

parents 74e66dce 828803e2
......@@ -54,15 +54,3 @@ $('.add-row').live('click', function() {
$('.delete-row').live('click', function() {
$(this).parents('tr').remove();
});
$('.needs-confirm').live('click', function() {
$('div.alert-message').show('fast');
$('div.actions').hide('fast');
return false;
});
$('.alert-close').live('click', function() {
$('div.alert-message').hide('fast');
$('div.actions').show('fast');
return false;
});
......@@ -17,25 +17,41 @@
<ul class="tabs">
<li{% ifequal tab "home" %} class="active"{% endifequal %}>
<a href="{% url admin.views.index %}">Home</a>
<a href="{% url synnefo.admin.views.index %}">Home</a>
</li>
<li{% ifequal tab "flavors" %} class="active"{% endifequal %}>
<a href="{% url admin.views.flavors_list %}">Flavors</a>
<a href="{% url synnefo.admin.views.flavors_list %}">Flavors</a>
</li>
<li{% ifequal tab "images" %} class="active"{% endifequal %}>
<a href="{% url admin.views.images_list %}">Images</a>
<a href="{% url synnefo.admin.views.images_list %}">Images</a>
</li>
<li{% ifequal tab "servers" %} class="active"{% endifequal %}>
<a href="{% url admin.views.servers_list %}">Servers</a>
<a href="{% url synnefo.admin.views.servers_list %}">Servers</a>
</li>
<li{% ifequal tab "users" %} class="active"{% endifequal %}>
<a href="{% url admin.views.users_list %}">Users</a>
<a href="{% url synnefo.admin.views.users_list %}">Users</a>
</li>
<li{% ifequal tab "invitations" %} class="active"{% endifequal %}>
<a href="{% url admin.views.invitations_list %}">Invitations</a>
<a href="{% url synnefo.admin.views.invitations_list %}">Invitations</a>
</li>
</ul>
{% if all_states %}
<div class="row">
<div class="span3 columns">
<h4>Filter by state:</h4>
</div>
<div class="span13 columns">
<ul class="pills">
{% for state in all_states %}
<li class="{% if state in filters %}active{% endif %}">
<a href="?toggle_filter={{ state }}">{{ state }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% block body %}{% endblock %}
</div>
</body>
......
{% extends "base.html" %}
{% block body %}
<form action="{% url admin.views.flavors_create %}" method="post">
<form action="{% url synnefo.admin.views.flavors_create %}" method="post">
<div class="clearfix">
<label for="flavor-cpu">CPUs</label>
<div class="input">
......
......@@ -2,7 +2,7 @@
{% block body %}
<form action="{% url admin.views.flavors_modify flavor.id %}" method="post">
<form action="{% url synnefo.admin.views.flavors_modify flavor.id %}" method="post">
<div class="clearfix">
<label for="flavor-id">ID</label>
<div class="input">
......@@ -44,11 +44,22 @@
</div>
</div>
<div class="clearfix">
<label for="flavor-deleted">Deleted</label>
<div class="input">
<ul class="inputs-list">
<li>
<label>
<input type="checkbox" id="flavor-deleted" name="deleted"{% if flavor.deleted %} checked{% endif %}>
</label>
</li>
</ul>
</div>
</div>
<div class="actions">
<button type="submit" class="btn primary">Save Changes</button>
<button type="reset" class="btn">Reset</button>
&nbsp;&nbsp;
<a class="btn danger" href="{% url admin.views.flavors_delete flavor.id %}">Delete Flavor</a>
</div>
</form>
......
......@@ -9,21 +9,23 @@
<th>CPUs</th>
<th>RAM</th>
<th>Disk</th>
<th>Deleted</th>
</tr>
</thead>
<tbody>
{% for flavor in flavors %}
<tr>
<td><a href="{% url admin.views.flavors_info flavor.id %}">{{ flavor.id }}</a></td>
<td><a href="{% url admin.views.flavors_info flavor.id %}">{{ flavor.name }}</a></td>
<td><a href="{% url synnefo.admin.views.flavors_info flavor.id %}">{{ flavor.id }}</a></td>
<td><a href="{% url synnefo.admin.views.flavors_info flavor.id %}">{{ flavor.name }}</a></td>
<td>{{ flavor.cpu }}</td>
<td>{{ flavor.ram }} MiB</td>
<td>{{ flavor.disk }} GiB</td>
<td>{{ flavor.deleted }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="btn success" href="{% url admin.views.flavors_create %}">Create a new Flavor</a>
<a class="btn success" href="{% url synnefo.admin.views.flavors_create %}">Create a new Flavor</a>
<br /><br />
{% endblock body %}
{% extends "base.html" %}
{% block body %}
<form action="{% url admin.views.images_modify image.id %}" method="post">
<form action="{% url synnefo.admin.views.images_modify image.id %}" method="post">
<div class="row">
<div class="span7 columns">
......@@ -65,7 +65,7 @@
</div>
<div class="clearfix">
<label for="image-format">Public</label>
<label for="image-public">Public</label>
<div class="input">
<ul class="inputs-list">
<li>
......
......@@ -17,9 +17,9 @@
<tbody>
{% for image in images %}
<tr>
<td><a href="{% url admin.views.images_info image.id %}">{{ image.id }}</a></td>
<td><a href="{% url admin.views.images_info image.id %}">{{ image.name }}</a></td>
<td>{% if image.owner %}<a href="{% url admin.views.users_info image.owner.id %}">{{ image.owner.name }}</a>{% endif %}
<td><a href="{% url synnefo.admin.views.images_info image.id %}">{{ image.id }}</a></td>
<td><a href="{% url synnefo.admin.views.images_info image.id %}">{{ image.name }}</a></td>
<td>{% if image.owner %}<a href="{% url synnefo.admin.views.users_info image.owner.id %}">{{ image.owner.name }}</a>{% endif %}
</td>
<td>{{ image.state }}</td>
<td>{{ image.backend_id }}</td>
......@@ -31,6 +31,6 @@
</tbody>
</table>
<a class="btn success" href="{% url admin.views.images_register %}">Register a new Image</a>
<a class="btn success" href="{% url synnefo.admin.views.images_register %}">Register a new Image</a>
<br /><br />
{% endblock body %}
......@@ -2,7 +2,7 @@
{% block body %}
<form action="{% url admin.views.images_register %}" method="post">
<form action="{% url synnefo.admin.views.images_register %}" method="post">
<div class="clearfix">
<label for="image-name">Name</label>
<div class="input">
......
......@@ -19,12 +19,12 @@
<tr>
<td>{{ invitation.id }}</td>
<td>
<a href="{% url admin.views.users_info invitation.source.id %}">{{ invitation.source }}</a>
<a href="{% url synnefo.admin.views.users_info invitation.source.id %}">{{ invitation.source.name }}</a>
<br />
{{ invitation.source.uniq }}
</td>
<td>
<a href="{% url admin.views.users_info invitation.target.id %}">{{ invitation.target }}</a>
<a href="{% url synnefo.admin.views.users_info invitation.target.id %}">{{ invitation.target.name }}</a>
<br />
{{ invitation.target.uniq }}
</td>
......@@ -32,7 +32,7 @@
<td>{{ invitation.level }}</td>
<td>{{ invitation.created }}</td>
<td>{{ invitation.updated }}</td>
<td><a class="btn info" href="{% url admin.views.invitations_resend invitation.id %}">Resend</a></td>
<td><a class="btn info" href="{% url synnefo.admin.views.invitations_resend invitation.id %}">Resend</a></td>
</tr>
{% endfor %}
</tbody>
......
......@@ -15,16 +15,16 @@
</tr>
</thead>
<tbody>
{% for vm in vms %}
{% for server in servers %}
<tr>
<td>{{ vm.id }}</td>
<td>{{ vm.name }}</td>
<td><a href="{% url admin.views.users_info vm.owner.id %}">{{ vm.owner.name }}</a></td>
<td>{{ vm.operstate }}</td>
<td><a href="{% url admin.views.flavors_info vm.flavor.id %}">{{ vm.flavor.name }}</a></td>
<td><a href="{% url admin.views.images_info vm.sourceimage.id %}">{{ vm.sourceimage.name }}</a></td>
<td>{{ vm.deleted }}</td>
<td>{{ vm.updated }}</td>
<td>{{ server.id }}</td>
<td>{{ server.name }}</td>
<td><a href="{% url synnefo.admin.views.users_info server.owner.id %}">{{ server.owner.name }}</a></td>
<td>{{ server.operstate }}</td>
<td><a href="{% url synnefo.admin.views.flavors_info server.flavor.id %}">{{ server.flavor.name }}</a></td>
<td><a href="{% url synnefo.admin.views.images_info server.sourceimage.id %}">{{ server.sourceimage.name }}</a></td>
<td>{{ server.deleted }}</td>
<td>{{ server.updated }}</td>
</tr>
{% endfor %}
</tbody>
......
......@@ -2,7 +2,7 @@
{% block body %}
<form action="{% url admin.views.users_modify user.id %}" method="post">
<form action="{% url synnefo.admin.views.users_modify user.id %}" method="post">
<div class="clearfix">
<label for="user-id">ID</label>
<div class="input">
......@@ -49,6 +49,17 @@
</div>
</div>
<div class="clearfix">
<label for="user-state">State</label>
<div class="input">
<select class="medium" id="user-state" name="state">
{% for state in states %}
<option{% ifequal state user.state %} selected{% endifequal %}>{{ state }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="clearfix">
<label for="user-invitations">Max Invitations</label>
<div class="input">
......@@ -73,16 +84,6 @@
<div class="actions">
<button type="submit" class="btn primary">Save Changes</button>
<button type="reset" class="btn">Reset</button>
&nbsp;&nbsp;
<a class="btn danger needs-confirm" href="{% url admin.views.users_delete user.id %}">Delete User</a>
</div>
<div class="alert-message block-message error">
<p><strong>WARNING:</strong> Deleting a user is a very destructive operation. Any objects with foreign keys pointing to this user will be deleted as well.</p>
<div class="alert-actions">
<a class="btn danger" href="{% url admin.views.users_delete user.id %}">Delete</a>
<a class="btn alert-close">Cancel</a>
</div>
</div>
</form>
{% endblock body %}
......@@ -2,7 +2,7 @@
{% block body %}
<form action="{% url admin.views.users_invite %}" method="post">
<form action="{% url synnefo.admin.views.users_invite %}" method="post">
<div class="clearfix">
<label for="user-inviter">Inviter ID</label>
<div class="input input-append">
......
......@@ -10,24 +10,26 @@
<th>Uniq</th>
<th>Credit</th>
<th>Type</th>
<th>State</th>
<th>Updated</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td><a href="{% url admin.views.users_info user.id %}">{{ user.id }}</a></td>
<td><a href="{% url admin.views.users_info user.id %}">{{ user.name }}</a></td>
<td><a href="{% url synnefo.admin.views.users_info user.id %}">{{ user.id }}</a></td>
<td><a href="{% url synnefo.admin.views.users_info user.id %}">{{ user.name }}</a></td>
<td>{{ user.realname }}</td>
<td>{{ user.uniq }}</td>
<td>{{ user.credit }}</td>
<td>{{ user.type }}</td>
<td>{{ user.state }}</td>
<td>{{ user.updated }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="btn primary" href="{% url admin.views.users_invite %}">Invite a user</a>
<a class="btn primary" href="{% url synnefo.admin.views.users_invite %}">Invite a user</a>
<br /><br />
{% endblock body %}
from django.conf.urls.defaults import patterns
import os
from synnefo import settings
from django.conf.urls.defaults import patterns
urlpatterns = patterns('synnefo.admin.views',
......@@ -35,4 +35,5 @@ urlpatterns += patterns('synnefo.admin.api',
urlpatterns += patterns('',
(r'^/static/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': settings.PROJECT_PATH + '/admin/static'}))
'document_root': os.path.join(os.path.dirname(__file__), 'static')})
)
\ No newline at end of file
......@@ -26,6 +26,20 @@ def requires_admin(func):
return wrapper
def get_filters(request, session_key, all_filters, default=None):
if default is None:
default = all_filters
filters = request.session.get(session_key, default)
filter = request.GET.get('toggle_filter')
if filter:
if filter in filters:
filters.remove(filter)
elif filter in all_filters:
filters.add(filter)
request.session[session_key] = filters
return filters
@requires_admin
def index(request):
stats = {}
......@@ -53,8 +67,18 @@ def index(request):
@requires_admin
def flavors_list(request):
flavors = models.Flavor.objects.order_by('id')
html = render('flavors_list.html', 'flavors', flavors=flavors)
all_states = set(['DELETED'])
default = set()
filters = get_filters(request, 'flavors_filters', all_states, default)
flavors = models.Flavor.objects.all()
if 'DELETED' not in filters:
flavors = flavors.exclude(deleted=True)
html = render('flavors_list.html', 'flavors',
flavors=flavors,
all_states=sorted(all_states),
filters=filters)
return HttpResponse(html)
......@@ -86,6 +110,7 @@ def flavors_modify(request, flavor_id):
flavor.cpu = int(request.POST.get('cpu'))
flavor.ram = int(request.POST.get('ram'))
flavor.disk = int(request.POST.get('disk'))
flavor.deleted = True if request.POST.get('deleted') else False
flavor.save()
_log.info('User %s modified Flavor %s', request.user.name, flavor.name)
return redirect(flavors_info, flavor.id)
......@@ -101,8 +126,18 @@ def flavors_delete(request, flavor_id):
@requires_admin
def images_list(request):
images = models.Image.objects.order_by('id')
html = render('images_list.html', 'images', images=images)
all_states = set(x[0] for x in models.Image.IMAGE_STATES)
default = all_states - set(['DELETED'])
filters = get_filters(request, 'images_filters', all_states, default)
images = models.Image.objects.all()
for state in all_states - filters:
images = images.exclude(state=state)
html = render('images_list.html', 'images',
images=images.order_by('id'),
all_states=sorted(all_states),
filters=filters)
return HttpResponse(html)
......@@ -174,15 +209,35 @@ def images_modify(request, image_id):
@requires_admin
def servers_list(request):
vms = models.VirtualMachine.objects.order_by('id')
html = render('servers_list.html', 'servers', vms=vms)
all_states = set(x[0] for x in models.VirtualMachine.OPER_STATES)
default = all_states - set(['DESTROYED'])
filters = get_filters(request, 'servers_filters', all_states, default)
servers = models.VirtualMachine.objects.all()
for state in all_states - filters:
servers = servers.exclude(operstate=state)
html = render('servers_list.html', 'servers',
servers=servers.order_by('id'),
all_states=sorted(all_states),
filters=filters)
return HttpResponse(html)
@requires_admin
def users_list(request):
users = models.SynnefoUser.objects.order_by('id')
html = render('users_list.html', 'users', users=users)
all_states = set(x[0] for x in models.SynnefoUser.ACCOUNT_STATE)
default = all_states - set(['DELETED'])
filters = get_filters(request, 'users_filters', all_states, default)
users = models.SynnefoUser.objects.all()
for state in all_states - filters:
users = users.exclude(state=state)
html = render('users_list.html', 'users',
users=users.order_by('id'),
all_states=sorted(all_states),
filters=filters)
return HttpResponse(html)
......@@ -209,7 +264,9 @@ def users_info(request, user_id):
types = [x[0] for x in models.SynnefoUser.ACCOUNT_TYPE]
if not user.type:
types = [''] + types
html = render('users_info.html', 'users', user=user, types=types)
states = [x[0] for x in models.SynnefoUser.ACCOUNT_STATE]
html = render('users_info.html', 'users',
user=user, types=types, states=states)
return HttpResponse(html)
......@@ -221,6 +278,7 @@ def users_modify(request, user_id):
user.uniq = request.POST.get('uniq')
user.credit = int(request.POST.get('credit'))
user.type = request.POST.get('type')
user.state = request.POST.get('state')
invitations = request.POST.get('invitations')
user.max_invitations = int(invitations) if invitations else None
user.save()
......
......@@ -64,10 +64,10 @@ def list_flavors(request, detail=False):
# unauthorized (401),
# badRequest (400),
# overLimit (413)
all_flavors = Flavor.objects.all()
flavors = [flavor_to_dict(flavor, detail) for flavor in all_flavors]
active_flavors = Flavor.objects.exclude(deleted=True)
flavors = [flavor_to_dict(flavor, detail) for flavor in active_flavors]
if request.serialization == 'xml':
data = render_to_string('list_flavors.xml', {
'flavors': flavors,
......
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'NetworkLink.available'
db.alter_column('db_networklink', 'available', self.gf('django.db.models.fields.BooleanField')(blank=True))
# Adding field 'SynnefoUser.state'
db.add_column('db_synnefouser', 'state', self.gf('django.db.models.fields.CharField')(default='ACTIVE', max_length=30), keep_default=False)
# Changing field 'Invitations.accepted'
db.alter_column('db_invitations', 'accepted', self.gf('django.db.models.fields.BooleanField')(blank=True))
# Changing field 'VirtualMachine.deleted'
db.alter_column('db_virtualmachine', 'deleted', self.gf('django.db.models.fields.BooleanField')(blank=True))
# Changing field 'VirtualMachine.suspended'
db.alter_column('db_virtualmachine', 'suspended', self.gf('django.db.models.fields.BooleanField')(blank=True))
# Changing field 'Image.public'
db.alter_column('db_image', 'public', self.gf('django.db.models.fields.BooleanField')(blank=True))
# Changing field 'Network.public'
db.alter_column('db_network', 'public', self.gf('django.db.models.fields.BooleanField')(blank=True))
def backwards(self, orm):
# Changing field 'NetworkLink.available'
db.alter_column('db_networklink', 'available', self.gf('django.db.models.fields.BooleanField')())
# Deleting field 'SynnefoUser.state'
db.delete_column('db_synnefouser', 'state')
# Changing field 'Invitations.accepted'
db.alter_column('db_invitations', 'accepted', self.gf('django.db.models.fields.BooleanField')())
# Changing field 'VirtualMachine.deleted'
db.alter_column('db_virtualmachine', 'deleted', self.gf('django.db.models.fields.BooleanField')())
# Changing field 'VirtualMachine.suspended'
db.alter_column('db_virtualmachine', 'suspended', self.gf('django.db.models.fields.BooleanField')())
# Changing field 'Image.public'
db.alter_column('db_image', 'public', self.gf('django.db.models.fields.BooleanField')())
# Changing field 'Network.public'
db.alter_column('db_network', 'public', self.gf('django.db.models.fields.BooleanField')())
models = {
'db.debit': {
'Meta': {'object_name': 'Debit'},
'description': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"}),
'when': ('django.db.models.fields.DateTimeField', [], {})
},
'db.disk': {
'Meta': {'object_name': 'Disk'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
'size': ('django.db.models.fields.PositiveIntegerField', [], {}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True', 'blank': 'True'})
},
'db.flavor': {
'Meta': {'unique_together': "(('cpu', 'ram', 'disk'),)", 'object_name': 'Flavor'},
'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'db.flavorcost': {
'Meta': {'object_name': 'FlavorCost'},
'cost_active': ('django.db.models.fields.PositiveIntegerField', [], {}),
'cost_inactive': ('django.db.models.fields.PositiveIntegerField', [], {}),
'effective_from': ('django.db.models.fields.DateTimeField', [], {}),
'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'db.image': {
'Meta': {'object_name': 'Image'},
'backend_id': ('django.db.models.fields.CharField', [], {'default': "'debian_base'", 'max_length': '50'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'format': ('django.db.models.fields.CharField', [], {'default': "'dump'", 'max_length': '30'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'sourcevm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'db.imagemetadata': {
'Meta': {'object_name': 'ImageMetadata'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'})
},
'db.invitations': {
'Meta': {'object_name': 'Invitations'},
'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source'", 'to': "orm['db.SynnefoUser']"}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'target'", 'to': "orm['db.SynnefoUser']"}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'db.limit': {