Commit eae91fdd by Czémán Arnold

dashboard, manager, vm: adds basic auto migration view and fake auto_migrate task

parent 4d7f1820
Pipeline #634 passed with stage
in 0 seconds
...@@ -587,3 +587,6 @@ REQUEST_HOOK_URL = get_env_variable("REQUEST_HOOK_URL", "") ...@@ -587,3 +587,6 @@ REQUEST_HOOK_URL = get_env_variable("REQUEST_HOOK_URL", "")
SSHKEY_EMAIL_ADD_KEY = False SSHKEY_EMAIL_ADD_KEY = False
TWO_FACTOR_ISSUER = get_env_variable("TWO_FACTOR_ISSUER", "CIRCLE") TWO_FACTOR_ISSUER = get_env_variable("TWO_FACTOR_ISSUER", "CIRCLE")
# Default value is every day at midnight
AUTO_MIGRATION_CRONTAB = get_env_variable("AUTO_MIGRATION_CRONTAB", "0 0 * * *")
...@@ -1697,3 +1697,11 @@ class TwoFactorConfirmationForm(forms.Form): ...@@ -1697,3 +1697,11 @@ class TwoFactorConfirmationForm(forms.Form):
totp = pyotp.TOTP(self.user.profile.two_factor_secret) totp = pyotp.TOTP(self.user.profile.two_factor_secret)
if not totp.verify(self.cleaned_data.get('confirmation_code')): if not totp.verify(self.cleaned_data.get('confirmation_code')):
raise ValidationError(_("Invalid confirmation code.")) raise ValidationError(_("Invalid confirmation code."))
class AutoMigrationForm(forms.Form):
minute = forms.CharField()
hour = forms.CharField()
day_of_month = forms.CharField()
month_of_year = forms.CharField()
day_of_week = forms.CharField()
...@@ -1079,6 +1079,10 @@ textarea[name="new_members"] { ...@@ -1079,6 +1079,10 @@ textarea[name="new_members"] {
max-width: 100%; max-width: 100%;
} }
#node-list-auto-migration-body {
padding: 20px;
}
#vm-list-table td.state, #vm-list-table td.state,
#vm-list-table td.memory { #vm-list-table td.memory {
white-space: nowrap; white-space: nowrap;
......
...@@ -3,4 +3,11 @@ $(function() { ...@@ -3,4 +3,11 @@ $(function() {
// find disabled nodes, set danger (red) on the rows // find disabled nodes, set danger (red) on the rows
$('.node-disabled').closest("tr").addClass('danger'); $('.node-disabled').closest("tr").addClass('danger');
}); });
$('#reschedule-now').click(function() {
$.get($(this).attr('href'), function(data){
highlight = data.result === 'ok' ? 'success' : 'danger';
addMessage(data.message, highlight);
});
return false;
});
}); });
...@@ -41,4 +41,23 @@ ...@@ -41,4 +41,23 @@
</div><!-- -col-md-12 --> </div><!-- -col-md-12 -->
</div><!-- .row --> </div><!-- .row -->
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<a id="reschedule-now" class="btn btn-danger pull-right" href="{% url "dashboard.views.reschedule" %}">
<i class="fa fa-magic"></i> {% trans "Reschedule now" %}
</a>
<h3 class="no-margin"><i class="fa fa-truck"></i> {% trans "Virtual machine auto migration" %}</h3>
</div>
<div id="node-list-auto-migration-body">
<h1>Crontab</h1>
<form>
{{ auto_migration_form.as_p }}
</form>
</div>
</div>
</div><!-- -col-md-12 -->
</div><!-- .row -->
{% endblock %} {% endblock %}
...@@ -56,6 +56,7 @@ from .views import ( ...@@ -56,6 +56,7 @@ from .views import (
MessageList, MessageDetail, MessageCreate, MessageDelete, MessageList, MessageDetail, MessageCreate, MessageDelete,
EnableTwoFactorView, DisableTwoFactorView, EnableTwoFactorView, DisableTwoFactorView,
AclUserGroupAutocomplete, AclUserAutocomplete, AclUserGroupAutocomplete, AclUserAutocomplete,
RescheduleView,
) )
from .views.vm import vm_ops, vm_mass_ops from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops from .views.node import node_ops
...@@ -153,6 +154,8 @@ urlpatterns = [ ...@@ -153,6 +154,8 @@ urlpatterns = [
r'(?P<time>[0-9]{1,2}[hdwy])$'), r'(?P<time>[0-9]{1,2}[hdwy])$'),
NodeListGraphView.as_view(), NodeListGraphView.as_view(),
name='dashboard.views.node-list-graph'), name='dashboard.views.node-list-graph'),
url(r'^node/reschedule/$', RescheduleView.as_view(),
name="dashboard.views.reschedule"),
url((r'^template/(?P<pk>\d+)/graph/(?P<metric>[a-z]+)/' url((r'^template/(?P<pk>\d+)/graph/(?P<metric>[a-z]+)/'
r'(?P<time>[0-9]{1,2}[hdwy])$'), r'(?P<time>[0-9]{1,2}[hdwy])$'),
TemplateGraphView.as_view(), TemplateGraphView.as_view(),
......
...@@ -25,7 +25,7 @@ from django.core.exceptions import PermissionDenied ...@@ -25,7 +25,7 @@ from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.db.models import Count from django.db.models import Count
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.http import HttpResponse from django.http import HttpResponse, JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
...@@ -37,11 +37,14 @@ from django_tables2 import SingleTableView ...@@ -37,11 +37,14 @@ from django_tables2 import SingleTableView
from firewall.models import Host from firewall.models import Host
from vm.models import Node, NodeActivity, Trait from vm.models import Node, NodeActivity, Trait
from vm.tasks.vm_tasks import check_queue from vm.tasks.vm_tasks import check_queue
from vm.tasks.local_periodic_tasks import auto_migrate
from ..forms import TraitForm, HostForm, NodeForm from ..forms import TraitForm, HostForm, NodeForm, AutoMigrationForm
from ..tables import NodeListTable from ..tables import NodeListTable
from .util import AjaxOperationMixin, OperationView, GraphMixin, DeleteViewBase from .util import AjaxOperationMixin, OperationView, GraphMixin, DeleteViewBase
from manager.mancelery import crontab_parser
def get_operations(instance, user): def get_operations(instance, user):
ops = [] ops = []
...@@ -190,6 +193,14 @@ class NodeList(LoginRequiredMixin, GraphMixin, SingleTableView): ...@@ -190,6 +193,14 @@ class NodeList(LoginRequiredMixin, GraphMixin, SingleTableView):
table_class = NodeListTable table_class = NodeListTable
table_pagination = False table_pagination = False
def get_crontab(self):
return crontab_parser(settings.AUTO_MIGRATION_CRONTAB)
def get_context_data(self):
context = super(NodeList, self).get_context_data()
context["auto_migration_form"] = AutoMigrationForm(self.get_crontab())
return context
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
if not self.request.user.has_perm('vm.view_statistics'): if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied() raise PermissionDenied()
...@@ -356,3 +367,23 @@ class NodeActivityDetail(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -356,3 +367,23 @@ class NodeActivityDetail(LoginRequiredMixin, SuperuserRequiredMixin,
).order_by('-started').select_related()) ).order_by('-started').select_related())
ctx['icon'] = _get_activity_icon(self.object) ctx['icon'] = _get_activity_icon(self.object)
return ctx return ctx
class RescheduleView(SuperuserRequiredMixin, View):
def get(self, *args, **kwargs):
try:
auto_migrate.apply_async(queue='localhost.man.slow')
except Exception as e:
msg = str(e)
result = 'error'
else:
result = 'ok'
msg = _('Reschedule has started.')
if self.request.is_ajax():
return JsonResponse({'result': result, 'message': msg})
else:
if result == 'ok':
messages.success(self.request, msg)
else:
messages.error(self.request, msg)
return redirect('dashboard.views.node-list')
...@@ -17,12 +17,26 @@ ...@@ -17,12 +17,26 @@
from celery import Celery from celery import Celery
from celery.signals import worker_ready from celery.signals import worker_ready
from celery.schedules import crontab
from datetime import timedelta from datetime import timedelta
from kombu import Queue, Exchange from kombu import Queue, Exchange
from os import getenv from os import getenv
HOSTNAME = "localhost" HOSTNAME = "localhost"
QUEUE_NAME = HOSTNAME + '.man' QUEUE_NAME = HOSTNAME + '.man'
AUTO_MIGRATION_CRONTAB = getenv('AUTO_MIGRATION_CRONTAB', '0 0 * * *')
def crontab_parser(crontab):
fields = crontab.split(' ')
return dict(
minute=fields[0],
hour=fields[1],
day_of_month=fields[2],
month_of_year=fields[3],
day_of_week=fields[4],
)
celery = Celery('manager', celery = Celery('manager',
...@@ -55,6 +69,11 @@ celery.conf.update( ...@@ -55,6 +69,11 @@ celery.conf.update(
'schedule': timedelta(hours=24), 'schedule': timedelta(hours=24),
'options': {'queue': 'localhost.man'} 'options': {'queue': 'localhost.man'}
}, },
'vm.local_periodic_tasks': {
'task': 'vm.tasks.local_periodic_tasks.auto_migrate',
'schedule': crontab(**crontab_parser(AUTO_MIGRATION_CRONTAB)),
'options': {'queue': 'localhost.man.slow'},
},
} }
) )
......
...@@ -76,3 +76,11 @@ def garbage_collector(timeout=15): ...@@ -76,3 +76,11 @@ def garbage_collector(timeout=15):
i.notify_owners_about_expiration() i.notify_owners_about_expiration()
else: else:
logger.debug("Instance %d didn't expire." % i.pk) logger.debug("Instance %d didn't expire." % i.pk)
@celery.task(ignore_result=True)
def auto_migrate(): # Dummy implementation
import time
logger.info("Auto migration has started.")
time.sleep(10)
logger.info("Auto migration has finished.")
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