Commit 76948cf4 by Nagy Gergő

Merge branch 'master' of git.cloud.bme.hu:circle/cloud

parents 507267b5 dfb15f98
...@@ -18,6 +18,6 @@ _build ...@@ -18,6 +18,6 @@ _build
# Logs: # Logs:
*.log *.log
.ropeproject .ropeproject
#celery
celerybeat-schedule celerybeat-schedule
.coverage
*,cover
"""
Creates Levels for all installed apps that have levels.
"""
from django.db.models import get_models, signals
from django.db import DEFAULT_DB_ALIAS
from django.core.exceptions import ImproperlyConfigured
from ..models import Level, AclBase
def create_levels(app, created_models, verbosity, db=DEFAULT_DB_ALIAS,
**kwargs):
"""Create and set the weights of the configured Levels.
Based on django.contrib.auth.management.__init__.create_permissions"""
# if not router.allow_migrate(db, auth_app.Permission):
# return
from django.contrib.contenttypes.models import ContentType
app_models = [k for k in get_models(app) if AclBase in k.__bases__]
print "Creating levels for models: %s." % ", ".join([m.__name__ for m in app_models])
# This will hold the levels we're looking for as
# (content_type, (codename, name))
searched_levels = list()
level_weights = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in app_models:
# Force looking up the content types in the current database
# before creating foreign keys to them.
ctype = ContentType.objects.db_manager(db).get_for_model(klass)
ctypes.add(ctype)
weight = 0
try:
for codename, name in klass.ACL_LEVELS:
searched_levels.append((ctype, (codename, name)))
level_weights.append((ctype, codename, weight))
weight += 1
except AttributeError:
raise ImproperlyConfigured(
"Class %s doesn't have ACL_LEVELS attribute." % klass)
# Find all the Levels that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_levels = set(Level.objects.using(db).filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
levels = [
Level(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_levels
if (ctype.pk, codename) not in all_levels
]
Level.objects.using(db).bulk_create(levels)
if verbosity >= 2:
print("Adding levels [%s]." % ", ".join(levels))
print("Searched: [%s]." % ", ".join([unicode(l) for l in searched_levels]))
print("All: [%s]." % ", ".join([unicode(l) for l in all_levels]))
# set weights
for ctype, codename, weight in level_weights:
Level.objects.filter(codename=codename,
content_type=ctype).update(weight=weight)
signals.post_syncdb.connect(
create_levels, dispatch_uid="circle.acl.management.create_levels")
from django.core.management.base import BaseCommand
from .. import create_levels
class Command(BaseCommand):
args = ''
help = 'Regenerates Levels'
def handle(self, *args, **options):
create_levels(None, None, 3)
import logging
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.generic import (
GenericForeignKey, GenericRelation
)
from django.contrib.contenttypes.models import ContentType
from django.db.models import (
ManyToManyField, ForeignKey, CharField, Model, IntegerField
)
logger = logging.getLogger(__name__)
class Level(Model):
"""Definition of a permission level.
Instances are automatically populated based on AclBase."""
name = CharField('name', max_length=50)
content_type = ForeignKey(ContentType)
codename = CharField('codename', max_length=100)
weight = IntegerField('weight', null=True)
def __unicode__(self):
return "<%s/%s>" % (unicode(self.content_type), self.name)
class Meta:
unique_together = (('content_type', 'codename'),
# ('content_type', 'weight'),
# TODO find a way of temp. disabling this constr.
)
class ObjectLevel(Model):
"""Permission level for a specific object."""
level = ForeignKey(Level)
content_type = ForeignKey(ContentType)
object_id = CharField(max_length=255)
content_object = GenericForeignKey()
users = ManyToManyField(User)
groups = ManyToManyField(Group)
def __unicode__(self):
return "<%s: %s>" % (unicode(self.content_object), unicode(self.level))
class Meta:
unique_together = (('content_type', 'object_id', 'level'),)
class AclBase(Model):
"""Define permission levels for Users/Groups per object."""
object_level_set = GenericRelation(ObjectLevel)
@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)
def set_level(self, whom, level):
"""Set level of object for a user or group.
:param whom: user or group the level is set for
:type whom: User or Group
:param level: codename of level to set
:type level: Level or str or unicode
"""
if isinstance(whom, User):
self.set_user_level(whom, level)
elif isinstance(whom, Group):
self.set_group_level(whom, level)
else:
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
:type level: Level or str or unicode
"""
logger.info('%s.set_user_level(%s, %s) called',
*[unicode(p) for p in [self, user, level]])
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)
for i in self.object_level_set.all():
if i.level_id != level.pk:
i.users.remove(user)
else:
i.users.add(user)
i.save()
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 isinstance(level, basestring):
level = self.get_level_object(level)
#self.object_level_set.get_or_create(level=level, content_object=self)
if not self.object_level_set.filter(level_id=level.pk).exists():
self.object_level_set.create(level=level)
for i in self.object_level_set.all():
if i.level_id != level.pk:
i.groups.remove(group)
else:
i.groups.add(group)
i.save()
def has_level(self, user, level, group_also=True):
logger.debug('%s.has_level(%s, %s, %s) called',
*[unicode(p) for p in [self, user, level, group_also]])
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
def get_users_with_level(self):
logger.debug('%s.get_users_with_level() called', unicode(self))
object_levels = (self.object_level_set.select_related(
'users', 'level').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
def get_groups_with_level(self):
logger.debug('%s.get_groups_with_level() called', unicode(self))
object_levels = (self.object_level_set.select_related(
'groups', 'level').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
class Meta:
abstract = True
from django.db.models import TextField
from ..models import AclBase
class TestModel(AclBase):
normal_field = TextField()
ACL_LEVELS = (
('alfa', 'Alfa'),
('bravo', 'Bravo'),
('charlie', 'Charlie'),
)
class Test2Model(AclBase):
normal2_field = TextField()
ACL_LEVELS = (
('one', 'One'),
('two', 'Two'),
('three', 'Three'),
)
from django.test import TestCase
from django.contrib.auth.models import User, Group, AnonymousUser
from ..models import ObjectLevel
from .models import TestModel, Test2Model
class AclUserTest(TestCase):
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.u2 = User.objects.create(username='user2', is_staff=True)
self.us = User.objects.create(username='superuser', is_superuser=True)
self.g1 = Group.objects.create(name='group1')
self.g1.user_set.add(self.u1)
self.g1.user_set.add(self.u2)
self.g1.save()
def test_level_exists(self):
for codename, name in TestModel.ACL_LEVELS:
level = TestModel.get_level_object(codename)
self.assertEqual(level.codename, codename)
for codename, name in Test2Model.ACL_LEVELS:
level = Test2Model.get_level_object(codename)
self.assertEqual(level.codename, codename)
def test_lowest_user_level(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa', False))
self.assertFalse(i.has_level(self.u1, 'bravo', False))
i.set_level(self.u1, 'alfa')
i.set_level(self.g1, 'bravo')
self.assertTrue(i.has_level(self.u1, 'alfa', False))
self.assertFalse(i.has_level(self.u1, 'bravo', False))
def test_anonymous_user_level(self):
i = TestModel.objects.create(normal_field='Hello')
anon = AnonymousUser()
self.assertFalse(i.has_level(anon, 'alfa'))
self.assertFalse(i.has_level(anon, 'bravo'))
def test_middle_user_level(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
i.set_level(self.u1, 'bravo')
self.assertTrue(i.has_level(self.u1, 'alfa'))
self.assertTrue(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
def test_level_set_twice_same(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
i.set_level(self.u1, 'bravo')
i.set_level(self.u1, 'bravo')
self.assertTrue(i.has_level(self.u1, 'alfa'))
self.assertTrue(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
def test_level_set_twice_different(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
i.set_level(self.u1, 'charlie')
i.set_level(self.u1, 'bravo')
self.assertTrue(i.has_level(self.u1, 'alfa'))
self.assertTrue(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
def test_superuser(self):
i = TestModel.objects.create(normal_field='Hello')
for u, v in [(self.u1, False), (self.u2, False), (self.us, True)]:
self.assertEqual(i.has_level(u, 'alfa'), v)
self.assertEqual(i.has_level(u, 'bravo'), v)
self.assertEqual(i.has_level(u, 'charlie'), v)
def test_check_group_membership(self):
groups = self.u1.groups.values_list('id', flat=True)
self.assertIn(self.g1.id, groups)
self.assertTrue(self.g1.user_set.filter(id=self.u2.id).exists())
def test_lowest_group_level(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
i.set_level(self.g1, 'alfa')
self.assertTrue(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
def test_middle_group_level(self):
i = TestModel.objects.create(normal_field='Hello')
self.assertFalse(i.has_level(self.u1, 'alfa'))
self.assertFalse(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
i.set_level(self.g1, 'bravo')
self.assertTrue(i.has_level(self.u1, 'alfa'))
self.assertTrue(i.has_level(self.u1, 'bravo'))
self.assertFalse(i.has_level(self.u1, 'charlie'))
def test_set_level_error_handling(self):
with self.assertRaises(AttributeError):
TestModel.objects.create().set_level('wrong arg', 'level')
def test_get_users_with_level(self):
i1 = TestModel.objects.create(normal_field='Hello')
i2 = Test2Model.objects.create(normal2_field='Hello2')
i1.set_level(self.u1, 'bravo')
i1.set_level(self.u2, 'charlie')
i2.set_level(self.u1, 'one')
i2.set_level(self.us, u'three')
res1 = i1.get_users_with_level()
self.assertEqual([(self.u1, u'bravo'), (self.u2, u'charlie')], res1)
res2 = i2.get_users_with_level()
self.assertEqual([(self.u1, u'one'), (self.us, u'three')], res2)
def test_get_groups_with_level(self):
i1 = TestModel.objects.create(normal_field='Hello')
i2 = Test2Model.objects.create(normal2_field='Hello2')
i1.set_level(self.g1, 'bravo')
i1.set_level(self.u2, 'charlie')
i2.set_level(self.g1, 'one')
i2.set_level(self.us, u'three')
res1 = i1.get_groups_with_level()
self.assertEqual([(self.g1, u'bravo')], res1)
res2 = i2.get_groups_with_level()
self.assertEqual([(self.g1, u'one')], res2)
def test_object_level_unicode(self):
i1 = TestModel.objects.create(normal_field='Hello')
i1.set_level(self.g1, 'bravo')
unicode(ObjectLevel.objects.all()[0])
# Create your views here.
"""Common settings and globals.""" """Common settings and globals."""
from datetime import timedelta
from os import environ from os import environ
from os.path import abspath, basename, dirname, join, normpath from os.path import abspath, basename, dirname, join, normpath
from json import loads from json import loads
# from socket import SOCK_STREAM # from socket import SOCK_STREAM
from sys import path from sys import path
# Normally you should not import ANYTHING from Django directly # Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception. # into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
...@@ -229,12 +230,14 @@ THIRD_PARTY_APPS = ( ...@@ -229,12 +230,14 @@ THIRD_PARTY_APPS = (
# Apps specific for this project go here. # Apps specific for this project go here.
LOCAL_APPS = ( LOCAL_APPS = (
'common',
'vm', 'vm',
'storage', 'storage',
'firewall', 'firewall',
'network', 'network',
'dashboard', 'dashboard',
'manager', 'manager',
'acl',
) )
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps # See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
...@@ -305,8 +308,9 @@ VM_ACCESS_PROTOCOLS = loads(get_env_variable('DJANGO_VM_ACCESS_PROTOCOLS', ...@@ -305,8 +308,9 @@ VM_ACCESS_PROTOCOLS = loads(get_env_variable('DJANGO_VM_ACCESS_PROTOCOLS',
"ssh": ["SSH", 22, "tcp"]}''')) "ssh": ["SSH", 22, "tcp"]}'''))
VM_SCHEDULER = 'manager.scheduler' VM_SCHEDULER = 'manager.scheduler'
from datetime import timedelta BROKER_URL = get_env_variable('AMQP_URI')
# Set up periodic firewall tasks
CELERYBEAT_SCHEDULE = { CELERYBEAT_SCHEDULE = {
'blabla': { 'blabla': {
'task': 'firewall.tasks.local_tasks.periodic_task', 'task': 'firewall.tasks.local_tasks.periodic_task',
......
...@@ -16,3 +16,9 @@ DATABASES = { ...@@ -16,3 +16,9 @@ DATABASES = {
"PORT": "", "PORT": "",
}, },
} }
SOUTH_TESTS_MIGRATE = False
INSTALLED_APPS += (
'acl.tests',
)
...@@ -32,6 +32,12 @@ class ActivityModel(TimeStampedModel): ...@@ -32,6 +32,12 @@ class ActivityModel(TimeStampedModel):
result = TextField(verbose_name=_('result'), blank=True, null=True, result = TextField(verbose_name=_('result'), blank=True, null=True,
help_text=_('Human readable result of activity.')) help_text=_('Human readable result of activity.'))
def __unicode__(self):
if self.parent:
return self.parent.activity_code + "->" + self.activity_code
else:
return self.activity_code
class Meta: class Meta:
abstract = True abstract = True
......
{% load i18n %}<!DOCTYPE html>
<html lang="{{lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>{% block title %}{% block title-page %}{% endblock %} | {% block title-site %}Circle{% endblock %}{% endblock %}</title>
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
<script src="{{ STATIC_URL }}dashboard/js/jquery.knob.js"></script>
<script src="{{ STATIC_URL}}dashboard/bootstrap-slider/bootstrap-slider.js"></script>
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/bootstrap-slider/slider.css"/>
<link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet">
<script src="{{ STATIC_URL }}dashboard/dashboard.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<a class="navbar-brand" href="/dashboard/">{% block header-site %}Circle{% endblock %}</a>
</div>
<div class="container">
{% block content %}
<h1 class="alert alert-error">Please override "content" block.</h1>
{% endblock %}
<hr />
<footer>
<p>&copy; Company 2013</p>
</footer>
</div> <!-- /container -->
</body>
{% block extra_js %}
{% endblock %}
</html>
...@@ -5,16 +5,40 @@ ...@@ -5,16 +5,40 @@
<a href="#" class="btn btn-link">{% trans "Transfer ownership..." %}</a> <a href="#" class="btn btn-link">{% trans "Transfer ownership..." %}</a>
</p> </p>
<h3>{% trans "Permissions"|capfirst %}</h3> <h3>{% trans "Permissions"|capfirst %}</h3>
<form action="{{acl.url}}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields"> <table class="table table-striped table-with-form-fields">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead> <thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead>
<tbody> <tbody>
<tr><td><i class="icon-user"></i></td><td>NEP123 (Gipsz Jakab)</td><td><select class="form-control"><option>owner</option></select></td><td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> {% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>NEP123 (Gipsz Jakab)</td><td><select class="form-control"><option>control</option></select></td><td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> <tr><td><i class="icon-user"></i></td><td>{{i.user}}</td>
<tr><td><i class="icon-group"></i></td><td>Cloud-fejlesztők</td><td><select class="form-control"><option>view</option></select></td><td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> <td><select class="form-control" name="perm-u-{{i.user.id}}">
<tr><td><i class="icon-plus"></i></td><td><input type="text" class="form-control"></td> {% for id, name in acl.levels %}
<td><select class="form-control"><option>owner</option></select></td><td></td></tr> <option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
{% for i in acl.groups %}
<tr><td><i class="icon-group"></i></td><td>{{i.group}}</td>
<td><select class="form-control" name="perm-g-{{i.group.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
<td><select class="form-control" name="perm-new">
{% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option>
{% endfor %}
</select></td><td></td>
</tr>
</tbody> </tbody>
</table> </table>
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button> <button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div> </div>
</form>
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User, Group
class VmDetailTest(TestCase):
fixtures = ['test-vm-fixture.json']
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.u2 = User.objects.create(username='user2', is_staff=True)
self.us = User.objects.create(username='superuser', is_superuser=True)
self.g1 = Group.objects.create(name='group1')
self.g1.user_set.add(self.u1)
self.g1.user_set.add(self.u2)
self.g1.save()
def test_404_vm_page(self):
c = Client()
response = c.get('/dashboard/vm/235555/')
self.assertEqual(response.status_code, 404)
def test_vm_page(self):
c = Client()
response = c.get('/dashboard/vm/1/')
self.assertEqual(response.status_code, 200)
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from .views import IndexView, VmDetailView, VmList, VmCreate, TemplateDetail from vm.models import Instance
from .views import (
IndexView, VmDetailView, VmList, VmCreate, TemplateDetail, AclUpdateView
)
urlpatterns = patterns( urlpatterns = patterns(
'', '',
...@@ -9,6 +12,8 @@ urlpatterns = patterns( ...@@ -9,6 +12,8 @@ urlpatterns = patterns(
name='dashboard.views.template-detail'), name='dashboard.views.template-detail'),
url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(), url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(),
name='dashboard.views.detail'), name='dashboard.views.detail'),
url(r'^vm/(?P<pk>\d+)/acl/$', AclUpdateView.as_view(model=Instance),
name='dashboard.views.vm-acl'),
url(r'^vm/list/$', VmList.as_view(), name='dashboard.views.vm-list'), url(r'^vm/list/$', VmList.as_view(), name='dashboard.views.vm-list'),
url(r'^vm/create/$', VmCreate.as_view(), url(r'^vm/create/$', VmCreate.as_view(),
name='dashboard.views.vm-create'), name='dashboard.views.vm-create'),
......
from os import getenv
import json
import logging
import re
from django.contrib.auth.models import User, Group
from django.contrib.messages import warning
from django.core import signing
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, DetailView, View
from django.views.generic.detail import SingleObjectMixin
from django.http import HttpResponse from django.http import HttpResponse
from django.views.generic import TemplateView, DetailView from django.views.generic import TemplateView, DetailView
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
...@@ -6,13 +20,12 @@ from django.shortcuts import redirect ...@@ -6,13 +20,12 @@ from django.shortcuts import redirect
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from tables import VmListTable from tables import VmListTable
from .tables import VmListTable
from vm.models import Instance, InstanceTemplate, InterfaceTemplate from vm.models import Instance, InstanceTemplate, InterfaceTemplate
from firewall.models import Vlan from firewall.models import Vlan
from storage.models import Disk from storage.models import Disk
from django.core import signing
from os import getenv
import json logger = logging.getLogger(__name__)
class IndexView(TemplateView): class IndexView(TemplateView):
...@@ -40,12 +53,22 @@ class IndexView(TemplateView): ...@@ -40,12 +53,22 @@ class IndexView(TemplateView):
return context return context
def get_acl_data(obj):
levels = obj.ACL_LEVELS
users = obj.get_users_with_level()
users = [{'user': u, 'level': l} for u, l in users]
groups = obj.get_groups_with_level()
groups = [{'group': g, 'level': l} for g, l in groups]
return {'users': users, 'groups': groups, 'levels': levels,
'url': reverse('dashboard.views.vm-acl', args=[obj.pk])}
class VmDetailView(DetailView): class VmDetailView(DetailView):
template_name = "dashboard/vm-detail.html" template_name = "dashboard/vm-detail.html"
queryset = Instance.objects.all() model = Instance
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs) context = super(VmDetailView, self).get_context_data(**kwargs)
instance = context['instance'] instance = context['instance']
if instance.node: if instance.node:
port = instance.vnc_port port = instance.vnc_port
...@@ -56,9 +79,55 @@ class VmDetailView(DetailView): ...@@ -56,9 +79,55 @@ class VmDetailView(DetailView):
context.update({ context.update({
'vnc_url': '%s' % value 'vnc_url': '%s' % value
}) })
context['acl'] = get_acl_data(instance)
return context return context
class AclUpdateView(View, SingleObjectMixin):
def post(self, request, *args, **kwargs):
instance = self.get_object()
if not (instance.has_level(request.user, "owner") or
getattr(instance, 'owner', None) == request.user):
logger.warning('Tried to set permissions of %s by non-owner %s.',
unicode(instance), unicode(request.user))
raise PermissionDenied()
self.set_levels(request, instance)
self.add_levels(request, instance)
return redirect(instance)
def set_levels(self, request, instance):
for key, value in request.POST.items():
m = re.match('perm-([ug])-(\d+)', key)
if m:
type, id = m.groups()
entity = {'u': User, 'g': Group}[type].objects.get(id=id)
instance.set_level(entity, value)
logger.info("Set %s's acl level for %s to %s by %s.",
unicode(entity), unicode(instance),
value, unicode(request.user))
def add_levels(self, request, instance):
name = request.POST['perm-new-name']
value = request.POST['perm-new']
if not name:
return
try:
entity = User.objects.get(username=name)
except User.DoesNotExist:
entity = None
try:
entity = Group.objects.get(name=name)
except Group.DoesNotExist:
warning(request, _('User or group "%s" not found.') % name)
return
instance.set_level(entity, value)
logger.info("Set %s's new acl level for %s to %s by %s.",
unicode(entity), unicode(instance),
value, unicode(request.user))
class TemplateDetail(DetailView): class TemplateDetail(DetailView):
model = InstanceTemplate model = InstanceTemplate
...@@ -133,9 +202,9 @@ class VmCreate(TemplateView): ...@@ -133,9 +202,9 @@ class VmCreate(TemplateView):
resp = {} resp = {}
try: try:
ikwargs = { ikwargs = {
'num_cores': request.POST.get('cpu-count'), 'num_cores': int(request.POST.get('cpu-count')),
'ram_size': request.POST.get('ram-size'), 'ram_size': int(request.POST.get('ram-size')),
'priority': request.POST.get('cpu-priority'), 'priority': int(request.POST.get('cpu-priority')),
'disks': Disk.objects.filter( 'disks': Disk.objects.filter(
pk__in=request.POST.getlist('disks')) pk__in=request.POST.getlist('disks'))
} }
......
...@@ -10,7 +10,7 @@ class Migration(SchemaMigration): ...@@ -10,7 +10,7 @@ class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
# Adding field 'Rule.owner' # Adding field 'Rule.owner'
db.add_column('firewall_rule', 'owner', db.add_column('firewall_rule', 'owner',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['auth.User']), self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']),
keep_default=False) keep_default=False)
......
...@@ -21,7 +21,7 @@ class Migration(SchemaMigration): ...@@ -21,7 +21,7 @@ class Migration(SchemaMigration):
# Adding field 'Host.vlan' # Adding field 'Host.vlan'
db.add_column('firewall_host', 'vlan', db.add_column('firewall_host', 'vlan',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['firewall.Vlan']), self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['firewall.Vlan']),
keep_default=False) keep_default=False)
# Removing M2M table for field vlan on 'Host' # Removing M2M table for field vlan on 'Host'
......
...@@ -15,7 +15,7 @@ class Migration(SchemaMigration): ...@@ -15,7 +15,7 @@ class Migration(SchemaMigration):
# Adding field 'Rule.nat_dport' # Adding field 'Rule.nat_dport'
db.add_column('firewall_rule', 'nat_dport', db.add_column('firewall_rule', 'nat_dport',
self.gf('django.db.models.fields.IntegerField')(default=None), self.gf('django.db.models.fields.IntegerField')(default=1),
keep_default=False) keep_default=False)
......
...@@ -58,7 +58,7 @@ class Migration(SchemaMigration): ...@@ -58,7 +58,7 @@ class Migration(SchemaMigration):
# Adding field 'Rule.foreign_network' # Adding field 'Rule.foreign_network'
db.add_column('firewall_rule', 'foreign_network', db.add_column('firewall_rule', 'foreign_network',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='ForeignRules', to=orm['firewall.VlanGroup']), self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='ForeignRules', to=orm['firewall.VlanGroup']),
keep_default=False) keep_default=False)
# Adding field 'Rule.vlan' # Adding field 'Rule.vlan'
......
...@@ -10,7 +10,7 @@ class Migration(SchemaMigration): ...@@ -10,7 +10,7 @@ class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
# Adding field 'Vlan.domain' # Adding field 'Vlan.domain'
db.add_column('firewall_vlan', 'domain', db.add_column('firewall_vlan', 'domain',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['firewall.Domain']), self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['firewall.Domain']),
keep_default=False) keep_default=False)
......
...@@ -815,7 +815,7 @@ class Blacklist(models.Model): ...@@ -815,7 +815,7 @@ class Blacklist(models.Model):
def send_task(sender, instance, created=False, **kwargs): def send_task(sender, instance, created=False, **kwargs):
reloadtask.apply_async(args=[sender.__name__]) reloadtask.apply_async(queue='localhost.man', args=[sender.__name__])
for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group, Blacklist, for sender in [Host, Rule, Domain, Record, Vlan, Firewall, Group, Blacklist,
......
from celery import Celery from celery import Celery
from datetime import timedelta
from kombu import Queue, Exchange from kombu import Queue, Exchange
from os import getenv from os import getenv
...@@ -13,5 +14,15 @@ celery.conf.update( ...@@ -13,5 +14,15 @@ celery.conf.update(
CELERY_QUEUES=( CELERY_QUEUES=(
Queue(HOSTNAME + '.man', Exchange('manager', type='direct'), Queue(HOSTNAME + '.man', Exchange('manager', type='direct'),
routing_key="manager"), routing_key="manager"),
) Queue(HOSTNAME + '.monitor', Exchange('monitor', type='direct'),
routing_key="monitor"),
),
CELERYBEAT_SCHEDULE={
'firewall.periodic_task': {
'task': 'firewall.tasks.local_tasks.periodic_task',
'schedule': timedelta(seconds=5),
'options': {'queue': 'localhost.man'}
},
}
) )
...@@ -21,7 +21,7 @@ class Migration(SchemaMigration): ...@@ -21,7 +21,7 @@ class Migration(SchemaMigration):
# Adding field 'Disk.datastore' # Adding field 'Disk.datastore'
db.add_column('storage_disk', 'datastore', db.add_column('storage_disk', 'datastore',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['storage.DataStore']), self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['storage.DataStore']),
keep_default=False) keep_default=False)
......
{% load i18n %}
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Page not found" %}{% endblock %} {% block title %}{% trans "Page not found" %}{% endblock %}
......
{% load i18n %}
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %}
{% block title %}HTTP 500{% endblock %} {% block title %}HTTP 500{% endblock %}
......
...@@ -16,11 +16,11 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -16,11 +16,11 @@ from django.utils.translation import ugettext_lazy as _
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from common.models import ActivityModel, activitycontextimpl from .tasks import local_tasks, vm_tasks, net_tasks
from firewall.models import Vlan, Host from firewall.models import Vlan, Host
from storage.models import Disk from storage.models import Disk
from .tasks import local_tasks, vm_tasks, net_tasks from common.models import ActivityModel, activitycontextimpl
from acl.models import AclBase
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
pwgen = User.objects.make_random_password pwgen = User.objects.make_random_password
...@@ -270,7 +270,7 @@ class InterfaceTemplate(Model): ...@@ -270,7 +270,7 @@ class InterfaceTemplate(Model):
verbose_name_plural = _('interface templates') verbose_name_plural = _('interface templates')
class Instance(VirtualMachineDescModel, TimeStampedModel): class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel):
"""Virtual machine instance. """Virtual machine instance.
...@@ -296,6 +296,11 @@ class Instance(VirtualMachineDescModel, TimeStampedModel): ...@@ -296,6 +296,11 @@ class Instance(VirtualMachineDescModel, TimeStampedModel):
('SHUTOFF', _('shutoff')), ('SHUTOFF', _('shutoff')),
('CRASHED', _('crashed')), ('CRASHED', _('crashed')),
('PMSUSPENDED', _('pmsuspended'))] # libvirt domain states ('PMSUSPENDED', _('pmsuspended'))] # libvirt domain states
ACL_LEVELS = (
('user', _('user')), # see all details
('operator', _('operator')), # console, networking, change state
('owner', _('owner')), # superuser, can delete, delegate perms
)
name = CharField(blank=True, max_length=100, verbose_name=_('name'), name = CharField(blank=True, max_length=100, verbose_name=_('name'),
help_text=_("Human readable name of instance.")) help_text=_("Human readable name of instance."))
description = TextField(blank=True, verbose_name=_('description')) description = TextField(blank=True, verbose_name=_('description'))
...@@ -336,7 +341,6 @@ class Instance(VirtualMachineDescModel, TimeStampedModel): ...@@ -336,7 +341,6 @@ class Instance(VirtualMachineDescModel, TimeStampedModel):
class Meta: class Meta:
ordering = ['pk', ] ordering = ['pk', ]
permissions = ()
verbose_name = _('instance') verbose_name = _('instance')
verbose_name_plural = _('instances') verbose_name_plural = _('instances')
...@@ -501,8 +505,8 @@ class Instance(VirtualMachineDescModel, TimeStampedModel): ...@@ -501,8 +505,8 @@ class Instance(VirtualMachineDescModel, TimeStampedModel):
return { return {
'name': self.vm_name, 'name': self.vm_name,
'vcpu': self.num_cores, 'vcpu': self.num_cores,
'memory': self.ram_size * 1024, # convert from MiB to KiB 'memory': int(self.ram_size) * 1024, # convert from MiB to KiB
'memory_max': self.max_ram_size * 1024, # convert from MiB to KiB 'memory_max': int(self.max_ram_size) * 1024, # convert from MiB to KiB
'cpu_share': self.priority, 'cpu_share': self.priority,
'arch': self.arch, 'arch': self.arch,
'boot_menu': self.boot_menu, 'boot_menu': self.boot_menu,
...@@ -756,6 +760,12 @@ class InstanceActivity(ActivityModel): ...@@ -756,6 +760,12 @@ class InstanceActivity(ActivityModel):
help_text=_('Instance this activity works on.'), help_text=_('Instance this activity works on.'),
verbose_name=_('instance')) verbose_name=_('instance'))
def __unicode__(self):
if self.parent:
return self.parent.activity_code + "(" + self.instance.name + ")" + "->" + self.activity_code
else:
return self.activity_code + "(" + self.instance.name + ")"
@classmethod @classmethod
def create(cls, code_suffix, instance, task_uuid=None, user=None): def create(cls, code_suffix, instance, task_uuid=None, user=None):
act = cls(activity_code='vm.Instance.' + code_suffix, act = cls(activity_code='vm.Instance.' + code_suffix,
......
from django.test import TestCase from django.test import TestCase
from .models import Template from ..models import InstanceTemplate
class TemplateTestCase(TestCase): class TemplateTestCase(TestCase):
def test_template_creation(self): def test_template_creation(self):
template = Template(name='My first template', template = InstanceTemplate(name='My first template',
access_method='ssh', ) # TODO add images & net access_method='ssh', )
# TODO add images & net
...@@ -11,6 +11,6 @@ chdir /home/cloud/circle/circle ...@@ -11,6 +11,6 @@ chdir /home/cloud/circle/circle
script script
. /home/cloud/.virtualenvs/circle/local/bin/postactivate . /home/cloud/.virtualenvs/circle/local/bin/postactivate
exec /home/cloud/.virtualenvs/circle/bin/python manage.py celery worker -A manager.mancelery --loglevel=info --logfile=/tmp/mancelery.log exec /home/cloud/.virtualenvs/circle/bin/python manage.py celery --app=manager.mancelery worker --autoreload --loglevel=info --hostname=mancelery -B -c 1 --logfile /tmp/mancelery.log
end script end script
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