Commit 96fe17ce by Kálmán Viktor

Merge branch 'master' into feature-new-tour

Conflicts:
	circle/dashboard/static/dashboard/dashboard.css
	circle/dashboard/urls.py
parents 2fee48c3 efdebdff
...@@ -465,3 +465,6 @@ SESSION_COOKIE_NAME = "csessid%x" % (((getnode() // 139) ^ ...@@ -465,3 +465,6 @@ SESSION_COOKIE_NAME = "csessid%x" % (((getnode() // 139) ^
(getnode() % 983)) & 0xffff) (getnode() % 983)) & 0xffff)
MAX_NODE_RAM = get_env_variable("MAX_NODE_RAM", 1024) MAX_NODE_RAM = get_env_variable("MAX_NODE_RAM", 1024)
# Url to download the client: (e.g. http://circlecloud.org/client/download/)
CLIENT_DOWNLOAD_URL = get_env_variable('CLIENT_DOWNLOAD_URL', 'http://circlecloud.org/client/download/')
...@@ -488,7 +488,7 @@ class HumanReadableException(HumanReadableObject, Exception): ...@@ -488,7 +488,7 @@ class HumanReadableException(HumanReadableObject, Exception):
"Level should be the name of an attribute of django." "Level should be the name of an attribute of django."
"contrib.messages (and it should be callable with " "contrib.messages (and it should be callable with "
"(request, message)). Like 'error', 'warning'.") "(request, message)). Like 'error', 'warning'.")
else: elif not hasattr(self, "level"):
self.level = "error" self.level = "error"
def send_message(self, request, level=None): def send_message(self, request, level=None):
......
...@@ -1383,7 +1383,6 @@ ...@@ -1383,7 +1383,6 @@
"time_of_suspend": null, "time_of_suspend": null,
"ram_size": 200, "ram_size": 200,
"priority": 10, "priority": 10,
"active_since": null,
"template": null, "template": null,
"access_method": "nx", "access_method": "nx",
"lease": 1, "lease": 1,
...@@ -1413,7 +1412,6 @@ ...@@ -1413,7 +1412,6 @@
"time_of_suspend": null, "time_of_suspend": null,
"ram_size": 200, "ram_size": 200,
"priority": 10, "priority": 10,
"active_since": null,
"template": null, "template": null,
"access_method": "nx", "access_method": "nx",
"lease": 1, "lease": 1,
......
...@@ -143,6 +143,8 @@ class VmCustomizeForm(forms.Form): ...@@ -143,6 +143,8 @@ class VmCustomizeForm(forms.Form):
self.template = kwargs.pop("template", None) self.template = kwargs.pop("template", None)
super(VmCustomizeForm, self).__init__(*args, **kwargs) super(VmCustomizeForm, self).__init__(*args, **kwargs)
if self.user.has_perm("vm_set_resouces"):
self.allowed_fields = tuple(self.fields.keys())
# set displayed disk and network list # set displayed disk and network list
self.fields['disks'].queryset = self.template.disks.all() self.fields['disks'].queryset = self.template.disks.all()
self.fields['networks'].queryset = Vlan.get_objects_with_level( self.fields['networks'].queryset = Vlan.get_objects_with_level(
...@@ -158,10 +160,29 @@ class VmCustomizeForm(forms.Form): ...@@ -158,10 +160,29 @@ class VmCustomizeForm(forms.Form):
self.initial['cpu_count'] = self.template.num_cores self.initial['cpu_count'] = self.template.num_cores
self.initial['ram_size'] = self.template.ram_size self.initial['ram_size'] = self.template.ram_size
else:
self.allowed_fields = ("name", "template", "customized", )
# initial name and template pk # initial name and template pk
self.initial['name'] = self.template.name self.initial['name'] = self.template.name
self.initial['template'] = self.template.pk self.initial['template'] = self.template.pk
self.initial['customized'] = self.template.pk self.initial['customized'] = True
def _clean_fields(self):
for name, field in self.fields.items():
if name in self.allowed_fields:
value = field.widget.value_from_datadict(
self.data, self.files, self.add_prefix(name))
try:
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self._errors[name] = self.error_class(e.messages)
if name in self.cleaned_data:
del self.cleaned_data[name]
class GroupCreateForm(forms.ModelForm): class GroupCreateForm(forms.ModelForm):
......
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
"Re-save blank Profile.org_id to be saved as NULL"
for i in orm.Profile.objects.filter(org_id=""):
i.save()
# fix default passwords set in migration 0010
f = [f for f in orm.Profile._meta.fields if f.name == 'smb_password'][0]
for i in orm.Profile.objects.filter(smb_password="asdasd"):
i.smb_password = f.get_default()
i.save()
def backwards(self, orm):
"Write your backwards methods here."
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'dashboard.connectcommand': {
'Meta': {'object_name': 'ConnectCommand'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': "'128'"}),
'template': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'command_set'", 'to': u"orm['auth.User']"})
},
u'dashboard.favourite': {
'Meta': {'object_name': 'Favourite'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Instance']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
},
u'dashboard.futuremember': {
'Meta': {'unique_together': "(('org_id', 'group'),)", 'object_name': 'FutureMember'},
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'org_id': ('django.db.models.fields.CharField', [], {'max_length': '64'})
},
u'dashboard.groupprofile': {
'Meta': {'object_name': 'GroupProfile'},
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.Group']", 'unique': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'org_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
u'dashboard.notification': {
'Meta': {'ordering': "['-created']", 'object_name': 'Notification'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message_data': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'status': ('model_utils.fields.StatusField', [], {'default': "'new'", 'max_length': '100', u'no_check_for_status': 'True'}),
'subject_data': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
'to': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'valid_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
},
u'dashboard.profile': {
'Meta': {'object_name': 'Profile'},
'disk_quota': ('sizefield.models.FileSizeField', [], {'default': '2147483648'}),
'email_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance_limit': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
'org_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'preferred_language': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '32'}),
'smb_password': ('django.db.models.fields.CharField', [], {'default': "u'5DP9uNZdKs'", 'max_length': '20'}),
'use_gravatar': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host_ipv6_prefixlen': ('django.db.models.fields.IntegerField', [], {'default': '112'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'storage.datastore': {
'Meta': {'ordering': "[u'name']", 'object_name': 'DataStore'},
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
u'storage.disk': {
'Meta': {'ordering': "[u'name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'derivatives'", 'null': 'True', 'to': u"orm['storage.Disk']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DataStore']"}),
'destroyed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'dev_num': ('django.db.models.fields.CharField', [], {'default': "u'a'", 'max_length': '1'}),
'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'size': ('sizefield.models.FileSizeField', [], {'default': 'None', 'null': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
},
u'vm.instance': {
'Meta': {'ordering': "(u'pk',)", 'object_name': 'Instance'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'active_since': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'destroyed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'instance_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_base': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'instance_set'", 'null': 'True', 'to': u"orm['vm.Node']"}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'pw': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'req_traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'}),
'status': ('model_utils.fields.StatusField', [], {'default': "u'NOSTATE'", 'max_length': '100', u'no_check_for_status': 'True'}),
'status_changed': ('model_utils.fields.MonitorField', [], {'default': 'datetime.datetime.now', u'monitor': "u'status'"}),
'system': ('django.db.models.fields.TextField', [], {}),
'template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'instance_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['vm.InstanceTemplate']"}),
'time_of_delete': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'time_of_suspend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'vnc_port': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
u'vm.instancetemplate': {
'Meta': {'ordering': "(u'name',)", 'object_name': 'InstanceTemplate'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'template_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.InstanceTemplate']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'req_traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'}),
'system': ('django.db.models.fields.TextField', [], {})
},
u'vm.lease': {
'Meta': {'ordering': "[u'name']", 'object_name': 'Lease'},
'delete_interval_seconds': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'suspend_interval_seconds': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
u'vm.node': {
'Meta': {'ordering': "(u'-enabled', u'normalized_name')", 'object_name': 'Node'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'normalized_name': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '100', 'monitor': "u'name'", 'blank': 'True'}),
'overcommit': ('django.db.models.fields.FloatField', [], {'default': '1.0'}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'})
},
u'vm.trait': {
'Meta': {'object_name': 'Trait'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}
complete_apps = ['dashboard']
symmetrical = True
...@@ -48,7 +48,7 @@ from common.models import HumanReadableObject, create_readable, Encoder ...@@ -48,7 +48,7 @@ from common.models import HumanReadableObject, create_readable, Encoder
from vm.tasks.agent_tasks import add_keys, del_keys from vm.tasks.agent_tasks import add_keys, del_keys
from vm.models.instance import ACCESS_METHODS from vm.models.instance import ACCESS_METHODS
from .store_api import Store, NoStoreException, NotOkException from .store_api import Store, NoStoreException, NotOkException, Timeout
from .validators import connect_command_template_validator from .validators import connect_command_template_validator
logger = getLogger(__name__) logger = getLogger(__name__)
...@@ -201,6 +201,11 @@ class Profile(Model): ...@@ -201,6 +201,11 @@ class Profile(Model):
def __unicode__(self): def __unicode__(self):
return self.get_display_name() return self.get_display_name()
def save(self, *args, **kwargs):
if self.org_id == "":
self.org_id = None
super(Profile, self).save(*args, **kwargs)
class Meta: class Meta:
permissions = ( permissions = (
('use_autocomplete', _('Can use autocomplete.')), ('use_autocomplete', _('Can use autocomplete.')),
...@@ -341,7 +346,7 @@ def update_store_profile(sender, **kwargs): ...@@ -341,7 +346,7 @@ def update_store_profile(sender, **kwargs):
profile.disk_quota) profile.disk_quota)
except NoStoreException: except NoStoreException:
logger.debug("Store is not available.") logger.debug("Store is not available.")
except NotOkException: except (NotOkException, Timeout):
logger.critical("Store is not accepting connections.") logger.critical("Store is not accepting connections.")
......
...@@ -964,3 +964,7 @@ textarea[name="list-new-namelist"] { ...@@ -964,3 +964,7 @@ textarea[name="list-new-namelist"] {
#vm-details-start-template-tour { #vm-details-start-template-tour {
margin-right: 5px; margin-right: 5px;
} }
#vm-activity-state {
margin-bottom: 15px;
}
...@@ -244,7 +244,7 @@ $(function () { ...@@ -244,7 +244,7 @@ $(function () {
var search_result = [] var search_result = []
var html = ''; var html = '';
for(var i in my_vms) { for(var i in my_vms) {
if(my_vms[i].name.indexOf(input) != -1) { if(my_vms[i].name.indexOf(input) != -1 || my_vms[i].host.indexOf(input) != -1) {
search_result.push(my_vms[i]); search_result.push(my_vms[i]);
} }
} }
...@@ -383,6 +383,19 @@ $(function () { ...@@ -383,6 +383,19 @@ $(function () {
$('.notification-messages').load("/dashboard/notifications/"); $('.notification-messages').load("/dashboard/notifications/");
$('#notification-button a span[class*="badge-pulse"]').remove(); $('#notification-button a span[class*="badge-pulse"]').remove();
}); });
/* on the client confirmation button fire the clientInstalledAction */
$(document).on("click", "#client-check-button", function(event) {
var connectUri = $('#connect-uri').val();
clientInstalledAction(connectUri);
return false;
});
$("#dashboard-vm-details-connect-button").click(function(event) {
var connectUri = $(this).attr("href");
clientInstalledAction(connectUri);
return false;
});
}); });
function generateVmHTML(pk, name, host, icon, _status, fav, is_last) { function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
...@@ -589,6 +602,12 @@ function addModalConfirmation(func, data) { ...@@ -589,6 +602,12 @@ function addModalConfirmation(func, data) {
}); });
} }
function clientInstalledAction(location) {
setCookie('downloaded_client', true, 365 * 24 * 60 * 60, "/");
window.location.href = location;
$('#confirmation-modal').modal("hide");
}
// for AJAX calls // for AJAX calls
/** /**
* Getter for user cookies * Getter for user cookies
...@@ -611,6 +630,14 @@ function getCookie(name) { ...@@ -611,6 +630,14 @@ function getCookie(name) {
return cookieValue; return cookieValue;
} }
function setCookie(name, value, seconds, path) {
if (seconds!=null) {
var today = new Date();
var expire = new Date();
expire.setTime(today.getTime() + seconds);
}
document.cookie = name+"="+escape(value)+"; expires="+expire.toUTCString()+"; path="+path;
}
/* no js compatibility */ /* no js compatibility */
function noJS() { function noJS() {
......
...@@ -222,6 +222,8 @@ function vmCustomizeLoaded() { ...@@ -222,6 +222,8 @@ function vmCustomizeLoaded() {
$(this).find("i").prop("class", "fa fa-spinner fa-spin"); $(this).find("i").prop("class", "fa fa-spinner fa-spin");
if($("#create-modal")) return true;
$.ajax({ $.ajax({
url: '/dashboard/vm/create/', url: '/dashboard/vm/create/',
headers: {"X-CSRFToken": getCookie('csrftoken')}, headers: {"X-CSRFToken": getCookie('csrftoken')},
......
...@@ -396,8 +396,14 @@ function checkNewActivity(runs) { ...@@ -396,8 +396,14 @@ function checkNewActivity(runs) {
} }
$("#vm-details-state span").html(data['human_readable_status'].toUpperCase()); $("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
if(data['status'] == "RUNNING") { if(data['status'] == "RUNNING") {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").removeClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled");
} else { } else {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").addClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled"); $("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled");
} }
......
...@@ -7,6 +7,7 @@ from datetime import datetime ...@@ -7,6 +7,7 @@ from datetime import datetime
from django.http import Http404 from django.http import Http404
from django.conf import settings from django.conf import settings
from requests import get, post, codes from requests import get, post, codes
from requests.exceptions import Timeout # noqa
from sizefield.utils import filesizeformat from sizefield.utils import filesizeformat
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -222,6 +222,7 @@ class LeaseListTable(Table): ...@@ -222,6 +222,7 @@ class LeaseListTable(Table):
fields = ('name', 'suspend_interval_seconds', fields = ('name', 'suspend_interval_seconds',
'delete_interval_seconds', ) 'delete_interval_seconds', )
prefix = "lease-" prefix = "lease-"
empty_text = _("No available leases.")
class UserKeyListTable(Table): class UserKeyListTable(Table):
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css"> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet"> <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ STATIC_URL }}/template.css"> <link rel="stylesheet" href="{{ STATIC_URL }}/template.css">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements --> <!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
......
{% load i18n %}
<p>
{% blocktrans %}
To effortlessly connect to all kind of virtual machines you have to install the <strong>CIRCLE Client</strong>.
{% endblocktrans %}
</p>
<p class="text-info">
{% blocktrans %}
To install the <strong>CIRCLE Client</strong> click on the <strong>Download the Client</strong> button.
The button takes you to the installation detail page, where you can choose your operating system and start
the download or read more detailed information about the <strong>Client</strong>. The program can be installed on Windows XP (and above)
or Debian based Linux operating systems. To successfully install the client you have to have admin (root or elevated) rights.
After the installation complete clicking on the <strong>I have the Client installed</strong> button will launch the appropriate tool
designed for that connection with necessarily predefined configurations. This option will also save your answer and this prompt about
installation will not pop up again.
{% endblocktrans %}
</p>
<br>
<div class="pull-right">
<form method="POST" id="dashboard-client-check" action="">
{% csrf_token %}
<a class="btn btn-default" href="{% url "dashboard.views.detail" pk=instance.pk %}" data-dismiss="modal">{% trans "Cancel" %}</a>
<a class="btn btn-info" href="{{ client_download_url }}" traget="_blank">{% trans "Download the Client" %}</a>
<button data-dismiss="modal" id="client-check-button" type="submit" class="btn btn-success" title="{% trans "I downloaded and installed the client and I want to connect using it. This choice will be saved to your compuer" %}">
<i class="fa fa-external-link"></i> {% trans "I have the Client installed" %}
</button>
<input id="connect-uri" name="connect-uri" type="hidden" value="{% if instance.get_connect_uri %}{{ instance.get_connect_uri}}{% endif %}" />
<input name="vm" type="hidden" value="{% if instance.get_connect_uri %}{{ instance.pk}}{% endif %}" />
</form>
</div>
\ No newline at end of file
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
{% load sizefieldtags %} {% load sizefieldtags %}
{% include "display-form-errors.html" with form=vm_create_form %} {% include "display-form-errors.html" with form=vm_create_form %}
<form method="POST"> <form method="POST" action="{% url "dashboard.views.vm-create" %}">
{% csrf_token %} {% csrf_token %}
{{ vm_create_form.template }} {{ vm_create_form.template }}
{{ vm_create_form.customized }}
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
...@@ -23,7 +24,6 @@ ...@@ -23,7 +24,6 @@
</div> </div>
{% if perms.vm.set_resources %} {% if perms.vm.set_resources %}
{{ vm_create_form.customized }}
<div class="row"> <div class="row">
<div class="col-sm-10"> <div class="col-sm-10">
<div class="form-group"> <div class="form-group">
......
...@@ -10,8 +10,9 @@ ...@@ -10,8 +10,9 @@
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{% blocktrans with owner=instance.owner fqdn=instance.primary_host %} {% blocktrans with owner=instance.owner name=instance.name id=instance.id%}
{{ owner }} offered to take the ownership of virtual machine {{fqdn}}. <strong>{{ owner }}</strong> offered to take the ownership of
virtual machine <strong>{{name}} ({{id}})</strong>.
Do you accept the responsility of being the host's owner? Do you accept the responsility of being the host's owner?
{% endblocktrans %} {% endblocktrans %}
<div class="pull-right"> <div class="pull-right">
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i> <i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }} {{ i.name }}
</span> </span>
<small class="text-muted"> {{ i.primary_host.hostname }}</small> <small class="text-muted"> {{ i.short_hostname }}</small>
<div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}"> <div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %} {% if i.fav %}
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i> <i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
</h1> </h1>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-5" id="vm-info-pane"> <div class="col-md-6" id="vm-info-pane">
<div class="big"> <div class="big" id="vm-activity-state">
<span id="vm-activity-state" class="label label-{% if object.get_status_id == 'wait' %}info{% else %}{% if object.succeeded %}success{% else %}error{% endif %}{% endif %}"> <span class="label label-{% if object.get_status_id == 'wait' %}info{% else %}{% if object.succeeded %}success{% else %}danger{% endif %}{% endif %}">
<span>{{ object.get_status_id|upper }}</span> <span>{{ object.get_status_id|upper }}</span>
</span> </span>
</div> </div>
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
{% include "dashboard/vm-detail/_activity-timeline.html" with active=object %} {% include "dashboard/vm-detail/_activity-timeline.html" with active=object %}
</div> </div>
<div class="col-md-7"> <div class="col-md-6">
<div class="panel panel-default"> <div class="panel panel-default">
<!--<div class="panel-heading"><h2 class="panel-title">{% trans "Activity" %}</h2></div> --> <!--<div class="panel-heading"><h2 class="panel-title">{% trans "Activity" %}</h2></div> -->
<div class="panel-body"> <div class="panel-body">
......
...@@ -10,6 +10,12 @@ ...@@ -10,6 +10,12 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{% if request.user.is_superuser %}
<a href="{{ login_token }}"
class="pull-right btn btn-danger btn-xs"
title="{% trans "Log in as this user. Recommended to open in an incognito window." %}">
{% trans "Login as this user" %}</a>
{% endif %}
<a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.index" %}">{% trans "Back" %}</a> <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.index" %}">{% trans "Back" %}</a>
<h3 class="no-margin"> <h3 class="no-margin">
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
......
...@@ -39,12 +39,14 @@ ...@@ -39,12 +39,14 @@
</div> </div>
</div> </div>
{% if show_lease_table %}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{% if perms.vm.create_leases %} {% if perms.vm.create_leases %}
<a href="{% url "dashboard.views.lease-create" %}" class="pull-right btn btn-success btn-xs" style="margin-right: 10px;"> <a href="{% url "dashboard.views.lease-create" %}"
class="pull-right btn btn-success btn-xs" style="margin-right: 10px;">
<i class="fa fa-plus"></i> {% trans "new lease" %} <i class="fa fa-plus"></i> {% trans "new lease" %}
</a> </a>
{% endif %} {% endif %}
...@@ -71,6 +73,7 @@ ...@@ -71,6 +73,7 @@
</div> </div>
{% endcomment %} {% endcomment %}
</div> </div>
{% endif %}
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}
......
...@@ -144,8 +144,21 @@ ...@@ -144,8 +144,21 @@
<i class="fa fa-copy" title="{% trans "Select all" %}"></i> <i class="fa fa-copy" title="{% trans "Select all" %}"></i>
</span> </span>
</div> </div>
{% endfor %} {% endfor %}
{% if instance.get_connect_uri %}
<div id="dashboard-vm-details-connect" class="operation-wrapper">
{% if client_download %}
<a id="dashboard-vm-details-connect-button" class="btn btn-xs btn-default operation " href="{{ instance.get_connect_uri}}" title="{% trans "Connect via the CIRCLE Client" %}">
<i class="fa fa-external-link"></i> {% trans "Connect" %}
</a>
<a href="{% url "dashboard.views.client-check" %}?vm={{ instance.pk }}">{% trans "Download client" %}</a>
{% else %}
<a id="dashboard-vm-details-connect-download-button" class="btn btn-xs btn-default operation " href="{% url "dashboard.views.client-check" %}?vm={{ instance.pk }}" title="{% trans "Download the CIRCLE Client" %}">
<i class="fa fa-external-link"></i> {% trans "Connect (download client)" %}
</a>
{% endif %}
</div>
{% endif %}
</div> </div>
<div class="col-md-8" id="vm-detail-pane"> <div class="col-md-8" id="vm-detail-pane">
<div class="panel panel-default" id="vm-detail-panel"> <div class="panel panel-default" id="vm-detail-panel">
......
{% spaceless %}
{% load django_tables2 %}
{% load i18n %}
{% if table.page %}
<div class="table-container">
{% endif %}
{% block table %}
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
{% nospaceless %}
{% block table.thead %}
<thead>
<tr>
{% for column in table.columns %}
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}><a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a></th>
{% else %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
{% endblock table.thead %}
{% block table.tbody %}
<tbody>
{% for row in table.page.object_list|default:table.rows %} {# support pagination #}
{% block table.tbody.row %}
<tr class="{{ forloop.counter|divisibleby:2|yesno:"even,odd" }}"> {# avoid cycle for Django 1.2-1.6 compatibility #}
{% for column, cell in row.items %}
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
{% endfor %}
</tr>
{% endblock table.tbody.row %}
{% empty %}
{% if table.empty_text %}
{% block table.tbody.empty_text %}
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
{% endblock table.tbody.empty_text %}
{% endif %}
{% endfor %}
</tbody>
{% endblock table.tbody %}
{% block table.tfoot %}
<tfoot></tfoot>
{% endblock table.tfoot %}
{% endnospaceless %}
</table>
{% endblock table %}
{% if table.page %}
</div>
{% endif %}
{% endspaceless %}
...@@ -46,6 +46,7 @@ from .views import ( ...@@ -46,6 +46,7 @@ from .views import (
GroupPermissionsView, GroupPermissionsView,
LeaseAclUpdateView, LeaseAclUpdateView,
toggle_template_tutorial, toggle_template_tutorial,
ClientCheck, TokenLogin,
) )
autocomplete_light.autodiscover() autocomplete_light.autodiscover()
...@@ -207,4 +208,8 @@ urlpatterns = patterns( ...@@ -207,4 +208,8 @@ urlpatterns = patterns(
name="dashboard.views.store-new-directory"), name="dashboard.views.store-new-directory"),
url(r"^store/refresh_toplist$", store_refresh_toplist, url(r"^store/refresh_toplist$", store_refresh_toplist,
name="dashboard.views.store-refresh-toplist"), name="dashboard.views.store-refresh-toplist"),
url(r"^client/check$", ClientCheck.as_view(),
name="dashboard.views.client-check"),
url(r'^token-login/(?P<token>.*)/$', TokenLogin.as_view(),
name="dashboard.views.token-login"),
) )
...@@ -29,8 +29,9 @@ import requests ...@@ -29,8 +29,9 @@ import requests
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.contrib.auth.views import login, redirect_to_login from django.contrib.auth.views import login as login_view, redirect_to_login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth import login
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import ( from django.core.exceptions import (
PermissionDenied, SuspiciousOperation, PermissionDenied, SuspiciousOperation,
...@@ -225,7 +226,7 @@ class FilterMixin(object): ...@@ -225,7 +226,7 @@ class FilterMixin(object):
cleaned_data = self.search_form.cleaned_data cleaned_data = self.search_form.cleaned_data
stype = cleaned_data.get('stype', "all") stype = cleaned_data.get('stype', "all")
superuser = stype == "all" superuser = stype == "all"
shared = stype == "shared" shared = stype == "shared" or stype == "all"
level = "owner" if stype == "owned" else "user" level = "owner" if stype == "owned" else "user"
queryset = model.get_objects_with_level( queryset = model.get_objects_with_level(
level, self.request.user, level, self.request.user,
...@@ -414,6 +415,9 @@ class VmDetailView(CheckedDetailView): ...@@ -414,6 +415,9 @@ class VmDetailView(CheckedDetailView):
context['can_change_resources'] = self.request.user.has_perm( context['can_change_resources'] = self.request.user.has_perm(
"vm.change_resources") "vm.change_resources")
# client info
context['client_download'] = self.request.COOKIES.get(
'downloaded_client')
# can link template # can link template
context['can_link_template'] = ( context['can_link_template'] = (
instance.template and instance.template.has_level(user, "operator") instance.template and instance.template.has_level(user, "operator")
...@@ -1534,6 +1538,37 @@ class GroupAclUpdateView(AclUpdateView): ...@@ -1534,6 +1538,37 @@ class GroupAclUpdateView(AclUpdateView):
return super(GroupAclUpdateView, self).get_object().profile return super(GroupAclUpdateView, self).get_object().profile
class ClientCheck(LoginRequiredMixin, TemplateView):
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/_modal.html']
else:
return ['dashboard/nojs-wrapper.html']
def get_context_data(self, *args, **kwargs):
context = super(ClientCheck, self).get_context_data(*args, **kwargs)
context.update({
'box_title': _('About CIRCLE Client'),
'ajax_title': False,
'client_download_url': settings.CLIENT_DOWNLOAD_URL,
'template': "dashboard/_client-check.html",
'instance': get_object_or_404(
Instance, pk=self.request.GET.get('vm')),
})
if not context['instance'].has_level(self.request.user, 'operator'):
raise PermissionDenied()
return context
def post(self, request, *args, **kwargs):
instance = get_object_or_404(Instance, pk=request.POST.get('vm'))
if not instance.has_level(request.user, 'operator'):
raise PermissionDenied()
response = HttpResponseRedirect(instance.get_absolute_url())
response.set_cookie('downloaded_client', 'True', 365 * 24 * 60 * 60)
return response
class TemplateChoose(LoginRequiredMixin, TemplateView): class TemplateChoose(LoginRequiredMixin, TemplateView):
def get_template_names(self): def get_template_names(self):
...@@ -1724,9 +1759,16 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView): ...@@ -1724,9 +1759,16 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super(TemplateList, self).get_context_data(*args, **kwargs) context = super(TemplateList, self).get_context_data(*args, **kwargs)
user = self.request.user
leases_w_operator = Lease.get_objects_with_level("operator", user)
context['lease_table'] = LeaseListTable( context['lease_table'] = LeaseListTable(
Lease.get_objects_with_level("user", self.request.user), leases_w_operator, request=self.request,
request=self.request) template="django_tables2/table_no_page.html",
)
context['show_lease_table'] = (
leases_w_operator.count() > 0 or
user.has_perm("vm.create_leases")
)
context['search_form'] = self.search_form context['search_form'] = self.search_form
...@@ -1851,7 +1893,7 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView): ...@@ -1851,7 +1893,7 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView):
'pk': i.pk, 'pk': i.pk,
'name': i.name, 'name': i.name,
'icon': i.get_status_icon(), 'icon': i.get_status_icon(),
'host': "" if not i.primary_host else i.primary_host.hostname, 'host': i.short_hostname,
'status': i.get_status_display(), 'status': i.get_status_display(),
'fav': i.pk in favs, 'fav': i.pk in favs,
} for i in instances] } for i in instances]
...@@ -2089,23 +2131,29 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -2089,23 +2131,29 @@ class VmCreate(LoginRequiredMixin, TemplateView):
if not request.user.has_perm('vm.create_vm'): if not request.user.has_perm('vm.create_vm'):
raise PermissionDenied() raise PermissionDenied()
form_error = form is not None if form is None:
template = (form.template.pk if form_error template_pk = request.GET.get("template")
else request.GET.get("template")) else:
template_pk = form.template.pk
if template_pk:
template = get_object_or_404(InstanceTemplate, pk=template_pk)
if not template.has_level(request.user, 'user'):
raise PermissionDenied()
if form is None:
form = self.form_class(user=request.user, template=template)
else:
templates = InstanceTemplate.get_objects_with_level( templates = InstanceTemplate.get_objects_with_level(
'user', request.user, disregard_superuser=True) 'user', request.user, disregard_superuser=True)
if form is None and template:
form = self.form_class(user=request.user,
template=templates.get(pk=template))
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
if template: if template_pk:
context.update({ context.update({
'template': 'dashboard/_vm-create-2.html', 'template': 'dashboard/_vm-create-2.html',
'box_title': _('Customize VM'), 'box_title': _('Customize VM'),
'ajax_title': True, 'ajax_title': True,
'vm_create_form': form, 'vm_create_form': form,
'template_o': templates.get(pk=template), 'template_o': template,
}) })
else: else:
context.update({ context.update({
...@@ -2126,52 +2174,48 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -2126,52 +2174,48 @@ class VmCreate(LoginRequiredMixin, TemplateView):
raise PermissionDenied() raise PermissionDenied()
args = {"template": template, "owner": user} args = {"template": template, "owner": user}
if "name" in request.POST:
args["name"] = request.POST.get("name")
instances = [Instance.create_from_template(**args)] instances = [Instance.create_from_template(**args)]
return self.__deploy(request, instances) return self.__deploy(request, instances)
def __create_customized(self, request, *args, **kwargs): def __create_customized(self, request, *args, **kwargs):
user = request.user user = request.user
# no form yet, using POST directly:
template = get_object_or_404(InstanceTemplate,
pk=request.POST.get("template"))
form = self.form_class( form = self.form_class(
request.POST, user=request.user, request.POST, user=request.user, template=template)
template=InstanceTemplate.objects.get(
pk=request.POST.get("template")
)
)
if not form.is_valid(): if not form.is_valid():
return self.get(request, form, *args, **kwargs) return self.get(request, form, *args, **kwargs)
post = form.cleaned_data post = form.cleaned_data
template = InstanceTemplate.objects.get(pk=post['template'])
# permission check
if not template.has_level(user, 'user'): if not template.has_level(user, 'user'):
raise PermissionDenied() raise PermissionDenied()
if request.user.has_perm('vm.set_resources'):
ikwargs = { ikwargs = {
'name': post['name'], 'name': post['name'],
'num_cores': post['cpu_count'], 'template': template,
'ram_size': post['ram_size'], 'owner': user,
'priority': post['cpu_priority'],
'max_ram_size': post['ram_size'],
} }
amount = post.get("amount", 1)
if request.user.has_perm('vm.set_resources'):
networks = [InterfaceTemplate(vlan=l, managed=l.managed) networks = [InterfaceTemplate(vlan=l, managed=l.managed)
for l in post['networks']] for l in post['networks']]
ikwargs.update({ ikwargs.update({
'template': template, 'num_cores': post['cpu_count'],
'owner': user, 'ram_size': post['ram_size'],
'priority': post['cpu_priority'],
'max_ram_size': post['ram_size'],
'networks': networks, 'networks': networks,
'disks': list(template.disks.all()), 'disks': list(template.disks.all()),
}) })
amount = post['amount'] else:
pass
instances = Instance.mass_create_from_template(amount=amount, instances = Instance.mass_create_from_template(amount=amount,
**ikwargs) **ikwargs)
return self.__deploy(request, instances) return self.__deploy(request, instances)
else:
raise PermissionDenied()
def __deploy(self, request, instances, *args, **kwargs): def __deploy(self, request, instances, *args, **kwargs):
for i in instances: for i in instances:
...@@ -2678,6 +2722,7 @@ def vm_activity(request, pk): ...@@ -2678,6 +2722,7 @@ def vm_activity(request, pk):
if not show_all: if not show_all:
activities = activities[:10] activities = activities[:10]
response['connect_uri'] = instance.get_connect_uri()
response['human_readable_status'] = instance.get_status_display() response['human_readable_status'] = instance.get_status_display()
response['status'] = instance.status response['status'] = instance.status
response['icon'] = instance.get_status_icon() response['icon'] = instance.get_status_icon()
...@@ -2954,12 +2999,55 @@ def circle_login(request): ...@@ -2954,12 +2999,55 @@ def circle_login(request):
extra_context = { extra_context = {
'saml2': saml_available, 'saml2': saml_available,
} }
response = login(request, authentication_form=authentication_form, response = login_view(request, authentication_form=authentication_form,
extra_context=extra_context) extra_context=extra_context)
set_language_cookie(request, response) set_language_cookie(request, response)
return response return response
class TokenLogin(View):
token_max_age = 120 # seconds
@classmethod
def get_salt(cls):
return unicode(cls)
@classmethod
def get_token(cls, user, sudoer):
return signing.dumps((sudoer.pk, user.pk),
salt=cls.get_salt(), compress=True)
@classmethod
def get_token_url(cls, user, sudoer):
key = cls.get_token(user, sudoer)
return reverse("dashboard.views.token-login", args=(key, ))
def get(self, request, token, *args, **kwargs):
try:
data = signing.loads(token, salt=self.get_salt(),
max_age=self.token_max_age)
logger.debug('TokenLogin token data: %s', unicode(data))
sudoer, user = data
logger.debug('Extracted TokenLogin data: sudoer: %s, user: %s',
unicode(sudoer), unicode(user))
except (signing.BadSignature, ValueError, TypeError) as e:
logger.warning('Tried invalid TokenLogin token. '
'Token: %s, user: %s. %s',
token, unicode(self.request.user), unicode(e))
raise SuspiciousOperation()
sudoer = User.objects.get(pk=sudoer)
if not sudoer.is_superuser:
raise PermissionDenied()
user = User.objects.get(pk=user)
user.backend = 'django.contrib.auth.backends.ModelBackend'
logger.warning('%s %d logged in as user %s %d',
unicode(sudoer), sudoer.pk, unicode(user), user.pk)
login(request, user)
messages.info(request, _("Logged in as user %s.") % unicode(user))
return redirect("/")
class MyPreferencesView(UpdateView): class MyPreferencesView(UpdateView):
model = Profile model = Profile
...@@ -3296,6 +3384,9 @@ class ProfileView(LoginRequiredMixin, DetailView): ...@@ -3296,6 +3384,9 @@ class ProfileView(LoginRequiredMixin, DetailView):
template__in=it) template__in=it)
context['instances_with_access'] = context[ context['instances_with_access'] = context[
'instances_with_access'].filter(template__in=it) 'instances_with_access'].filter(template__in=it)
if self.request.user.is_superuser:
context['login_token'] = TokenLogin.get_token_url(
user, self.request.user)
return context return context
......
...@@ -6,8 +6,8 @@ msgid "" ...@@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-09-03 12:57+0200\n" "POT-Creation-Date: 2014-09-03 18:49+0200\n"
"PO-Revision-Date: 2014-09-03 12:50+0200\n" "PO-Revision-Date: 2014-09-03 19:00+0200\n"
"Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n" "Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n" "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
"Language: hu\n" "Language: hu\n"
...@@ -476,7 +476,7 @@ msgstr "Műveletek" ...@@ -476,7 +476,7 @@ msgstr "Műveletek"
#: dashboard/tables.py:152 dashboard/templates/dashboard/_vm-create-2.html:38 #: dashboard/tables.py:152 dashboard/templates/dashboard/_vm-create-2.html:38
#: dashboard/templates/dashboard/node-detail.html:78 #: dashboard/templates/dashboard/node-detail.html:78
#: dashboard/templates/dashboard/vm-detail.html:150 #: dashboard/templates/dashboard/vm-detail.html:163
msgid "Resources" msgid "Resources"
msgstr "Erőforrások" msgstr "Erőforrások"
...@@ -531,214 +531,218 @@ msgstr "Érvénytelen sablon." ...@@ -531,214 +531,218 @@ msgstr "Érvénytelen sablon."
msgid "console access" msgid "console access"
msgstr "konzolhozzáférés" msgstr "konzolhozzáférés"
#: dashboard/views.py:443 #: dashboard/views.py:446
msgid "VM successfully renamed." msgid "VM successfully renamed."
msgstr "A virtuális gép átnevezésre került." msgstr "A virtuális gép átnevezésre került."
#: dashboard/views.py:467 #: dashboard/views.py:470
msgid "VM description successfully updated." msgid "VM description successfully updated."
msgstr "A VM leírása megváltoztatásra került." msgstr "A VM leírása megváltoztatásra került."
#: dashboard/views.py:544 #: dashboard/views.py:547
msgid "There is a problem with your input." msgid "There is a problem with your input."
msgstr "A megadott érték nem megfelelő." msgstr "A megadott érték nem megfelelő."
#: dashboard/views.py:546 #: dashboard/views.py:549
msgid "Unknown error." msgid "Unknown error."
msgstr "Ismeretlen hiba." msgstr "Ismeretlen hiba."
#: dashboard/views.py:687 #: dashboard/views.py:690
msgid "Could not start operation." msgid "Could not start operation."
msgstr "A művelet megkezdése meghiúsult." msgstr "A művelet megkezdése meghiúsult."
#: dashboard/views.py:704 #: dashboard/views.py:707
msgid "Operation failed." msgid "Operation failed."
msgstr "A művelet meghiúsult." msgstr "A művelet meghiúsult."
#: dashboard/views.py:709 #: dashboard/views.py:712
msgid "Operation succeeded." msgid "Operation succeeded."
msgstr "A művelet sikeresen végrehajtásra került." msgstr "A művelet sikeresen végrehajtásra került."
#: dashboard/views.py:711 #: dashboard/views.py:714
msgid "Operation is started." msgid "Operation is started."
msgstr "A művelet megkezdődött." msgstr "A művelet megkezdődött."
#: dashboard/views.py:948 #: dashboard/views.py:951
msgid "The token has expired." msgid "The token has expired."
msgstr "A token lejárt." msgstr "A token lejárt."
#: dashboard/views.py:1136 #: dashboard/views.py:1139
#, python-format #, python-format
msgid "Failed to execute %(op)s operation on instance %(instance)s." msgid "Failed to execute %(op)s operation on instance %(instance)s."
msgstr "%(op)s végrehajtása meghiúsult a következőn: %(instance)s." msgstr "%(op)s végrehajtása meghiúsult a következőn: %(instance)s."
#: dashboard/views.py:1152 #: dashboard/views.py:1155
#, python-format #, python-format
msgid "You are not permitted to execute %(op)s on instance %(instance)s." msgid "You are not permitted to execute %(op)s on instance %(instance)s."
msgstr "Nem engedélyezett a(z) %(op)s végrehajtása a(z) %(instance)s gépen." msgstr "Nem engedélyezett a(z) %(op)s végrehajtása a(z) %(instance)s gépen."
#: dashboard/views.py:1230 #: dashboard/views.py:1233
msgid "Node successfully renamed." msgid "Node successfully renamed."
msgstr "A csomópont átnevezésre került." msgstr "A csomópont átnevezésre került."
#: dashboard/views.py:1324 #: dashboard/views.py:1327
#, python-format #, python-format
msgid "User \"%s\" not found." msgid "User \"%s\" not found."
msgstr "Nem található „%s” felhasználó." msgstr "Nem található „%s” felhasználó."
#: dashboard/views.py:1340 #: dashboard/views.py:1343
msgid "Group successfully renamed." msgid "Group successfully renamed."
msgstr "A csoport átnevezésre került." msgstr "A csoport átnevezésre került."
#: dashboard/views.py:1371 #: dashboard/views.py:1374
#, python-format #, python-format
msgid "Acl user/group %(w)s successfully modified." msgid "Acl user/group %(w)s successfully modified."
msgstr "A(z) %(w)s ACL felhasználó/csoport módosításra került." msgstr "A(z) %(w)s ACL felhasználó/csoport módosításra került."
#: dashboard/views.py:1373 #: dashboard/views.py:1376
#, python-format #, python-format
msgid "Acl user/group %(w)s successfully added." msgid "Acl user/group %(w)s successfully added."
msgstr "A(z) %(w)s ACL felhasználó/csoport hozzáadásra került." msgstr "A(z) %(w)s ACL felhasználó/csoport hozzáadásra került."
#: dashboard/views.py:1375 #: dashboard/views.py:1378
#, python-format #, python-format
msgid "Acl user/group %(w)s successfully removed." msgid "Acl user/group %(w)s successfully removed."
msgstr "A(z) %(w)s ACL felhasználó/csoport törlésre került." msgstr "A(z) %(w)s ACL felhasználó/csoport törlésre került."
#: dashboard/views.py:1459 #: dashboard/views.py:1462
msgid "" msgid ""
"The original owner cannot be removed, however you can transfer ownership." "The original owner cannot be removed, however you can transfer ownership."
msgstr "Az eredeti tulajdonos nem törölhető, azonban a tulajdon átruházható." msgstr "Az eredeti tulajdonos nem törölhető, azonban a tulajdon átruházható."
#: dashboard/views.py:1495 #: dashboard/views.py:1498
#, python-format #, python-format
msgid "User \"%s\" has already access to this object." msgid "User \"%s\" has already access to this object."
msgstr "„%s” felhasználó már hozzáfér az objektumhoz." msgstr "„%s” felhasználó már hozzáfér az objektumhoz."
#: dashboard/views.py:1504 #: dashboard/views.py:1507
#, python-format #, python-format
msgid "Group \"%s\" has already access to this object." msgid "Group \"%s\" has already access to this object."
msgstr "„%s” csoport már hozzáfér az objektumhoz." msgstr "„%s” csoport már hozzáfér az objektumhoz."
#: dashboard/views.py:1509 #: dashboard/views.py:1512
#, python-format #, python-format
msgid "User or group \"%s\" not found." msgid "User or group \"%s\" not found."
msgstr "Nem található „%s” felhasználó vagy csoport." msgstr "Nem található „%s” felhasználó vagy csoport."
#: dashboard/views.py:1547 #: dashboard/views.py:1548
msgid "About CIRCLE Client"
msgstr "A CIRCLE kliensről"
#: dashboard/views.py:1581
msgid "Choose template" msgid "Choose template"
msgstr "Válasszon sablont" msgstr "Válasszon sablont"
#: dashboard/views.py:1562 #: dashboard/views.py:1596
msgid "Select an option to proceed." msgid "Select an option to proceed."
msgstr "Válasszon a folytatáshoz." msgstr "Válasszon a folytatáshoz."
#: dashboard/views.py:1593 #: dashboard/views.py:1627
msgid "Create a new base VM" msgid "Create a new base VM"
msgstr "Alap VM létrehozása" msgstr "Alap VM létrehozása"
#: dashboard/views.py:1649 #: dashboard/views.py:1683
msgid "Successfully modified template." msgid "Successfully modified template."
msgstr "A sablon módosításra került." msgstr "A sablon módosításra került."
#: dashboard/views.py:1755 #: dashboard/views.py:1789
msgid "Error during filtering." msgid "Error during filtering."
msgstr "A szűrés sikertelen." msgstr "A szűrés sikertelen."
#: dashboard/views.py:1780 #: dashboard/views.py:1814
msgid "Template successfully deleted." msgid "Template successfully deleted."
msgstr "A sablon törlésre került." msgstr "A sablon törlésre került."
#: dashboard/views.py:1985 #: dashboard/views.py:2019
msgid "Member successfully removed from group." msgid "Member successfully removed from group."
msgstr "A csoporttag eltávolításra került." msgstr "A csoporttag eltávolításra került."
#: dashboard/views.py:2026 #: dashboard/views.py:2060
msgid "Future user successfully removed from group." msgid "Future user successfully removed from group."
msgstr "A leendő csoporttag eltávolításra került." msgstr "A leendő csoporttag eltávolításra került."
#: dashboard/views.py:2053 #: dashboard/views.py:2087
msgid "Group successfully deleted." msgid "Group successfully deleted."
msgstr "A csoport törlésre került." msgstr "A csoport törlésre került."
#: dashboard/views.py:2102 #: dashboard/views.py:2136
msgid "Customize VM" msgid "Customize VM"
msgstr "VM testreszabása" msgstr "VM testreszabása"
#: dashboard/views.py:2110 #: dashboard/views.py:2144
msgid "Create a VM" msgid "Create a VM"
msgstr "VM létrehozása" msgstr "VM létrehozása"
#: dashboard/views.py:2179 #: dashboard/views.py:2213
#, python-format #, python-format
msgid "Successfully created %(count)d VM." msgid "Successfully created %(count)d VM."
msgid_plural "Successfully created %(count)d VMs." msgid_plural "Successfully created %(count)d VMs."
msgstr[0] "%(count)d VM létrehozásra került." msgstr[0] "%(count)d VM létrehozásra került."
msgstr[1] "%(count)d VM létrehozásra került." msgstr[1] "%(count)d VM létrehozásra került."
#: dashboard/views.py:2184 #: dashboard/views.py:2218
msgid "VM successfully created." msgid "VM successfully created."
msgstr "VM létrehozásra került." msgstr "VM létrehozásra került."
#: dashboard/views.py:2213 #: dashboard/views.py:2247
#, python-format #, python-format
msgid "Instance limit (%d) exceeded." msgid "Instance limit (%d) exceeded."
msgstr "A példányok létrehozási korlátját (%d) túllépte." msgstr "A példányok létrehozási korlátját (%d) túllépte."
#: dashboard/views.py:2273 #: dashboard/views.py:2307
msgid "Node successfully created." msgid "Node successfully created."
msgstr "A csomópont létrehozásra került." msgstr "A csomópont létrehozásra került."
#: dashboard/views.py:2301 #: dashboard/views.py:2335
msgid "Create a Group" msgid "Create a Group"
msgstr "Csoport létrehozása" msgstr "Csoport létrehozása"
#: dashboard/views.py:2317 #: dashboard/views.py:2351
msgid "Group successfully created." msgid "Group successfully created."
msgstr "A csoport létrehozásra került." msgstr "A csoport létrehozásra került."
#: dashboard/views.py:2331 #: dashboard/views.py:2365
msgid "Group is successfully updated." msgid "Group is successfully updated."
msgstr "A csoport frissítésre került." msgstr "A csoport frissítésre került."
#: dashboard/views.py:2394 #: dashboard/views.py:2428
msgid "Node successfully deleted." msgid "Node successfully deleted."
msgstr "A csomópont törlésre került." msgstr "A csomópont törlésre került."
#: dashboard/views.py:2441 #: dashboard/views.py:2475
msgid "Trait successfully added to node." msgid "Trait successfully added to node."
msgstr "A csomópontjellemző hozzáadásra került." msgstr "A csomópontjellemző hozzáadásra került."
#: dashboard/views.py:2486 #: dashboard/views.py:2520
msgid "Node successfully changed status." msgid "Node successfully changed status."
msgstr "A csomópont állapota megváltoztatásra került." msgstr "A csomópont állapota megváltoztatásra került."
#: dashboard/views.py:2533 #: dashboard/views.py:2567
msgid "Node successfully flushed." msgid "Node successfully flushed."
msgstr "A csomópont ürítésre kerül." msgstr "A csomópont ürítésre kerül."
#: dashboard/views.py:2552 #: dashboard/views.py:2586
msgid "Port delete confirmation" msgid "Port delete confirmation"
msgstr "Porteltávolítás megerősítése" msgstr "Porteltávolítás megerősítése"
#: dashboard/views.py:2553 #: dashboard/views.py:2587
#, python-format #, python-format
msgid "Are you sure you want to close %(port)d/%(proto)s on %(vm)s?" msgid "Are you sure you want to close %(port)d/%(proto)s on %(vm)s?"
msgstr "Biztosan bezárja a(z) %(port)d/%(proto)s portot a következőn: %(vm)s?" msgstr "Biztosan bezárja a(z) %(port)d/%(proto)s portot a következőn: %(vm)s?"
#: dashboard/views.py:2568 #: dashboard/views.py:2602
msgid "Port successfully removed." msgid "Port successfully removed."
msgstr "A port eltávolításra került." msgstr "A port eltávolításra került."
#: dashboard/views.py:2590 #: dashboard/views.py:2624
msgid "Successfully created a new lease." msgid "Successfully created a new lease."
msgstr "Új bérlési mód létrehozásra került." msgstr "Új bérlési mód létrehozásra került."
#: dashboard/views.py:2605 #: dashboard/views.py:2639
msgid "Successfully modified lease." msgid "Successfully modified lease."
msgstr "A bérlési mód megváltoztatásra került." msgstr "A bérlési mód megváltoztatásra került."
#: dashboard/views.py:2635 #: dashboard/views.py:2669
msgid "" msgid ""
"You can't delete this lease because some templates are still using it, " "You can't delete this lease because some templates are still using it, "
"modify these to proceed: " "modify these to proceed: "
...@@ -746,19 +750,19 @@ msgstr "" ...@@ -746,19 +750,19 @@ msgstr ""
"Nem törölhető a bérleti mód, mivel az alábbi sablonok még használják. A " "Nem törölhető a bérleti mód, mivel az alábbi sablonok még használják. A "
"folytatáshoz módosítsa őket: " "folytatáshoz módosítsa őket: "
#: dashboard/views.py:2652 #: dashboard/views.py:2686
msgid "Lease successfully deleted." msgid "Lease successfully deleted."
msgstr "A bérlési mód törlésre került." msgstr "A bérlési mód törlésre került."
#: dashboard/views.py:2734 #: dashboard/views.py:2769
msgid "Can not find specified user." msgid "Can not find specified user."
msgstr "Nem található a megadott felhasználó." msgstr "Nem található a megadott felhasználó."
#: dashboard/views.py:2750 #: dashboard/views.py:2785
msgid "Ownership offer" msgid "Ownership offer"
msgstr "Átruházási ajánlat" msgstr "Átruházási ajánlat"
#: dashboard/views.py:2751 #: dashboard/views.py:2786
#, python-format #, python-format
msgid "" msgid ""
"%(user)s offered you to take the ownership of his/her virtual machine called " "%(user)s offered you to take the ownership of his/her virtual machine called "
...@@ -768,49 +772,49 @@ msgstr "" ...@@ -768,49 +772,49 @@ msgstr ""
"%(user)s át kívánja ruházni %(instance)s nevű virtuális gépét Önre. <a href=" "%(user)s át kívánja ruházni %(instance)s nevű virtuális gépét Önre. <a href="
"\"%(token)s\" class=\"btn btn-success btn-small\">Elfogadás</a>" "\"%(token)s\" class=\"btn btn-success btn-small\">Elfogadás</a>"
#: dashboard/views.py:2757 #: dashboard/views.py:2792
msgid "Can not notify selected user." msgid "Can not notify selected user."
msgstr "A kiválaszott felhasználó értesítése sikertelen." msgstr "A kiválaszott felhasználó értesítése sikertelen."
#: dashboard/views.py:2760 #: dashboard/views.py:2795
#, python-format #, python-format
msgid "User %s is notified about the offer." msgid "User %s is notified about the offer."
msgstr "%s felhasználó értesítésre került az ajánlatról." msgstr "%s felhasználó értesítésre került az ajánlatról."
#: dashboard/views.py:2771 #: dashboard/views.py:2806
msgid "Ownership successfully transferred to you." msgid "Ownership successfully transferred to you."
msgstr "A tulajdon átruházásra került." msgstr "A tulajdon átruházásra került."
#: dashboard/views.py:2784 #: dashboard/views.py:2819
msgid "This token is for an other user." msgid "This token is for an other user."
msgstr "A token más felhasználó nevére szól." msgstr "A token más felhasználó nevére szól."
#: dashboard/views.py:2787 #: dashboard/views.py:2822
msgid "This token is invalid or has expired." msgid "This token is invalid or has expired."
msgstr "A token érvénytelen vagy lejárt." msgstr "A token érvénytelen vagy lejárt."
#: dashboard/views.py:2809 #: dashboard/views.py:2844
msgid "Ownership accepted" msgid "Ownership accepted"
msgstr "Átruházás elfogadva" msgstr "Átruházás elfogadva"
#: dashboard/views.py:2810 #: dashboard/views.py:2845
#, python-format #, python-format
msgid "Your ownership offer of %(instance)s has been accepted by %(user)s." msgid "Your ownership offer of %(instance)s has been accepted by %(user)s."
msgstr "%(instance)s gépre vonatkozó átruházási ajánlatát elfogadta %(user)s." msgstr "%(instance)s gépre vonatkozó átruházási ajánlatát elfogadta %(user)s."
#: dashboard/views.py:2989 #: dashboard/views.py:3024
msgid "You don't have a profile." msgid "You don't have a profile."
msgstr "Nincs profilja." msgstr "Nincs profilja."
#: dashboard/views.py:3027 #: dashboard/views.py:3062
msgid "Successfully modified subscription." msgid "Successfully modified subscription."
msgstr "A feliratkozás módosításra került." msgstr "A feliratkozás módosításra került."
#: dashboard/views.py:3088 #: dashboard/views.py:3123
msgid "Disk remove confirmation" msgid "Disk remove confirmation"
msgstr "Lemez törlésének megerősítése" msgstr "Lemez törlésének megerősítése"
#: dashboard/views.py:3089 #: dashboard/views.py:3124
#, python-format #, python-format
msgid "" msgid ""
"Are you sure you want to remove <strong>%(disk)s</strong> from <strong>" "Are you sure you want to remove <strong>%(disk)s</strong> from <strong>"
...@@ -819,71 +823,71 @@ msgstr "" ...@@ -819,71 +823,71 @@ msgstr ""
"Biztosan eltávolítja a(z) <strong>%(disk)s</strong> lemezt a következőből: " "Biztosan eltávolítja a(z) <strong>%(disk)s</strong> lemezt a következőből: "
"%(app)s?" "%(app)s?"
#: dashboard/views.py:3108 #: dashboard/views.py:3143
msgid "Disk successfully removed." msgid "Disk successfully removed."
msgstr "A lemez eltávolításra került." msgstr "A lemez eltávolításra került."
#: dashboard/views.py:3205 #: dashboard/views.py:3240
#, python-format #, python-format
msgid "" msgid ""
"Are you sure you want to remove this interface from <strong>%(vm)s</strong>?" "Are you sure you want to remove this interface from <strong>%(vm)s</strong>?"
msgstr "" msgstr ""
"Biztosan eltávolítja az interfészt a(z) <strong>%(vm)s</strong> gépből?" "Biztosan eltávolítja az interfészt a(z) <strong>%(vm)s</strong> gépből?"
#: dashboard/views.py:3219 #: dashboard/views.py:3254
msgid "Interface successfully deleted." msgid "Interface successfully deleted."
msgstr "Az interfész törlésre került." msgstr "Az interfész törlésre került."
#: dashboard/views.py:3320 #: dashboard/views.py:3355
msgid "Successfully modified SSH key." msgid "Successfully modified SSH key."
msgstr "Az SSH kulcs módosításra került." msgstr "Az SSH kulcs módosításra került."
#: dashboard/views.py:3358 #: dashboard/views.py:3393
msgid "SSH key successfully deleted." msgid "SSH key successfully deleted."
msgstr "Az SSH kulcs törlésre került." msgstr "Az SSH kulcs törlésre került."
#: dashboard/views.py:3374 #: dashboard/views.py:3409
msgid "Successfully created a new SSH key." msgid "Successfully created a new SSH key."
msgstr "Az új SSH kulcs hozzáadásra került." msgstr "Az új SSH kulcs hozzáadásra került."
#: dashboard/views.py:3390 #: dashboard/views.py:3425
msgid "Successfully modified command template." msgid "Successfully modified command template."
msgstr "A parancssablon módosításra került." msgstr "A parancssablon módosításra került."
#: dashboard/views.py:3433 #: dashboard/views.py:3468
msgid "Command template successfully deleted." msgid "Command template successfully deleted."
msgstr "A parancssablon törlésre került." msgstr "A parancssablon törlésre került."
#: dashboard/views.py:3450 #: dashboard/views.py:3485
msgid "Successfully created a new command template." msgid "Successfully created a new command template."
msgstr "A parancssablon létrehozásra került." msgstr "A parancssablon létrehozásra került."
#: dashboard/views.py:3499 #: dashboard/views.py:3534
msgid "No store." msgid "No store."
msgstr "Nincs tárhely." msgstr "Nincs tárhely."
#: dashboard/views.py:3501 #: dashboard/views.py:3536
msgid "Store has some problems now. Try again later." msgid "Store has some problems now. Try again later."
msgstr "A tárhely nem működik. Próbálja később." msgstr "A tárhely nem működik. Próbálja később."
#: dashboard/views.py:3505 #: dashboard/views.py:3540
msgid "Unknown store error." msgid "Unknown store error."
msgstr "Ismeretlen tárhelyhiba." msgstr "Ismeretlen tárhelyhiba."
#: dashboard/views.py:3522 #: dashboard/views.py:3557
msgid "Something went wrong during download." msgid "Something went wrong during download."
msgstr "Hiba a letöltésben." msgstr "Hiba a letöltésben."
#: dashboard/views.py:3537 dashboard/views.py:3557 #: dashboard/views.py:3572 dashboard/views.py:3592
msgid "Unable to upload file." msgid "Unable to upload file."
msgstr "Fájl feltöltése sikertelen." msgstr "Fájl feltöltése sikertelen."
#: dashboard/views.py:3594 #: dashboard/views.py:3629
#, python-format #, python-format
msgid "Unable to remove %s." msgid "Unable to remove %s."
msgstr "%s törlése sikertelen." msgstr "%s törlése sikertelen."
#: dashboard/views.py:3613 #: dashboard/views.py:3648
msgid "Unable to create folder." msgid "Unable to create folder."
msgstr "Mappa létrehozása sikertelen." msgstr "Mappa létrehozása sikertelen."
...@@ -920,6 +924,76 @@ msgstr "Támogatás" ...@@ -920,6 +924,76 @@ msgstr "Támogatás"
msgid "Confirmation" msgid "Confirmation"
msgstr "Megerősítés" msgstr "Megerősítés"
#: dashboard/templates/dashboard/_client-check.html:4
msgid ""
"\n"
" To effortlessly connect to all kind of virtual machines you have to "
"install the <strong>CIRCLE Client</strong>.\n"
" "
msgstr ""
"\n"
" A virtuális gépekhez való könnyű csatlakozáshoz telepítse a "
"<strong>CIRCLE klienst</strong>.\n"
" "
#: dashboard/templates/dashboard/_client-check.html:9
msgid ""
"\n"
" To install the <strong>CIRCLE Client</strong> click on the "
"<strong>Download the Client</strong> button. \n"
" The button takes you to the installation detail page, where you can "
"choose your operating system and start \n"
" the download or read more detailed information about the <strong>Client</"
"strong>. The program can be installed on Windows XP (and above)\n"
" or Debian based Linux operating systems. To successfully install the "
"client you have to have admin (root or elevated) rights.\n"
" After the installation complete clicking on the <strong>I have the "
"Client installed</strong> button will launch the appropriate tool\n"
" designed for that connection with necessarily predefined configurations. "
"This option will also save your answer and this prompt about\n"
" installation will not pop up again.\n"
" "
msgstr ""
"\n"
" A <strong>CIRCLE kliens</strong> letöltéséhez kattintson a "
"<strong>Kliens letöltése</strong> gombra. \n"
" A gomb a letöltőoldalra visz, ahol kiválaszthatja a megfelelő <strong>"
"kliens</strong> "
"telepítőcsomagot.\n"
" A telepítés után kattintson a <strong>Már telepítve van</strong> gombra a "
"megfelelő eszköz indításához. "
"Ezt a választást a rendszer megjegyzi.\n"
" "
#: dashboard/templates/dashboard/_client-check.html:23
#: dashboard/templates/dashboard/mass-operate.html:33
#: dashboard/templates/dashboard/operate.html:21
#: dashboard/templates/dashboard/confirm/ajax-delete.html:15
#: dashboard/templates/dashboard/confirm/ajax-node-flush.html:17
#: dashboard/templates/dashboard/confirm/ajax-node-status.html:17
#: dashboard/templates/dashboard/confirm/base-delete.html:27
#: dashboard/templates/dashboard/confirm/mass-delete.html:11
#: dashboard/templates/dashboard/confirm/node-status.html:27
#: dashboard/templates/dashboard/store/remove.html:34
msgid "Cancel"
msgstr "Mégsem"
#: dashboard/templates/dashboard/_client-check.html:24
msgid "Download the Client"
msgstr "Kliens letöltése"
#: dashboard/templates/dashboard/_client-check.html:25
msgid ""
"I downloaded and installed the client and I want to connect using it. This "
"choice will be saved to your compuer"
msgstr ""
"A kliens letöltésre és telepítésre került. Ez a beállítás "
"mentésre kerül."
#: dashboard/templates/dashboard/_client-check.html:26
msgid "I have the Client installed"
msgstr "Már telepítve van"
#: dashboard/templates/dashboard/_disk-list-element.html:10 #: dashboard/templates/dashboard/_disk-list-element.html:10
#: dashboard/templates/dashboard/node-detail/_activity-timeline.html:28 #: dashboard/templates/dashboard/node-detail/_activity-timeline.html:28
#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:45 #: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:45
...@@ -1054,7 +1128,7 @@ msgstr "Lemezek" ...@@ -1054,7 +1128,7 @@ msgstr "Lemezek"
#: dashboard/templates/dashboard/_vm-create-1.html:40 #: dashboard/templates/dashboard/_vm-create-1.html:40
#: dashboard/templates/dashboard/_vm-create-2.html:65 #: dashboard/templates/dashboard/_vm-create-2.html:65
#: dashboard/templates/dashboard/base.html:46 #: dashboard/templates/dashboard/base.html:46
#: dashboard/templates/dashboard/vm-detail.html:164 #: dashboard/templates/dashboard/vm-detail.html:177
msgid "Network" msgid "Network"
msgstr "Hálózat" msgstr "Hálózat"
...@@ -1454,7 +1528,7 @@ msgstr "Nincs jogosultsága virtuális gépek indítására vagy kezelésére." ...@@ -1454,7 +1528,7 @@ msgstr "Nincs jogosultsága virtuális gépek indítására vagy kezelésére."
#: dashboard/templates/dashboard/instanceactivity_detail.html:25 #: dashboard/templates/dashboard/instanceactivity_detail.html:25
#: dashboard/templates/dashboard/node-detail.html:91 #: dashboard/templates/dashboard/node-detail.html:91
#: dashboard/templates/dashboard/vm-detail.html:169 #: dashboard/templates/dashboard/vm-detail.html:182
#: dashboard/templates/dashboard/node-detail/activity.html:3 #: dashboard/templates/dashboard/node-detail/activity.html:3
#: dashboard/templates/dashboard/vm-detail/activity.html:3 #: dashboard/templates/dashboard/vm-detail/activity.html:3
msgid "Activity" msgid "Activity"
...@@ -1536,18 +1610,6 @@ msgstr[1] "" ...@@ -1536,18 +1610,6 @@ msgstr[1] ""
"Biztosan végrehajtja a(z) <strong>%(op)s</strong> műveletet a következő " "Biztosan végrehajtja a(z) <strong>%(op)s</strong> műveletet a következő "
"%(count)s példányon?\n" "%(count)s példányon?\n"
#: dashboard/templates/dashboard/mass-operate.html:33
#: dashboard/templates/dashboard/operate.html:21
#: dashboard/templates/dashboard/confirm/ajax-delete.html:15
#: dashboard/templates/dashboard/confirm/ajax-node-flush.html:17
#: dashboard/templates/dashboard/confirm/ajax-node-status.html:17
#: dashboard/templates/dashboard/confirm/base-delete.html:27
#: dashboard/templates/dashboard/confirm/mass-delete.html:11
#: dashboard/templates/dashboard/confirm/node-status.html:27
#: dashboard/templates/dashboard/store/remove.html:34
msgid "Cancel"
msgstr "Mégsem"
#: dashboard/templates/dashboard/node-add-trait.html:19 #: dashboard/templates/dashboard/node-add-trait.html:19
msgid "Add Trait" msgid "Add Trait"
msgstr "Jellemző hozzáadása" msgstr "Jellemző hozzáadása"
...@@ -1591,7 +1653,7 @@ msgid "Remove node and it's host." ...@@ -1591,7 +1653,7 @@ msgid "Remove node and it's host."
msgstr "Csomópont és hoszt törlése." msgstr "Csomópont és hoszt törlése."
#: dashboard/templates/dashboard/node-detail.html:72 #: dashboard/templates/dashboard/node-detail.html:72
#: dashboard/templates/dashboard/vm-detail.html:145 #: dashboard/templates/dashboard/vm-detail.html:158
msgid "Home" msgid "Home"
msgstr "Kezdőoldal" msgstr "Kezdőoldal"
...@@ -1845,11 +1907,31 @@ msgstr "Összes kiválasztása" ...@@ -1845,11 +1907,31 @@ msgstr "Összes kiválasztása"
msgid "Connection is not possible." msgid "Connection is not possible."
msgstr "A csatlakozás nem lehetséges." msgstr "A csatlakozás nem lehetséges."
#: dashboard/templates/dashboard/vm-detail.html:155 #: dashboard/templates/dashboard/vm-detail.html:140
msgid "Connect via the CIRCLE Client"
msgstr "Csatlakozás CIRCLE klienssel"
#: dashboard/templates/dashboard/vm-detail.html:141
msgid "Connect"
msgstr "Csatlakozás"
#: dashboard/templates/dashboard/vm-detail.html:143
msgid "Download client"
msgstr "Kliens letöltése"
#: dashboard/templates/dashboard/vm-detail.html:145
msgid "Download the CIRCLE Client"
msgstr "A CIRCLE kliens letöltése"
#: dashboard/templates/dashboard/vm-detail.html:146
msgid "Connect (download client)"
msgstr "Csatlakozás (kliens letöltése)"
#: dashboard/templates/dashboard/vm-detail.html:168
msgid "Console" msgid "Console"
msgstr "Konzol" msgstr "Konzol"
#: dashboard/templates/dashboard/vm-detail.html:159 #: dashboard/templates/dashboard/vm-detail.html:172
msgid "Access" msgid "Access"
msgstr "Hozzáférés" msgstr "Hozzáférés"
...@@ -2032,13 +2114,14 @@ msgstr "Átruházás" ...@@ -2032,13 +2114,14 @@ msgstr "Átruházás"
#, python-format #, python-format
msgid "" msgid ""
"\n" "\n"
" %(owner)s offered to take the ownership of virtual machine " " <strong>%(owner)s</strong> offered to take the ownership of\n"
"%(fqdn)s.\n" " virtual machine <strong>%(name)s (%(id)s)</strong>.\n"
" Do you accept the responsility of being the host's owner?\n" " Do you accept the responsility of being the host's owner?\n"
" " " "
msgstr "" msgstr ""
"\n" "\n"
" %(owner)s kezdeményezte a(z) %(fqdn)s virtuális gép átruházását.\n" " <strong>%(owner)s</strong> kezdeményezte a(z) <strong>%(name)s\n"
" (%(id)s)</strong> virtuális gép átruházását.\n"
" Elfogadja a gép birtoklásával járó felelősséget?\n" " Elfogadja a gép birtoklásával járó felelősséget?\n"
" " " "
...@@ -3631,7 +3714,7 @@ msgstr "" ...@@ -3631,7 +3714,7 @@ msgstr ""
"lemezen, mivel az alapja, „%(b_name)s” (%(b_pk)s) [%(b_filename)s] nem " "lemezen, mivel az alapja, „%(b_name)s” (%(b_pk)s) [%(b_filename)s] nem "
"volt még csatolva." "volt még csatolva."
#: storage/models.py:413 storage/models.py:488 vm/models/instance.py:899 #: storage/models.py:413 storage/models.py:488 vm/models/instance.py:897
msgid "Operation aborted by user." msgid "Operation aborted by user."
msgstr "A műveletet a felhasználó megszakította." msgstr "A műveletet a felhasználó megszakította."
...@@ -5041,7 +5124,7 @@ msgstr "példány létrehozása" ...@@ -5041,7 +5124,7 @@ msgstr "példány létrehozása"
msgid "vm state changed to %(state)s" msgid "vm state changed to %(state)s"
msgstr "VM állapota erre változott: %(state)s" msgstr "VM állapota erre változott: %(state)s"
#: vm/models/instance.py:663 #: vm/models/instance.py:661
#, python-format #, python-format
msgid "" msgid ""
"Your instance <a href=\"%(url)s\">%(instance)s</a> is going to expire. It " "Your instance <a href=\"%(url)s\">%(instance)s</a> is going to expire. It "
...@@ -5053,7 +5136,7 @@ msgstr "" ...@@ -5053,7 +5136,7 @@ msgstr ""
"kerül. Kérjük, <a href=\"%(token)s\">újítsa meg</a> vagy <a href=\"%(url)s" "kerül. Kérjük, <a href=\"%(token)s\">újítsa meg</a> vagy <a href=\"%(url)s"
"\">törölje</a> most." "\">törölje</a> most."
#: vm/models/instance.py:675 #: vm/models/instance.py:673
#, python-format #, python-format
msgid "" msgid ""
"%(failed)s notifications failed and %(success) succeeded. Failed ones are: " "%(failed)s notifications failed and %(success) succeeded. Failed ones are: "
...@@ -5062,7 +5145,7 @@ msgstr "" ...@@ -5062,7 +5145,7 @@ msgstr ""
"%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: " "%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: "
"%(faileds)s." "%(faileds)s."
#: vm/models/instance.py:677 #: vm/models/instance.py:675
#, python-format #, python-format
msgid "" msgid ""
"%(failed)s notifications failed and %(success) succeeded. Failed ones are: " "%(failed)s notifications failed and %(success) succeeded. Failed ones are: "
...@@ -5071,16 +5154,16 @@ msgstr "" ...@@ -5071,16 +5154,16 @@ msgstr ""
"%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: " "%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: "
"%(faileds_ex)s." "%(faileds_ex)s."
#: vm/models/instance.py:685 #: vm/models/instance.py:683
#, python-format #, python-format
msgid "%(success)s notifications succeeded." msgid "%(success)s notifications succeeded."
msgstr "%(success)s sikeres értesítés." msgstr "%(success)s sikeres értesítés."
#: vm/models/instance.py:690 #: vm/models/instance.py:688
msgid "notify owner about expiration" msgid "notify owner about expiration"
msgstr "tulaj értesítése a lejáratról" msgstr "tulaj értesítése a lejáratról"
#: vm/models/instance.py:698 #: vm/models/instance.py:696
#, python-format #, python-format
msgid "%(instance)s expiring soon" msgid "%(instance)s expiring soon"
msgstr "%(instance)s hamarosan lejár" msgstr "%(instance)s hamarosan lejár"
......
...@@ -6,7 +6,7 @@ msgid "" ...@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-09-03 12:57+0200\n" "POT-Creation-Date: 2014-09-03 18:49+0200\n"
"PO-Revision-Date: 2014-09-03 12:51+0200\n" "PO-Revision-Date: 2014-09-03 12:51+0200\n"
"Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n" "Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n" "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
......
...@@ -18,26 +18,37 @@ ...@@ -18,26 +18,37 @@
from logging import getLogger from logging import getLogger
from django.db.models import Sum from django.db.models import Sum
from django.utils.translation import ugettext_noop
from common.models import HumanReadableException
logger = getLogger(__name__) logger = getLogger(__name__)
class NotEnoughMemoryException(Exception): class SchedulerError(HumanReadableException):
admin_message = None
def __init__(self, message=None): def __init__(self, params=None, level=None, **kwargs):
if message is None: kwargs.update(params or {})
message = "No node has enough memory to accomodate the guest." super(SchedulerError, self).__init__(
level, self.message, self.admin_message or self.message,
kwargs)
Exception.__init__(self, message)
class NotEnoughMemoryException(SchedulerError):
message = ugettext_noop(
"The resources required for launching the virtual machine are not "
"available currently. Please try again later.")
class TraitsUnsatisfiableException(Exception): admin_message = ugettext_noop(
"The required free memory for launching the virtual machine is not "
"available on any usable node currently. Please try again later.")
def __init__(self, message=None):
if message is None:
message = "No node can satisfy all required traits of the guest."
Exception.__init__(self, message) class TraitsUnsatisfiableException(SchedulerError):
message = ugettext_noop(
"No node can satisfy the required traits of the "
"new vitual machine currently.")
def select_node(instance, nodes): def select_node(instance, nodes):
...@@ -77,18 +88,26 @@ def has_enough_ram(ram_size, node): ...@@ -77,18 +88,26 @@ def has_enough_ram(ram_size, node):
"""True, if the node has enough memory to accomodate a guest requiring """True, if the node has enough memory to accomodate a guest requiring
ram_size mebibytes of memory; otherwise, false. ram_size mebibytes of memory; otherwise, false.
""" """
ram_size = ram_size * 1024 * 1024
try: try:
total = node.ram_size total = node.ram_size
used = (node.ram_usage / 100) * total used = node.byte_ram_usage
unused = total - used unused = total - used
overcommit = node.ram_size_with_overcommit overcommit = node.ram_size_with_overcommit
reserved = node.instance_set.aggregate(r=Sum('ram_size'))['r'] or 0 reserved = (node.instance_set.aggregate(
r=Sum('ram_size'))['r'] or 0) * 1024 * 1024
free = overcommit - reserved free = overcommit - reserved
return ram_size < unused and ram_size < free retval = ram_size < unused and ram_size < free
logger.debug('has_enough_ram(%d, %s)=%s (total=%s unused=%s'
' overcommit=%s free=%s free_ok=%s overcommit_ok=%s)',
ram_size, node, retval, total, unused, overcommit, free,
ram_size < unused, ram_size < free)
return retval
except TypeError as e: except TypeError as e:
logger.warning('Got incorrect monitoring data for node %s. %s', logger.exception('Got incorrect monitoring data for node %s. %s',
unicode(node), unicode(e)) unicode(node), unicode(e))
return False return False
......
...@@ -6,3 +6,8 @@ ...@@ -6,3 +6,8 @@
text-align: center; text-align: center;
} }
#host-detail-records-table td:first-child,
#host-detail-records-table th:first-child {
text-align: center;
width: 60px;
}
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _
from django_tables2 import Table, A from django_tables2 import Table, A
from django_tables2.columns import LinkColumn, TemplateColumn from django_tables2.columns import LinkColumn, TemplateColumn
...@@ -181,3 +183,20 @@ class VlanGroupTable(Table): ...@@ -181,3 +183,20 @@ class VlanGroupTable(Table):
attrs = {'class': 'table table-striped table-condensed'} attrs = {'class': 'table table-striped table-condensed'}
fields = ('name', 'vlans', 'description', 'owner', ) fields = ('name', 'vlans', 'description', 'owner', )
order_by = 'name' order_by = 'name'
class HostRecordsTable(Table):
fqdn = LinkColumn(
"network.record", args=[A("pk")],
order_by=("name", ),
)
class Meta:
model = Record
attrs = {
'class': "table table-striped table-bordered",
'id': "host-detail-records-table",
}
fields = ("type", "fqdn")
order_by = ("name", )
empty_text = _("No records.")
{% load i18n %} {% load i18n %}
{% load l10n %} {% load l10n %}
{# <span style="color: #FF0000;">[{{ record.r_type }}]</span> #} {% if record.direction == "in" %}
{% if record.direction == "1" %}
{{ record.foreign_network }} {{ record.foreign_network }}
[{% for v in record.foreign_network.vlans.all %}
{{ v.name }}{% if not forloop.last %},{% endif %}
{% endfor %}]
{% else %} {% else %}
{% if record.r_type == "host" %} {% if record.r_type == "host" %}
{{ record.host.get_fqdn }} {{ record.host.get_fqdn }}
...@@ -11,10 +13,10 @@ ...@@ -11,10 +13,10 @@
{{ record.r_type }} {{ record.r_type }}
{% endif %} {% endif %}
{% endif %} {% endif %}
{#<span style="color: #0000FF;"> ▸ </span>#}
<i class="fa fa-arrow-right"></i> <i class="fa fa-arrow-right"></i>
{% if record.direction == "0" %}
{% if record.direction == "out" %}
{{ record.foreign_network }} {{ record.foreign_network }}
{% else %} {% else %}
{% if record.r_type == "host" %} {% if record.r_type == "host" %}
...@@ -33,6 +35,11 @@ ...@@ -33,6 +35,11 @@
{% if record.nat %} {% if record.nat %}
<span class="label label-success">NAT <span class="label label-success">NAT
[ {{ record.dport }} <i class="fa fa-arrow-right"></i> [
{{record.nat_external_port}} ]</span> {{record.nat_external_port}}
<i class="fa fa-arrow-right"></i>
{{ record.dport }}
]
{{ record.proto|upper }}
</span>
{% endif %} {% endif %}
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-7"> <div class="col-md-6">
{% crispy form %} {% crispy form %}
</div> </div>
<div class="col-sm-5"> <div class="col-md-6">
<div class="page-header"> <div class="page-header">
<a href="{% url "network.rule_create" %}?host={{ host_pk }}" class="btn btn-success pull-right btn-xs"><i class="fa fa-plus-circle"></i> {% trans "Add new rule" %}</a> <a href="{% url "network.rule_create" %}?host={{ host_pk }}" class="btn btn-success pull-right btn-xs"><i class="fa fa-plus-circle"></i> {% trans "Add new rule" %}</a>
<h3>{% trans "Rules" %}</h3> <h3>{% trans "Rules" %}</h3>
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
{% endif %} {% endif %}
<div class="page-header"> <div class="page-header">
<h3>Add host group</h3> <h3>{% trans "Add host group" %}</h3>
</div> </div>
{% if not_used_groups|length == 0 %} {% if not_used_groups|length == 0 %}
No more groups to add! No more groups to add!
...@@ -64,7 +64,12 @@ ...@@ -64,7 +64,12 @@
</div><!-- input-group --> </div><!-- input-group -->
</form> </form>
{% endif %} {% endif %}
</div><!-- col-sm-4 --> <div class="page-header">
<h3>{% trans "Records" %}</h3>
</div>
{% render_table records_table %}
</div><!-- col-sm-5 -->
</div><!-- row --> </div><!-- row -->
{% endblock %} {% endblock %}
......
...@@ -399,6 +399,12 @@ class HostDetail(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -399,6 +399,12 @@ class HostDetail(LoginRequiredMixin, SuperuserRequiredMixin,
# set host pk (we need this for URL-s) # set host pk (we need this for URL-s)
context['host_pk'] = self.kwargs['pk'] context['host_pk'] = self.kwargs['pk']
from network.tables import HostRecordsTable
context['records_table'] = HostRecordsTable(
Record.objects.filter(host=self.get_object()),
request=self.request, template="django_tables2/table_no_page.html"
)
return context return context
def get_success_url(self): def get_success_url(self):
......
...@@ -11,10 +11,6 @@ ...@@ -11,10 +11,6 @@
body { body {
margin-top: 40px; margin-top: 40px;
} }
.container {
width: 600px;
}
ul, li { ul, li {
list-style: none; list-style: none;
margin: 0; margin: 0;
...@@ -22,6 +18,8 @@ ...@@ -22,6 +18,8 @@
} }
.container > .content { .container > .content {
max-width: 570px;
margin: auto;
background-color: #fff; background-color: #fff;
padding: 20px; padding: 20px;
-webkit-border-radius: 10px 10px 10px 10px; -webkit-border-radius: 10px 10px 10px 10px;
...@@ -38,8 +36,10 @@ ...@@ -38,8 +36,10 @@
} }
.login-form { .login-form {
margin-top: 40px; margin: 20px auto 0 auto;
padding: 0 10px; padding: 0 10px;
max-width: 250px;
} }
.login-form form { .login-form form {
...@@ -79,3 +79,10 @@ ...@@ -79,3 +79,10 @@
<img src="{{ STATIC_URL}}dashboard/img/logo.png" style="height: 25px;"/> <img src="{{ STATIC_URL}}dashboard/img/logo.png" style="height: 25px;"/>
</a> </a>
{% endblock %} {% endblock %}
{% block content %}
<div class="content">
{% block content_box %}{% endblock %}
</div>
{% endblock %}
...@@ -11,15 +11,14 @@ ...@@ -11,15 +11,14 @@
</a> </a>
{% endblock %} {% endblock %}
{% block content %} {% block content_box %}
<div class="content">
<div class="row"> <div class="row">
{% if form.password.errors or form.username.errors %} {% if form.password.errors or form.username.errors %}
<div class="login-form-errors"> <div class="login-form-errors">
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
</div> </div>
{% endif %} {% endif %}
<div class="col-sm-{% if saml2 %}6{% else %}12{% endif %}"> <div class="col-xs-{% if saml2 %}6{% else %}12{% endif %}">
<div class="login-form"> <div class="login-form">
<form action="" method="POST"> <form action="" method="POST">
{% csrf_token %} {% csrf_token %}
...@@ -28,8 +27,8 @@ ...@@ -28,8 +27,8 @@
</div> </div>
</div> </div>
{% if saml2 %} {% if saml2 %}
<div class="col-sm-6"> <div class="col-xs-6">
<h4 style="padding-top: 0; margin-top: 0;">{% trans "Login with SSO" %}</h4> <h4 style="padding-top: 0; margin-top: 20px;">{% trans "Login with SSO" %}</h4>
<a href="{% url "saml2_login" %}">{% trans "Click here!" %}</a> <a href="{% url "saml2_login" %}">{% trans "Click here!" %}</a>
</div> </div>
{% endif %} {% endif %}
......
...@@ -5,9 +5,8 @@ ...@@ -5,9 +5,8 @@
{% block title-page %}{% trans "Password reset complete" %}{% endblock %} {% block title-page %}{% trans "Password reset complete" %}{% endblock %}
{% block content %} {% block content_box %}
<div class="content"> <div class="row">
<div class="row">
<div class="login-form-errors"> <div class="login-form-errors">
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
</div> </div>
...@@ -17,6 +16,5 @@ ...@@ -17,6 +16,5 @@
<a href="{% url "accounts.login" %}">{% trans "Click here to login" %}</a> <a href="{% url "accounts.login" %}">{% trans "Click here to login" %}</a>
</div> </div>
</div> </div>
</div>
</div> </div>
{% endblock %} {% endblock %}
...@@ -5,15 +5,16 @@ ...@@ -5,15 +5,16 @@
{% block title-page %}{% trans "Password reset confirm" %}{% endblock %} {% block title-page %}{% trans "Password reset confirm" %}{% endblock %}
{% block content %} {% block content_box %}
<div> <div class="row">
<div class="row">
<div class="login-form-errors"> <div class="login-form-errors">
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div style="margin: 0 0 25px 0;"> <div>
{% blocktrans %}Please enter your new password twice so we can verify you typed it in correctly!{% endblocktrans %} {% blocktrans %}
Please enter your new password twice so we can verify you typed it in correctly.
{% endblocktrans %}
</div> </div>
{% if form %} {% if form %}
...@@ -21,10 +22,9 @@ ...@@ -21,10 +22,9 @@
{% else %} {% else %}
<div class="alert alert-warning"> <div class="alert alert-warning">
{% url "accounts.password-reset" as url %} {% url "accounts.password-reset" as url %}
{% blocktrans with url=url %}This token is expired, please <a href="{{ url }}">request</a> a new password reset link again!{% endblocktrans %} {% blocktrans with url=url %}This token is expired, please <a href="{{ url }}">request</a> a new password reset link again.{% endblocktrans %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div>
</div> </div>
{% endblock %} {% endblock %}
...@@ -5,16 +5,14 @@ ...@@ -5,16 +5,14 @@
{% block title-page %}{% trans "Password reset done" %}{% endblock %} {% block title-page %}{% trans "Password reset done" %}{% endblock %}
{% block content %} {% block content_box %}
<div class="content"> <div class="row">
<div class="row">
<div class="login-form-errors"> <div class="login-form-errors">
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="pull-right"><a href="{% url "accounts.login" %}">{% trans "Back to login" %}</a></div> <div class="pull-right"><a href="{% url "accounts.login" %}">{% trans "Back to login" %}</a></div>
{% trans "We have sent you an email about your next steps!" %} {% trans "We have sent you an email about your next steps." %}
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
...@@ -5,20 +5,20 @@ ...@@ -5,20 +5,20 @@
{% block title-page %}{% trans "Password reset" %}{% endblock %} {% block title-page %}{% trans "Password reset" %}{% endblock %}
{% block content %} {% block content_box %}
<div class="content"> <div class="row">
<div class="row">
<div class="login-form-errors"> <div class="login-form-errors">
{% include "display-form-errors.html" %} {% include "display-form-errors.html" %}
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="pull-right"><a href="{% url "accounts.login" %}">{% trans "Back to login" %}</a></div> <div class="pull-right"><a href="{% url "accounts.login" %}">{% trans "Back to login" %}</a></div>
<h4 style="margin: 0 0 25px 0;">{% blocktrans %}Enter your email address to reset your password!{% endblocktrans %}</h4> <h4 style="margin: 0 0 25px 0;">
{% blocktrans %}Enter your email address to reset your password.{% endblocktrans %}
</h4>
<form action="" method="POST"> <form action="" method="POST">
{% csrf_token %} {% csrf_token %}
{% crispy form %} {% crispy form %}
</form> </form>
</div> </div>
</div>
</div> </div>
{% endblock %} {% endblock %}
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Instance.active_since'
db.delete_column(u'vm_instance', 'active_since')
def backwards(self, orm):
# Adding field 'Instance.active_since'
db.add_column(u'vm_instance', 'active_since',
self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
keep_default=False)
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'ordering': "('normalized_hostname', 'vlan')", 'unique_together': "(('hostname', 'vlan'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'external_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'normalized_hostname': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '80', 'monitor': "'hostname'", 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
'host_ipv6_prefixlen': ('django.db.models.fields.IntegerField', [], {'default': '112'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv6_template': ('django.db.models.fields.TextField', [], {'default': "'2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0'"}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'network_type': ('django.db.models.fields.CharField', [], {'default': "'portforward'", 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'storage.datastore': {
'Meta': {'ordering': "[u'name']", 'object_name': 'DataStore'},
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
u'storage.disk': {
'Meta': {'ordering': "[u'name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'derivatives'", 'null': 'True', 'to': u"orm['storage.Disk']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DataStore']"}),
'destroyed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'dev_num': ('django.db.models.fields.CharField', [], {'default': "u'a'", 'max_length': '1'}),
'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'size': ('sizefield.models.FileSizeField', [], {'default': 'None', 'null': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
},
u'vm.instance': {
'Meta': {'ordering': "(u'pk',)", 'object_name': 'Instance'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'destroyed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'instance_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_base': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'instance_set'", 'null': 'True', 'to': u"orm['vm.Node']"}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'pw': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'req_traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'}),
'status': ('model_utils.fields.StatusField', [], {'default': "u'NOSTATE'", 'max_length': '100', u'no_check_for_status': 'True'}),
'status_changed': ('model_utils.fields.MonitorField', [], {'default': 'datetime.datetime.now', u'monitor': "u'status'"}),
'system': ('django.db.models.fields.TextField', [], {}),
'template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'instance_set'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['vm.InstanceTemplate']"}),
'time_of_delete': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'time_of_suspend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'vnc_port': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'})
},
u'vm.instanceactivity': {
'Meta': {'ordering': "[u'-finished', u'-started', u'instance', u'-id']", 'object_name': 'InstanceActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'activity_log'", 'to': u"orm['vm.Instance']"}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': u"orm['vm.InstanceActivity']"}),
'readable_name_data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
'result_data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
'resultant_state': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'succeeded': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'vm.instancetemplate': {
'Meta': {'ordering': "(u'name',)", 'object_name': 'InstanceTemplate'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'template_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.InstanceTemplate']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'req_traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'}),
'system': ('django.db.models.fields.TextField', [], {})
},
u'vm.interface': {
'Meta': {'ordering': "(u'-vlan__managed',)", 'object_name': 'Interface'},
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'interface_set'", 'to': u"orm['vm.Instance']"}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'vm_interface'", 'to': u"orm['firewall.Vlan']"})
},
u'vm.interfacetemplate': {
'Meta': {'object_name': 'InterfaceTemplate'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'interface_set'", 'to': u"orm['vm.InstanceTemplate']"}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'vm.lease': {
'Meta': {'ordering': "[u'name']", 'object_name': 'Lease'},
'delete_interval_seconds': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'suspend_interval_seconds': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
u'vm.namedbaseresourceconfig': {
'Meta': {'object_name': 'NamedBaseResourceConfig'},
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {})
},
u'vm.node': {
'Meta': {'ordering': "(u'-enabled', u'normalized_name')", 'object_name': 'Node'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'normalized_name': ('common.models.HumanSortField', [], {'default': "''", 'maximum_number_length': '4', 'max_length': '100', 'monitor': "u'name'", 'blank': 'True'}),
'overcommit': ('django.db.models.fields.FloatField', [], {'default': '1.0'}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'traits': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['vm.Trait']", 'symmetrical': 'False', 'blank': 'True'})
},
u'vm.nodeactivity': {
'Meta': {'object_name': 'NodeActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'activity_log'", 'to': u"orm['vm.Node']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': u"orm['vm.NodeActivity']"}),
'readable_name_data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
'result_data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'succeeded': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'vm.trait': {
'Meta': {'object_name': 'Trait'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}
complete_apps = ['vm']
\ No newline at end of file
...@@ -244,10 +244,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -244,10 +244,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
verbose_name=_('time of delete'), verbose_name=_('time of delete'),
help_text=_("Proposed time of automatic " help_text=_("Proposed time of automatic "
"deletion.")) "deletion."))
active_since = DateTimeField(blank=True, null=True,
help_text=_("Time stamp of successful "
"boot report."),
verbose_name=_('active since'))
node = ForeignKey(Node, blank=True, null=True, node = ForeignKey(Node, blank=True, null=True,
related_name='instance_set', related_name='instance_set',
help_text=_("Current hypervisor of this instance."), help_text=_("Current hypervisor of this instance."),
...@@ -532,15 +528,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -532,15 +528,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
return self.primary_host.mac if self.primary_host else None return self.primary_host.mac if self.primary_host else None
@property @property
def uptime(self):
"""Uptime of the instance.
"""
if self.active_since:
return timezone.now() - self.active_since
else:
return timedelta() # zero
@property
def os_type(self): def os_type(self):
"""Get the type of the instance's operating system. """Get the type of the instance's operating system.
""" """
...@@ -549,13 +536,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -549,13 +536,6 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
else: else:
return self.template.os_type return self.template.os_type
def get_age(self):
"""Deprecated. Use uptime instead.
Get age of VM in seconds.
"""
return self.uptime.seconds
@property @property
def waiting(self): def waiting(self):
"""Indicates whether the instance's waiting for an operation to finish. """Indicates whether the instance's waiting for an operation to finish.
...@@ -607,14 +587,19 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -607,14 +587,19 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
port = self.get_connect_port(use_ipv6=use_ipv6) port = self.get_connect_port(use_ipv6=use_ipv6)
host = self.get_connect_host(use_ipv6=use_ipv6) host = self.get_connect_host(use_ipv6=use_ipv6)
proto = self.access_method proto = self.access_method
if proto == 'ssh': return ('circle:%(proto)s:cloud:%(pw)s:%(host)s:%(port)d' %
proto = 'sshterm'
return ('%(proto)s:cloud:%(pw)s:%(host)s:%(port)d' %
{'port': port, 'proto': proto, 'pw': self.pw, {'port': port, 'proto': proto, 'pw': self.pw,
'host': host}) 'host': host})
except: except:
return return
@property
def short_hostname(self):
try:
return self.primary_host.hostname
except AttributeError:
return self.vm_name
def get_vm_desc(self): def get_vm_desc(self):
"""Serialize Instance object to vmdriver. """Serialize Instance object to vmdriver.
""" """
......
...@@ -34,6 +34,7 @@ from common.models import ( ...@@ -34,6 +34,7 @@ from common.models import (
create_readable, humanize_exception, HumanReadableException create_readable, humanize_exception, HumanReadableException
) )
from common.operations import Operation, register_operation from common.operations import Operation, register_operation
from manager.scheduler import SchedulerError
from .tasks.local_tasks import ( from .tasks.local_tasks import (
abortable_async_instance_operation, abortable_async_node_operation, abortable_async_instance_operation, abortable_async_node_operation,
) )
...@@ -620,6 +621,17 @@ class ShutdownOperation(InstanceOperation): ...@@ -620,6 +621,17 @@ class ShutdownOperation(InstanceOperation):
self.instance.yield_node() self.instance.yield_node()
self.instance.yield_vnc_port() self.instance.yield_vnc_port()
def on_abort(self, activity, error):
if isinstance(error, TimeLimitExceeded):
activity.result = humanize_exception(ugettext_noop(
"The virtual machine did not switch off in the provided time "
"limit. Most of the time this is caused by incorrect ACPI "
"settings. You can also try to power off the machine from the "
"operating system manually."), error)
activity.resultant_state = None
else:
super(ShutdownOperation, self).on_abort(activity, error)
register_operation(ShutdownOperation) register_operation(ShutdownOperation)
...@@ -717,6 +729,9 @@ class WakeUpOperation(InstanceOperation): ...@@ -717,6 +729,9 @@ class WakeUpOperation(InstanceOperation):
return self.instance.status == self.instance.STATUS.SUSPENDED return self.instance.status == self.instance.STATUS.SUSPENDED
def on_abort(self, activity, error): def on_abort(self, activity, error):
if isinstance(error, SchedulerError):
activity.resultant_state = None
else:
activity.resultant_state = 'ERROR' activity.resultant_state = 'ERROR'
def _operation(self, activity, timeout=60): def _operation(self, activity, timeout=60):
......
...@@ -46,7 +46,7 @@ def send_init_commands(instance, act): ...@@ -46,7 +46,7 @@ def send_init_commands(instance, act):
with act.sub_activity('set_hostname', with act.sub_activity('set_hostname',
readable_name=ugettext_noop('set hostname')): readable_name=ugettext_noop('set hostname')):
set_hostname.apply_async( set_hostname.apply_async(
queue=queue, args=(vm, instance.primary_host.hostname)) queue=queue, args=(vm, instance.short_hostname))
def send_networking_commands(instance, act): def send_networking_commands(instance, act):
......
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