Commit 04bbf626 by Őry Máté

Merge branch 'master' into feature-titles

parents 60fedbfc ea3039d6
......@@ -214,6 +214,14 @@ class ActivityModel(TimeStampedModel):
self.result_data = None if value is None else value.to_dict()
@classmethod
def construct_activity_code(cls, code_suffix, sub_suffix=None):
code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
if sub_suffix:
return join_activity_code(code, sub_suffix)
else:
return code
@celery.task()
def compute_cached(method, instance, memcached_seconds,
......
......@@ -143,7 +143,7 @@ class VmCustomizeForm(forms.Form):
self.template = kwargs.pop("template", None)
super(VmCustomizeForm, self).__init__(*args, **kwargs)
if self.user.has_perm("vm_set_resouces"):
if self.user.has_perm("vm.set_resources"):
self.allowed_fields = tuple(self.fields.keys())
# set displayed disk and network list
self.fields['disks'].queryset = self.template.disks.all()
......@@ -481,7 +481,7 @@ class TemplateForm(forms.ModelForm):
else:
self.allowed_fields = (
'name', 'access_method', 'description', 'system', 'tags',
'arch', 'lease')
'arch', 'lease', 'has_agent')
if (self.user.has_perm('vm.change_template_resources')
or not self.instance.pk):
self.allowed_fields += tuple(set(self.fields.keys()) -
......
......@@ -261,7 +261,7 @@ def get_or_create_profile(self):
Group.profile = property(get_or_create_profile)
def create_profile(sender, user, request, **kwargs):
def create_profile(user):
if not user.pk:
return False
profile, created = Profile.objects.get_or_create(user=user)
......@@ -272,7 +272,11 @@ def create_profile(sender, user, request, **kwargs):
logger.exception("Can't create user %s", unicode(user))
return created
user_logged_in.connect(create_profile)
def create_profile_hook(sender, user, request, **kwargs):
return create_profile(user)
user_logged_in.connect(create_profile_hook)
if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
logger.debug("Register save_org_id to djangosaml2 pre_user_save")
......
......@@ -51,6 +51,7 @@
{{ form.req_traits|as_crispy_field }}
{{ form.description|as_crispy_field }}
{{ form.system|as_crispy_field }}
{{ form.has_agent|as_crispy_field }}
</fieldset>
<fieldset>
<legend>{% trans "External resources" %}</legend>
......
......@@ -91,7 +91,7 @@ from vm.models import (
from storage.models import Disk
from firewall.models import Vlan, Host, Rule
from .models import (Favourite, Profile, GroupProfile, FutureMember,
ConnectCommand)
ConnectCommand, create_profile)
from .store_api import Store, NoStoreException, NotOkException
......@@ -3288,6 +3288,7 @@ class UserCreationView(LoginRequiredMixin, PermissionRequiredMixin,
self.get_group(group_pk)
ret = super(UserCreationView, self).post(*args, **kwargs)
if self.object:
create_profile(self.object)
self.object.groups.add(self.group)
return redirect(
reverse('dashboard.views.group-detail', args=[group_pk]))
......
......@@ -17,6 +17,7 @@
from django.forms import ModelForm
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput
......@@ -56,8 +57,9 @@ class BlacklistItemForm(ModelForm):
)
),
FormActions(
Submit('submit', 'Save changes'),
LinkButton('back', 'Back', reverse_lazy('network.blacklist_list'))
Submit('submit', _('Save changes')),
LinkButton('back', _("Back"),
reverse_lazy('network.blacklist_list'))
)
)
......@@ -77,8 +79,8 @@ class DomainForm(ModelForm):
),
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.domain_list'))
Submit('submit', _('Save')),
LinkButton('back', _("Back"), reverse_lazy('network.domain_list'))
)
)
......@@ -91,15 +93,15 @@ class GroupForm(ModelForm):
helper.layout = Layout(
Div(
Fieldset(
'Identity',
'',
'name',
'description',
'owner',
),
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.group_list'))
Submit('submit', _('Save')),
LinkButton('back', _("Back"), reverse_lazy('network.group_list'))
)
)
......@@ -112,13 +114,13 @@ class HostForm(ModelForm):
helper.layout = Layout(
Div(
Fieldset(
'Identity',
'',
'hostname',
'reverse',
'mac',
),
Fieldset(
'Network',
_('Network'),
'vlan',
'ipv4',
'ipv6',
......@@ -126,7 +128,7 @@ class HostForm(ModelForm):
'external_ipv4',
),
Fieldset(
'Information',
_('Information'),
'description',
'location',
'comment',
......@@ -134,8 +136,8 @@ class HostForm(ModelForm):
),
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.host_list')))
Submit('submit', _('Save')),
LinkButton('back', _('Back'), reverse_lazy('network.host_list')))
)
class Meta:
......@@ -159,8 +161,8 @@ class RecordForm(ModelForm):
)
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.record_list'))
Submit('submit', _("Save")),
LinkButton('back', _("Back"), reverse_lazy('network.record_list'))
)
)
......@@ -173,7 +175,7 @@ class RuleForm(ModelForm):
helper.layout = Layout(
Div(
Fieldset(
'Identity',
'',
'direction',
'description',
'foreign_network',
......@@ -189,7 +191,7 @@ class RuleForm(ModelForm):
'nat_external_ipv4',
),
Fieldset(
'External',
_('External'),
'vlan',
'vlangroup',
'host',
......@@ -198,8 +200,8 @@ class RuleForm(ModelForm):
)
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.rule_list'))
Submit('submit', _("Save")),
LinkButton('back', _("Back"), reverse_lazy('network.rule_list'))
)
)
......@@ -219,8 +221,8 @@ class SwitchPortForm(ModelForm):
)
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back',
Submit('submit', _("Save")),
LinkButton('back', _("Back"),
reverse_lazy('network.switch_port_list'))
)
)
......@@ -234,41 +236,42 @@ class VlanForm(ModelForm):
helper.layout = Layout(
Div(
Fieldset(
'Identity',
'',
'name',
'vid',
'network_type',
'managed',
),
Fieldset(
'IPv4',
_('IPv4'),
'network4',
'snat_to',
'snat_ip',
'dhcp_pool',
),
Fieldset(
'IPv6',
_('IPv6'),
'network6',
'ipv6_template',
'host_ipv6_prefixlen',
),
Fieldset(
'Domain name service',
_('Domain name service'),
'domain',
'reverse_domain',
),
Fieldset(
'Info',
_('Info'),
'description',
'comment',
'owner',
# 'created_at',
# 'modified_at',
),
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy('network.vlan_list'))
Submit('submit', _("Save")),
LinkButton('back', _("Back"), reverse_lazy('network.vlan_list'))
)
)
......@@ -289,8 +292,8 @@ class VlanGroupForm(ModelForm):
)
),
FormActions(
Submit('submit', 'Save'),
LinkButton('back', 'Back', reverse_lazy(
Submit('submit', _("Save")),
LinkButton('back', _("Back"), reverse_lazy(
'network.vlan_group_list'))
)
)
......
......@@ -34,6 +34,7 @@ from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm,
BlacklistItemForm, RuleForm, VlanGroupForm, SwitchPortForm)
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import FormMixin
from django.utils.translation import ugettext_lazy as _
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
......@@ -45,22 +46,11 @@ from dashboard.views import AclUpdateView
from dashboard.forms import AclUserOrGroupAddForm
class SuccessMessageMixin(FormMixin):
"""
Adds a success message on successful form submission.
From django/contrib/messages/views.py@9a85ad89
"""
success_message = ''
def form_valid(self, form):
response = super(SuccessMessageMixin, self).form_valid(form)
success_message = self.get_success_message(form.cleaned_data)
if success_message:
messages.success(self.request, success_message)
return response
def get_success_message(self, cleaned_data):
return self.success_message % cleaned_data
class InitialOwnerMixin(FormMixin):
def get_initial(self):
initial = super(InitialOwnerMixin, self).get_initial()
initial['owner'] = self.request.user
return initial
class IndexView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
......@@ -190,7 +180,7 @@ class DomainDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class DomainCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Domain
template_name = "network/domain-create.html"
form_class = DomainForm
......@@ -274,7 +264,7 @@ class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
class GroupCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Group
template_name = "network/group-create.html"
form_class = GroupForm
......@@ -413,7 +403,7 @@ class HostDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class HostCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Host
template_name = "network/host-create.html"
form_class = HostForm
......@@ -497,7 +487,7 @@ class RecordDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class RecordCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Record
template_name = "network/record-create.html"
form_class = RecordForm
......@@ -505,21 +495,21 @@ class RecordCreate(LoginRequiredMixin, SuperuserRequiredMixin,
success_message = _(u'Successfully created record!')
def get_initial(self):
initial = super(RecordCreate, self).get_initial()
initial['domain'] = self.request.GET.get('domain')
host_pk = self.request.GET.get("host")
try:
host = Host.objects.get(pk=host_pk)
except (Host.DoesNotExist, ValueError):
host = None
initial = {'owner': self.request.user}
if host:
initial.update({
'type': "CNAME",
'host': host,
'address': host.get_fqdn(),
})
else:
initial['domain'] = self.request.GET.get('domain')
return initial
......@@ -569,18 +559,19 @@ class RuleDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class RuleCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Rule
template_name = "network/rule-create.html"
form_class = RuleForm
success_message = _(u'Successfully created rule!')
def get_initial(self):
return {
# 'owner': 1,
initial = super(RuleCreate, self).get_initial()
initial.update({
'host': self.request.GET.get('host'),
'hostgroup': self.request.GET.get('hostgroup')
}
})
return initial
class RuleDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
......@@ -680,7 +671,7 @@ class VlanDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class VlanCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = Vlan
template_name = "network/vlan-create.html"
form_class = VlanForm
......@@ -760,7 +751,7 @@ class VlanGroupDetail(LoginRequiredMixin, SuperuserRequiredMixin,
class VlanGroupCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
SuccessMessageMixin, InitialOwnerMixin, CreateView):
model = VlanGroup
template_name = "network/vlan-group-create.html"
form_class = VlanGroupForm
......
......@@ -367,7 +367,8 @@ class Disk(TimeStampedModel):
disk = cls.__create(user, params)
disk.clean()
disk.save()
logger.debug("Disk created: %s", params)
logger.debug(u"Disk created from: %s",
unicode(params.get("base", "nobase")))
return disk
@classmethod
......
......@@ -24,7 +24,7 @@ from celery.signals import worker_ready
from celery.contrib.abortable import AbortableAsyncResult
from django.core.urlresolvers import reverse
from django.db.models import CharField, ForeignKey
from django.db.models import CharField, ForeignKey, BooleanField
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, ugettext_noop
......@@ -70,6 +70,8 @@ class InstanceActivity(ActivityModel):
help_text=_('Instance this activity works on.'),
verbose_name=_('instance'))
resultant_state = CharField(blank=True, max_length=20, null=True)
interruptible = BooleanField(default=False, help_text=_(
'Other activities can interrupt this one.'))
class Meta:
app_label = 'vm'
......@@ -91,24 +93,30 @@ class InstanceActivity(ActivityModel):
@classmethod
def create(cls, code_suffix, instance, task_uuid=None, user=None,
concurrency_check=True, readable_name=None,
resultant_state=None):
resultant_state=None, interruptible=False):
readable_name = _normalize_readable_name(readable_name, code_suffix)
# Check for concurrent activities
active_activities = instance.activity_log.filter(finished__isnull=True)
if concurrency_check and active_activities.exists():
raise ActivityInProgressError.create(active_activities[0])
activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
for i in active_activities:
if i.interruptible:
i.finish(False, result=ugettext_noop(
"Interrupted by other activity."))
else:
raise ActivityInProgressError.create(i)
activity_code = cls.construct_activity_code(code_suffix)
act = cls(activity_code=activity_code, instance=instance, parent=None,
resultant_state=resultant_state, started=timezone.now(),
readable_name_data=readable_name.to_dict(),
task_uuid=task_uuid, user=user)
task_uuid=task_uuid, user=user, interruptible=interruptible)
act.save()
return act
def create_sub(self, code_suffix, task_uuid=None, concurrency_check=True,
readable_name=None, resultant_state=None):
readable_name=None, resultant_state=None,
interruptible=False):
readable_name = _normalize_readable_name(readable_name, code_suffix)
# Check for concurrent activities
......@@ -119,7 +127,7 @@ class InstanceActivity(ActivityModel):
act = InstanceActivity(
activity_code=join_activity_code(self.activity_code, code_suffix),
instance=self.instance, parent=self,
resultant_state=resultant_state,
resultant_state=resultant_state, interruptible=interruptible,
readable_name_data=readable_name.to_dict(), started=timezone.now(),
task_uuid=task_uuid, user=self.user)
act.save()
......@@ -183,13 +191,14 @@ class InstanceActivity(ActivityModel):
@contextmanager
def sub_activity(self, code_suffix, on_abort=None, on_commit=None,
readable_name=None, task_uuid=None,
concurrency_check=True):
concurrency_check=True, interruptible=False):
"""Create a transactional context for a nested instance activity.
"""
if not readable_name:
warn("Set readable_name", stacklevel=3)
act = self.create_sub(code_suffix, task_uuid, concurrency_check,
readable_name=readable_name)
readable_name=readable_name,
interruptible=interruptible)
return activitycontextimpl(act, on_abort=on_abort, on_commit=on_commit)
def get_operation(self):
......
......@@ -123,6 +123,10 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
'format like "%s".') %
'Ubuntu 12.04 LTS Desktop amd64'))
tags = TaggableManager(blank=True, verbose_name=_("tags"))
has_agent = BooleanField(verbose_name=_('has agent'), default=True,
help_text=_(
'If the machine has agent installed, and '
'the manager should wait for its start.'))
class Meta:
abstract = True
......@@ -424,7 +428,8 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
# prepare parameters
common_fields = ['name', 'description', 'num_cores', 'ram_size',
'max_ram_size', 'arch', 'priority', 'boot_menu',
'raw_data', 'lease', 'access_method', 'system']
'raw_data', 'lease', 'access_method', 'system',
'has_agent']
params = dict(template=template, owner=owner, pw=pwgen())
params.update([(f, getattr(template, f)) for f in common_fields])
params.update(kwargs) # override defaults w/ user supplied values
......
......@@ -299,16 +299,20 @@ class DeployOperation(InstanceOperation):
"deploy network")):
self.instance.deploy_net()
try:
self.instance.renew(parent_activity=activity)
except:
pass
# Resume vm
with activity.sub_activity(
'booting', readable_name=ugettext_noop(
"boot virtual machine")):
self.instance.resume_vm(timeout=timeout)
try:
self.instance.renew(parent_activity=activity)
except:
pass
if self.instance.has_agent:
activity.sub_activity('os_boot', readable_name=ugettext_noop(
"wait operating system loading"), interruptible=True)
register_operation(DeployOperation)
......@@ -425,8 +429,11 @@ class RebootOperation(InstanceOperation):
required_perms = ()
accept_states = ('RUNNING', )
def _operation(self, timeout=5):
def _operation(self, activity, timeout=5):
self.instance.reboot_vm(timeout=timeout)
if self.instance.has_agent:
activity.sub_activity('os_boot', readable_name=ugettext_noop(
"wait operating system loading"), interruptible=True)
register_operation(RebootOperation)
......@@ -499,8 +506,11 @@ class ResetOperation(InstanceOperation):
required_perms = ()
accept_states = ('RUNNING', )
def _operation(self, timeout=5):
def _operation(self, activity, timeout=5):
self.instance.reset_vm(timeout=timeout)
if self.instance.has_agent:
activity.sub_activity('os_boot', readable_name=ugettext_noop(
"wait operating system loading"), interruptible=True)
register_operation(ResetOperation)
......
......@@ -85,16 +85,22 @@ def agent_started(vm, version=None):
from vm.models import Instance, instance_activity, InstanceActivity
instance = Instance.objects.get(id=int(vm.split('-')[-1]))
queue = instance.get_remote_queue_name("agent")
initialized = InstanceActivity.objects.filter(
instance=instance, activity_code='vm.Instance.agent.cleanup').exists()
initialized = instance.activity_log.filter(
activity_code='vm.Instance.agent.cleanup').exists()
with instance_activity(code_suffix='agent',
readable_name=ugettext_noop('agent'),
concurrency_check=False,
instance=instance) as act:
with act.sub_activity('starting',
readable_name=ugettext_noop('starting')):
pass
for i in InstanceActivity.objects.filter(
instance=instance, activity_code__endswith='.os_boot',
finished__isnull=True):
i.finish(True)
if version and version != settings.AGENT_VERSION:
try:
update_agent(instance, act)
......@@ -108,10 +114,9 @@ def agent_started(vm, version=None):
send_init_commands(instance, act)
send_networking_commands(instance, act)
with act.sub_activity(
'start_access_server',
readable_name=ugettext_noop('start access server')
):
with act.sub_activity('start_access_server',
readable_name=ugettext_noop(
'start access server')):
start_access_server.apply_async(queue=queue, args=(vm, ))
......
......@@ -217,6 +217,8 @@ class InstanceActivityTestCase(TestCase):
def test_create_concurrency_check(self):
instance = MagicMock(spec=Instance)
instance.activity_log.filter.return_value.__iter__.return_value = iter(
[MagicMock(spec=InstanceActivity, interruptible=False)])
instance.activity_log.filter.return_value.exists.return_value = True
with self.assertRaises(ActivityInProgressError):
......
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