@@ -16,7 +16,7 @@ from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDeta
ClassicRecipeSearchView, ClassicRecipeDetailView, ClassicRecipeStatsView, LayerUpdateDetailView, UpdateListView, \
UpdateDetailView, StatsView, publish_view, LayerCheckListView, BBClassCheckListView, TaskStatusView, \
ComparisonRecipeSelectView, ComparisonRecipeSelectDetailView, task_log_view, task_stop_view, email_test_view, \
- BranchCompareView, RecipeDependenciesView
+ BranchCompareView, RecipeDependenciesView, update_layer_view
from layerindex.models import LayerItem, Recipe, RecipeChangeset
from rest_framework import routers
from . import restviews
@@ -8,7 +8,7 @@
from django.views.defaults import page_not_found
from django.urls import include, re_path, reverse_lazy
-from layerindex.views import LayerListView, RecipeSearchView, MachineSearchView, DistroSearchView, ClassSearchView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, RedirectParamsView, DuplicatesView, LayerUpdateDetailView, layer_export_recipes_csv_view, comparison_update_view
+from layerindex.views import LayerListView, RecipeSearchView, MachineSearchView, DistroSearchView, ClassSearchView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, RedirectParamsView, DuplicatesView, LayerUpdateDetailView, layer_export_recipes_csv_view, comparison_update_view, update_layer_view
urlpatterns = [
re_path(r'^$',
@@ -41,6 +41,7 @@ urlpatterns = [
template_name='layerindex/classes.html'),
name='class_search'),
re_path(r'^edit/(?P<slug>[-\w]+)/$', edit_layer_view, {'template_name': 'layerindex/editlayer.html'}, name="edit_layer"),
+ re_path(r'^update/(?P<slug>[-\w]+)/$', update_layer_view, {'template_name': 'layerindex/updatelayer.html'}, name="update_layer"),
re_path(r'^duplicates/$',
DuplicatesView.as_view(
template_name='layerindex/duplicates.html'),
@@ -121,6 +121,41 @@ def delete_layer_view(request, template_name, slug):
'cancel_url': layeritem.get_absolute_url()
})
+def update_layer_view(request, template_name, branch='master', slug=None):
+ if not (request.user.is_authenticated and request.user.is_staff):
+ raise PermissionDenied
+ return_url = None
+ branchobj = Branch.objects.filter(name=branch)[:1].get()
+ if slug:
+ # Existing Layer Update
+ layeritem = get_object_or_404(LayerItem, name=slug)
+ layerbranch = get_object_or_404(LayerBranch, layer=layeritem, branch=branchobj)
+ returnto = request.GET.get('returnto', 'layer_item')
+ if returnto:
+ if returnto == 'layer_review':
+ return_url = reverse_lazy(returnto, args=(layeritem.name,))
+ else:
+ return_url = reverse_lazy(returnto, args=(branch, layeritem.name))
+ else:
+ # Pre-Publish Layer Update attempt
+ layeritem = LayerItem()
+ layerbranch = LayerBranch(layer=layeritem, branch=branchobj)
+
+ from celery import uuid
+
+ cmd = 'layerindex/update.py --layer %s --branch %s;' % (layeritem.name, branch)
+
+ task_id = uuid()
+ # Create this here first, because inside the task we don't have all of the required info
+ update = Update(task_id=task_id)
+ update.started = datetime.now()
+ update.triggered_by = request.user
+ update.save()
+
+ res = tasks.run_update_command.apply_async((branch, cmd), task_id=task_id)
+
+ return HttpResponseRedirect(reverse_lazy('task_status', kwargs={'task_id': task_id}))
+
def edit_layer_view(request, template_name, branch='master', slug=None):
return_url = None
branchobj = Branch.objects.filter(name=branch)[:1].get()
@@ -38,6 +38,9 @@
{% endif %}
{% if user.is_authenticated %}
<span class="pull-right">
+ {% if user.is_staff %}
+ <a href="{% url 'update_layer' url_branch layeritem.name %}" class="btn btn-info">Update layer</a>
+ {% endif %}
{% if perms.layerindex.publish_layer or useredit %}
<a href="{% url 'edit_layer' url_branch layeritem.name %}" class="btn btn-default">Edit layer</a>
{% if layeritem.layernote_set.count == 0 %}
@@ -47,6 +47,9 @@
<a href="{% url 'delete_layer' layeritem.name %}" class="btn btn-warning">Delete layer</a>
<a href="{% url 'publish' layeritem.name %}" class="btn btn-primary">Publish layer</a>
{% endif %}
+ {% if user.is_staff %}
+ <a href="{% url 'update_layer' 'master' layeritem.name %}" class="btn btn-info">Update layer</a>
+ {% endif %}
</span>
{% endif %}
</h1>
new file mode 100644
@@ -0,0 +1,119 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load extrafilters %}
+
+{% comment %}
+
+ layerindex-web - update page
+
+ Copyright (C) 2016, 2018 Intel Corporation
+ Licensed under the MIT license, see COPYING.MIT for details
+
+{% endcomment %}
+
+<!--
+{% block title_append %} - {{ update.started }} {% endblock %}
+-->
+
+{% block content %}
+{% autoescape on %}
+
+ <ul class="breadcrumb">
+ <li><a href="{% url 'update_list' %}">Updates</a></li>
+ <li class="active">{{ update.started }}</li>
+ </ul>
+
+
+<h2>{{ update.started }} {% if update.reload %}(reload){% endif %}
+<span id="status-label" class="label {% if update.finished %}{% if update.retcode %}label-danger{% else %}label-success{% endif %}{% endif %} pull-right">{% if update.finished %}{% if update.retcode < 0 %}TERMINATED ({{ update.retcode }}){% elif update.retcode %}FAILED{% endif %}{% endif %}</span>
+</h2>
+
+{% if update.log %}
+ <pre>{{ update.log }}</pre>
+{% endif %}
+
+{% for layerupdate in layerupdates %}
+ <table class="table table-bordered">
+ <thead>
+ {% with layerbranch_exists=layerupdate.layerbranch_exists %}
+ <tr><td{% if layerupdate.errors > 0 or layerupdate.retcode != 0 %} class="error"{% elif layerupdate.warnings %} class="warning"{% endif %}>
+ {% if layerbranch_exists %}<a href="{% url 'layer_item' layerupdate.branch.name layerupdate.layer.name %}">{% endif %}<strong>{{ layerupdate.layer.name }} {{ layerupdate.branch.name }}</strong>{% if layerbranch_exists %}</a>{% endif %}
+ {% if layerupdate.vcs_before_rev != layerupdate.vcs_after_rev %}
+ <span class="pull-right">
+ {% if layerbranch_exists %}
+ {% with before_url=layerupdate.vcs_before_commit_url after_url=layerupdate.vcs_after_commit_url %}
+ {% if before_url %}<a href="{{ before_url }}">{% endif %}{{ layerupdate.vcs_before_rev|truncatesimple:10 }}{% if before_url %}</a>{% endif %} → {% if after_url %}<a href="{{ after_url }}">{% endif %}{{ layerupdate.vcs_after_rev|truncatesimple:10 }}{% if after_url %}</a>{% endif %}
+ {% endwith %}
+ {% else %}
+ {{ layerupdate.vcs_before_rev|truncatesimple:10 }} → {{ layerupdate.vcs_after_rev|truncatesimple:10 }}
+ {% endif %}
+ </span>
+ {% endif %}
+ </td></tr>
+ {% endwith %}
+ </thead>
+ <tbody>
+ {% if layerupdate.log %}
+ <tr><td class="td-pre">
+ <pre class="pre-scrollable pre-plain">{{ layerupdate.log }}</pre>
+ </td></tr>
+ {% endif %}
+ </tbody>
+ </table>
+{% endfor %}
+
+{% if not update.log and not layerupdates %}
+ <p>No messages or layer updates</p>
+{% endif %}
+
+{% if update.comparisonrecipeupdate_set.exists %}
+<h3>Updated comparison recipes</h3>
+<ul>
+{% for recipeupdate in update.comparisonrecipeupdate_set.all %}
+ <li><a href="{% url 'comparison_recipe' recipeupdate.recipe.id %}">{{ recipeupdate.recipe.pn }}</a> {% if recipeupdate.meta_updated and recipeupdate.link_updated %}(meta, link){% elif recipeupdate.link_updated %}(link){% elif recipeupdate.meta_updated %}(meta){% endif %}</li>
+{% endfor %}
+</ul>
+{% endif %}
+
+{% endautoescape %}
+
+{% endblock %}
+{% if updates.count > 0 %}
+<div class="tab-pane" id="updates">
+ <nav class="navbar navbar-default">
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <a class="navbar-brand">{{ layeritem.name }} updates</a>
+ </div>
+ </div>
+ </nav>
+
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th>Date/time</th>
+ <th>Errors</th>
+ <th>Warnings</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for update in updates %}
+ <tr>
+ <td>
+ {% if update.log %}
+ <a href="{% url 'layerupdate' update.id %}">{{ update.started }}{% if update.update.reload %} (reload){% endif%}</a>
+ {% else %}
+ <span class="text-muted">{{ update.started }}{% if update.update.reload %} (reload){% endif%}</span>
+ {% endif %}
+ </td>
+ <td>{% if update.errors %}<span class="badge badge-important">{{ update.errors }}</span>{% endif %}</td>
+ <td>{% if update.warnings %}<span class="badge badge-warning">{{ update.warnings }}</span>{% endif %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</div>
+{% endif %}
+
+{% block scripts %}
+{% endblock %}
* Add an "Update Layer" button to the layer detail view. - This allows a user that is a member of is_staff to trigger an update of the current layer (for the current branch) * Add an "Update Layer" button to the reviewdetail view - This allows a user that is a member of is_staff and has publish_layer permissions to trigger an update attempt of the layer under review (even in the un-published state) * The update is run as a task with Celery NOTE: You must have the RABBITMQ_ and DATABASE_ credentials set correctly in the docker/settings.py file or set via environment variables or you will get authentication errors talking to layersdb or layersrabbit containers. [YOCTO #12484] layerindex/views.py: add update_layer_view layerindex/urls.py: add update_layer_view layerindex/urls_branch.py: add update_layer_view templates/layerindex/reviewdetail.html: add Update Layer button templates/layerindex/detail.html: add Update Layer button templates/layerindex: add updatelayer.html TODO: While the update is happening, the AJAX rendering of the update.log is showing the b'' characters and not adding any new lines. If you go back to the same task view afterwards, the log is rendered as expected. TODO: After the update is completed, it would be nice to have a button to return you to the page from where you called the "Update Layer". Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- layerindex/urls.py | 2 +- layerindex/urls_branch.py | 3 +- layerindex/views.py | 35 ++++++++ templates/layerindex/detail.html | 3 + templates/layerindex/reviewdetail.html | 3 + templates/layerindex/updatelayer.html | 119 +++++++++++++++++++++++++ 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 templates/layerindex/updatelayer.html