Skip to content
Browse files

Bring back the ability to update plugins

- This is still rough around the edges
- While we're here, change the layout
- Database info for updated plugin still needs to be updated
  • Loading branch information...
1 parent 1b472d2 commit 8bb372f16598395c0f9824170e99ddb6bf3a9350 @jhixson74 jhixson74 committed Sep 25, 2013
View
5 gui/api/resources.py
@@ -1483,7 +1483,6 @@ def dehydrate(self, bundle):
bundle.data['_edit_url'] = reverse('jail_template_edit', kwargs={
'id': bundle.obj.id
})
-
return bundle
@@ -1641,6 +1640,10 @@ def dehydrate(self, bundle):
'plugins_install_available',
kwargs={'oid': bundle.obj.id},
)
+ bundle.data['_update_url'] = reverse(
+ 'plugin_update',
+ kwargs={'oid': bundle.obj.id},
+ )
return bundle
View
42 gui/freeadmin/static/lib/js/freeadmin.js
@@ -213,6 +213,48 @@ require([
}
+ get_selected_plugin = function() {
+ var plugin = null;
+ var grid = dom.byId("dgrid_available").grid;
+ for (var i in grid.selection) {
+ plugin = grid.row(i).data;
+ break;
+ }
+
+ if (!plugin) {
+ console.log("Something is wrong here, now plugin found!");
+ return null;
+ }
+
+ return plugin;
+ }
+
+ show_plugin_update_button = function(evt, actionName, action) {
+ var plugin = get_selected_plugin();
+ if (!plugin) {
+ console.log("Something is wrong here, no plugin found!");
+ return;
+ }
+
+ xhr.get('/plugins/plugin/installed/' + plugin.id + '/', {
+ sync: true
+ }).then(function(data) {
+ var obj = JSON.parse(data)
+ if (obj.installed > 0) {
+ query(".grid" + actionName).forEach(function(item, idx) {
+ domStyle.set(item, "display", "block");
+ });
+ } else {
+ query(".grid" + actionName).forEach(function(item, idx) {
+ domStyle.set(item, "display", "none");
+ });
+ }
+ if (action.on_select_after !== undefined) {
+ action.on_select_after(evt, actionName, action);
+ }
+ });
+ }
+
ask_service = function(srv) {
dialog = new Dialog({
View
6 gui/middleware/notifier.py
@@ -2655,6 +2655,8 @@ def update_pbi(self, plugin=None):
from freenasUI.jails.models import JailsConfiguration
ret = False
+ open('/tmp/.plugin_upload_update', 'w+').close()
+
if not plugin:
raise MiddlewareError("plugin is NULL")
@@ -2762,8 +2764,8 @@ def update_pbi(self, plugin=None):
plugin.save()
ret = True
- except Exception:
- ret = False
+ except Exception as e:
+ raise MiddlewareError(_(e))
return ret
View
13 gui/plugins/admin.py
@@ -69,6 +69,19 @@ def get_actions(self):
}
}""",
}
+ actions["Update"] = {
+ 'button_name': _("Update"),
+ 'on_select': """function(evt, actionName, action) {
+ show_plugin_update_button(evt, actionName, action);
+ }""",
+ 'on_click': """function() {
+ var mybtn = this;
+ for (var i in grid.selection) {
+ var data = grid.row(i).data;
+ editObject('Update plugin', data._update_url, [mybtn,]);
+ }
+ }""",
+ }
return actions
View
17 gui/plugins/forms.py
@@ -27,7 +27,7 @@
import logging
import shutil
-from django.forms import FileField
+from django.forms import FileField, MultipleChoiceField
from django.utils.translation import ugettext_lazy as _
from dojango import forms
@@ -39,6 +39,10 @@
from freenasUI.middleware.notifier import notifier
from freenasUI.network.models import Alias, Interfaces
from freenasUI.plugins import models
+from freenasUI.plugins.plugin import (
+ get_installed_plugins_by_remote_oid,
+ get_installed_plugins_count_by_remote_oid
+)
from freenasUI.system.forms import (
clean_path_execbit
)
@@ -176,6 +180,17 @@ def done(self, *args, **kwargs):
self.plugin.plugin_name)
+class PluginUpdateForm(Form):
+ jails = MultipleChoiceField(required=True)
+
+ def __init__(self, *args, **kwargs):
+ oid = kwargs.pop('oid')
+ super(PluginUpdateForm, self).__init__(*args, **kwargs)
+
+ plugins = get_installed_plugins_by_remote_oid(oid)
+ self.fields['jails'].choices = [(p.plugin_jail, p.plugin_jail) for p in plugins]
+
+
class ConfigurationForm(ModelForm):
class Meta:
View
110 gui/plugins/plugin.py
@@ -2,6 +2,7 @@
import logging
import os
import platform
+import re
import urllib2
from django.utils.translation import ugettext as _
@@ -191,4 +192,113 @@ def _get_remote_item(self, p):
def all(self):
return self.get_local()
+
+def get_available_plugins():
+ from freenasUI.plugins import models, availablePlugins
+
+ conf = models.Configuration.objects.latest('id')
+ if conf and conf.collectionurl:
+ url = conf.collectionurl
+ else:
+ url = models.PLUGINS_INDEX
+
+ return availablePlugins.get_remote(url=url, cache=True)
+
+
+def get_remote_plugin_by_oid(oid):
+ from freenasUI.plugins import models, availablePlugins
+
+ plugin = None
+ for p in get_available_plugins():
+ if p.id == int(oid):
+ plugin = p
+ break
+
+ return plugin
+
+
+def get_remote_plugin_pbiname_by_oid(oid):
+ plugin = get_remote_plugin_by_oid(oid)
+ if not plugin:
+ return None
+
+ pbiname = None
+ for url in plugin.urls:
+ parts = url.split('/')
+ nparts = len(parts)
+ pbiname = parts[0]
+ if nparts > 0:
+ pbiname = parts[nparts - 1]
+ break
+
+ if pbiname:
+ pbiname = re.sub('\.pbi$', '', pbiname, flags=re.I)
+
+ return pbiname
+
+
+def get_remote_plugin_by_installed_oid(oid):
+ from freenasUI.plugins import models
+
+ rplugin = None
+ iplugin = models.Plugins.objects.filter(id=oid)
+ if iplugin:
+ iplugin = iplugin[0]
+
+ for rp in get_available_plugins():
+ pbiname = get_remote_plugin_pbiname_by_oid(rp.id)
+ if not pbiname:
+ continue
+
+ if iplugin.plugin_arch.lower() == rp.arch.lower() and \
+ iplugin.plugin_pbiname.lower() == pbiname.lower():
+ rplugin = rp
+ break
+
+ return rplugin
+
+
+def get_installed_plugin_update_status(oid):
+ from freenasUI.plugins import models
+ status = False
+
+ iplugin = models.Plugins.objects.filter(id=oid)
+ if iplugin:
+ iplugin = iplugin[0]
+
+ rplugin = get_remote_plugin_by_installed_oid(oid)
+ if rplugin and iplugin:
+ if str(iplugin.plugin_version).lower() != str(rplugin.version).lower():
+ status = True
+
+ return status
+
+
+def get_installed_plugins_by_remote_oid(oid):
+ from freenasUI.plugins import models, availablePlugins
+ iplugins = []
+
+ pbiname = get_remote_plugin_pbiname_by_oid(oid)
+ if not pbiname:
+ return iplugins
+
+ if pbiname:
+ plugins = models.Plugins.objects.filter(
+ plugin_arch=plugin.arch,
+ plugin_pbiname=pbiname
+ )
+
+ return plugins
+
+
+def get_installed_plugins_count_by_remote_oid(oid):
+ icount = 0
+
+ iplugins = get_installed_plugins_by_remote_oid(oid)
+ if iplugins:
+ icount = len(iplugins)
+
+ return icount
+
+
availablePlugins = Available()
View
4 gui/plugins/urls.py
@@ -40,7 +40,9 @@
url(r'^plugin/install/progress/$', 'install_progress', name="plugins_install_progress"),
url(r'^plugin/edit/(?P<plugin_id>\d+)/$', 'plugin_edit', name="plugin_edit"),
url(r'^plugin/info/(?P<plugin_id>\d+)/$', 'plugin_info', name="plugin_info"),
- url(r'^plugin/update/(?P<plugin_id>\d+)/$', 'plugin_update', name="plugin_update"),
+ url(r'^plugin/installed/(?P<oid>[0-9a-f]{1,64})/$', 'plugin_installed', name="plugin_installed"),
+ url(r'^plugin/update/(?P<oid>[0-9a-f]{1,64})/$', 'plugin_update', name="plugin_update"),
+ url(r'^plugin/update/progress/$', 'update_progress', name="plugins_update_progress"),
url(r'^json-rpc/v1/', jsonrpc_site.dispatch, name="plugins_jsonrpc_v1"),
url(r'^(?P<name>[^/]+)/(?P<oid>\d+)/(?P<path>.+)$', 'plugin_fcgi_client', name="plugin_fcgi_client"),
)
View
64 gui/plugins/utils/__init__.py
@@ -79,7 +79,69 @@ def get_plugin_status(args):
))
]
#TODO: Increase timeout based on number of plugins
- response = opener.open(url, None, 5).read()
+ response = opener.open(url, None).read()
+ json = simplejson.loads(response)
+ except Exception, e:
+ log.warn(_("Couldn't retrieve %(url)s: %(error)s") % {
+ 'url': url,
+ 'error': e,
+ })
+ return plugin, json, jail_status
+
+
+def get_plugin_start(args):
+
+ plugin, host, request = args
+ url = "%s/plugins/%s/%d/_s/start" % (
+ host,
+ plugin.plugin_name,
+ plugin.id)
+ json = None
+
+ jail_status = notifier().pluginjail_running(pjail=plugin.plugin_jail)
+ if not jail_status:
+ return plugin, json, jail_status
+
+ try:
+ opener = urllib2.build_opener()
+ opener.addheaders = [
+ ('Cookie', 'sessionid=%s' % (
+ request.COOKIES.get("sessionid", ''),
+ ))
+ ]
+ #TODO: Increase timeout based on number of plugins
+ response = opener.open(url, None).read()
+ json = simplejson.loads(response)
+ except Exception, e:
+ log.warn(_("Couldn't retrieve %(url)s: %(error)s") % {
+ 'url': url,
+ 'error': e,
+ })
+ return plugin, json, jail_status
+
+
+def get_plugin_stop(args):
+
+ plugin, host, request = args
+ url = "%s/plugins/%s/%d/_s/stop" % (
+ host,
+ plugin.plugin_name,
+ plugin.id)
+ json = None
+
+ jail_status = notifier().pluginjail_running(pjail=plugin.plugin_jail)
+ if not jail_status:
+ return plugin, json, jail_status
+
+ try:
+ opener = urllib2.build_opener()
+ opener.addheaders = [
+ ('Cookie', 'sessionid=%s' % (
+ request.COOKIES.get("sessionid", ''),
+ ))
+ ]
+ #TODO: Increase timeout based on number of plugins
+ response = opener.open(url, None).read()
json = simplejson.loads(response)
except Exception, e:
log.warn(_("Couldn't retrieve %(url)s: %(error)s") % {
View
133 gui/plugins/views.py
@@ -25,9 +25,11 @@
#
#####################################################################
from collections import namedtuple
+
import json
import logging
import os
+import re
from django.shortcuts import render
from django.http import Http404, HttpResponse
@@ -45,8 +47,17 @@
from freenasUI.middleware.exceptions import MiddlewareError
from freenasUI.middleware.notifier import notifier
from freenasUI.plugins import models, forms, availablePlugins
-from freenasUI.plugins.plugin import PROGRESS_FILE
-from freenasUI.plugins.utils import get_base_url, get_plugin_status
+from freenasUI.plugins.plugin import (
+ PROGRESS_FILE,
+ get_installed_plugin_update_status,
+ get_remote_plugin_by_installed_oid
+)
+from freenasUI.plugins.utils import (
+ get_base_url,
+ get_plugin_status,
+ get_plugin_start,
+ get_plugin_stop
+)
from freenasUI.plugins.utils.fcgi_client import FCGIApp
import freenasUI.plugins.api_calls
@@ -100,6 +111,8 @@ def plugins(request):
jail_status=jail_status,
)
+ plugin.update_available = get_installed_plugin_update_status(plugin.id)
+
return render(request, "plugins/plugins.html", {
'plugins': plugins,
})
@@ -132,37 +145,71 @@ def plugin_info(request, plugin_id):
})
-def plugin_update(request, plugin_id):
- plugin_id = int(plugin_id)
- plugin = models.Plugins.objects.get(id=plugin_id)
+def plugin_installed(request, oid):
+ data = {
+ 'installed': get_installed_plugins_count_by_remote_oid(oid)
+ }
- plugin_upload_path = notifier().get_plugin_upload_path()
- notifier().change_upload_location(plugin_upload_path)
+ return HttpResponse(json.dumps(data), mimetype='application/json')
+
+
+def plugin_update(request, oid):
+ host = get_base_url(request)
+
+ jc = JailsConfiguration.objects.order_by("-id")[0]
+ logfile = '%s/warden.log' % jc.jc_path
+ if os.path.exists(logfile):
+ os.unlink(logfile)
+ if os.path.exists(WARDEN_EXTRACT_STATUS_FILE):
+ os.unlink(WARDEN_EXTRACT_STATUS_FILE)
+ if os.path.exists("/tmp/.plugin_upload_install"):
+ os.unlink("/tmp/.plugin_upload_install")
+ if os.path.exists("/tmp/.jailcreate"):
+ os.unlink("/tmp/.jailcreate")
+ if os.path.exists(PROGRESS_FILE):
+ os.unlink(PROGRESS_FILE)
+
+ iplugin = models.Plugins.objects.filter(id=oid)
+ if not iplugin:
+ raise MiddlewareError(_("Plugin not installed"))
+ iplugin = iplugin[0]
+
+ rplugin = get_remote_plugin_by_installed_oid(oid)
+ if not rplugin:
+ raise MiddlewareError(_("Invalid plugin"))
+
+ (p, js, jail_status) = get_plugin_status([iplugin, host, request])
+ if js and js['status'] == 'RUNNING':
+ (p, js, jail_status) = get_plugin_stop([iplugin, host, request])
if request.method == "POST":
- form = forms.PBIUpdateForm(request.POST, request.FILES, plugin=plugin)
- if form.is_valid():
- form.done()
- return JsonResp(
- request,
- message=_('Plugin successfully updated'),
- events=['reloadHttpd()'],
+ plugin_upload_path = notifier().get_plugin_upload_path()
+ notifier().change_upload_location(plugin_upload_path)
+
+ if not rplugin.download("/var/tmp/firmware/pbifile.pbi"):
+ raise MiddlewareError(_("Failed to download plugin"))
+
+ jail = Jails.objects.filter(jail_host=iplugin.plugin_jail)
+ if not jail:
+ raise MiddlewareError(_("Jail does not exist"))
+
+ if notifier().update_pbi(plugin=iplugin):
+ notifier()._restart_plugins(
+ iplugin.plugin_jail,
+ iplugin.plugin_name,
)
+
else:
- resp = render(request, "plugins/plugin_update.html", {
- 'form': form,
- })
- resp.content = (
- "<html><body><textarea>"
- + resp.content +
- "</textarea></boby></html>"
- )
- return resp
- else:
- form = forms.PBIUpdateForm(plugin=plugin)
+ raise MiddlewareError(_("Failed to update plugin"))
+
+ return JsonResp(
+ request,
+ message=_("Plugin successfully updated"),
+ events=['reloadHttpd()'],
+ )
return render(request, "plugins/plugin_update.html", {
- 'form': form,
+ 'plugin': rplugin,
})
@@ -181,16 +228,8 @@ def install_available(request, oid):
'error': e.value,
})
- jc = JailsConfiguration.objects.order_by("-id")[0]
- logfile = '%s/warden.log' % jc.jc_path
- if os.path.exists(logfile):
- os.unlink(logfile)
- if os.path.exists(WARDEN_EXTRACT_STATUS_FILE):
- os.unlink(WARDEN_EXTRACT_STATUS_FILE)
- if os.path.exists("/tmp/.plugin_upload_install"):
- os.unlink("/tmp/.plugin_upload_install")
- if os.path.exists("/tmp/.jailcreate"):
- os.unlink("/tmp/.jailcreate")
+ if os.path.exists("/tmp/.plugin_upload_update"):
+ os.unlink("/tmp/.plugin_upload_update")
if os.path.exists(PROGRESS_FILE):
os.unlink(PROGRESS_FILE)
@@ -244,7 +283,6 @@ def install_progress(request):
logfile = '%s/warden.log' % jc.jc_path
data = {}
if os.path.exists(PROGRESS_FILE):
- log.debug("XXX: step 1: %s", PROGRESS_FILE)
data = {'step': 1}
with open(PROGRESS_FILE, 'r') as f:
try:
@@ -293,6 +331,27 @@ def install_progress(request):
return HttpResponse(content, mimetype='application/json')
+def update_progress(request):
+ jc = JailsConfiguration.objects.order_by("-id")[0]
+ logfile = '%s/warden.log' % jc.jc_path
+ data = {}
+
+ if os.path.exists(PROGRESS_FILE):
+ data = {'step': 1}
+ with open(PROGRESS_FILE, 'r') as f:
+ try:
+ current = int(f.readlines()[-1].strip())
+ except:
+ pass
+ data['percent'] = current
+
+ if os.path.exists("/tmp/.plugin_upload_update"):
+ data = {'step': 2}
+
+ content = json.dumps(data)
+ return HttpResponse(content, mimetype='application/json')
+
+
def upload(request, jail_id=-1):
#FIXME: duplicated code with available_install
View
2 gui/templates/plugins/available_datagrid.html
@@ -1,8 +1,10 @@
{% extends "freeadmin/generic_model_datagrid.html" %}
{% block actions_static_pane %}
+{% comment %}
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'top'" style="background-color: #eee; border-bottom: 1px solid #ccc; height: 21px;">
<div style="padding: 4px;">{% trans "Available" %}</div>
</div>
+{% endcomment %}
{{ block.super }}
{% endblock %}
{% block actions_static_buttons %}
View
27 gui/templates/plugins/index.html
@@ -1,16 +1,25 @@
<div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="nested:true, doLayout:true" id="tab_pluginssettings">
-<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title: '{% trans "Plugins"|escapejs %}'
-{% if focus == "plugins.Plugins" %}, selected: true{% endif %},
-doLayout: true,
-href: '{% url "plugins_plugins" %}',
-refreshOnShow: true" class="objrefresh" tab="plugins.Plugins" id="tab_Plugins">
+<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title: '{% trans "Available"|escapejs %}'
+ {% if focus == "plugins.Available" %}, selected: true{% endif %},
+ doLayout: true,
+ href: '{% url "freeadmin_plugins_available_datagrid" %}',
+ refreshOnShow: true" class="objrefresh" tab="plugins.Available" id="tab_Available">
</div>
+
+<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title: '{% trans "Installed"|escapejs %}'
+ {% if focus == "plugins.Installed" %}, selected: true{% endif %},
+ doLayout: true,
+ href: '{% url "plugins_plugins" %}',
+ refreshOnShow: true" class="objrefresh" tab="plugins.Installed" id="tab_Installed">
+</div>
+
+
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="title: '{% trans "Configuration"|escapejs %}'
-{% if focus == "plugins.Configuration" %}, selected: true{% endif %},
-doLayout: false,
-href: '{% url "freeadmin_plugins_configuration_edit" oid=conf.id %}?inline=true',
-refreshOnShow: true" class="objrefresh" tab="plugins.Configuration" id="tab_Configuration">
+ {% if focus == "plugins.Configuration" %}, selected: true{% endif %},
+ doLayout: false,
+ href: '{% url "freeadmin_plugins_configuration_edit" oid=conf.id %}?inline=true',
+ refreshOnShow: true" class="objrefresh" tab="plugins.Configuration" id="tab_Configuration">
</div>
</div>
View
17 gui/templates/plugins/plugin_update.html
@@ -1,10 +1,19 @@
{% extends "freeadmin/generic_form.html" %}
+{% block form %}
+{% blocktrans with plugin=plugin.name %}Are you sure you want to update "{{ plugin }}" plugin?{% endblocktrans %}
+{% endblock %}
{% block onSubmit %}
doSubmit({
form: this,
event: e,
- url: '{{ request.path }}',
- progressbar: true
- });
+ url: '{% block formpost %}{{ request.path }}{% endblock %}',
+ progressbar: {
+ steps: [
+ {"label": "Downloading plugin"},
+ {"label": "Updating plugin"},
+ ],
+ poolUrl: '{% url "plugins_update_progress" %}',
+ fileUpload: false
+ }
+});
{% endblock %}
-{% block oklabel %}{% trans "Upload" %}{% endblock %}
View
16 gui/templates/plugins/plugins.html
@@ -1,3 +1,5 @@
+
+{% comment %}
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="gutters: true, splitter:true">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top', href: '{% url "freeadmin_plugins_available_datagrid" %}'" style="height: 50%">
@@ -8,6 +10,7 @@
<div style="background-color: #eee; padding: 3px; border-bottom: 1px solid #ccc;">
{% trans "Installed" %}
</div>
+{% endcomment %}
<table class="grid">
<thead class="header">
@@ -43,16 +46,15 @@
<img src="{{ STATIC_URL }}images/ui/buttons/{% if p.service.status == "RUNNING" %}on{% else %}off{% endif %}.png" onClick="togglePluginService(this, '{{ p.plugin_name|escapejs }}', {{ p.id|escapejs }})" status="{% if p.service.status == "RUNNING" %}on{% else %}off{% endif %}" style="cursor: pointer;">
</td>
<td>
- {% if p.service.jail_status %}
- {% comment %}
- <button data-dojo-type="dijit.form.Button" type="button">
+ {% if p.update_available %}
+ <button id="btn_Plugins_{{ p.id }}_Update" data-dojo-type="dijit.form.Button" type="button" >
{% trans "Update" %}
- <script id="btn_Plugins_{{ p.id }}_Update" type="dojo/method" data-dojo-event="onClick" data-dojo-args="evt">
- editObject('{% trans "Update plugin"|force_escape|force_escape %}', '{% url "plugin_update" plugin_id=p.id %}', [this,]);
+ <script type="dojo/method" data-dojo-event="onClick" data-dojo-args="evt">
+ editObject('{% trans "Update plugin"|force_escape|force_escape %}', '{% url "plugin_update" oid=p.id %}', [this,]);
</script>
</button>
- {% endcomment %}
- {% endif %}
+ {% endif %}
+
<button id="btn_Plugins_{{ p.id }}_Delete" data-dojo-type="dijit.form.Button" type="button">
{% trans "Delete" %}
<script type="dojo/method" data-dojo-event="onClick" data-dojo-args="evt">
View
8 gui/templates/plugins/plugins_pick.html
@@ -0,0 +1,8 @@
+{% extends "freeadmin/generic_form.html" %}
+{% block onSubmit %}
+doSubmit({
+ form: this,
+ event: e,
+ url: '{% url "plugin_update" oid=oid %}'
+});
+{% endblock %}
View
2 nanobsd/Files/etc/rc.d/ix-plugins
@@ -152,7 +152,7 @@ plugins_stop()
${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "${sql}" | \
while read name jail path enabled
do
- if [ "${enabled}" != "0" ]
+ if [ "${enabled}" != "1" ]
then
continue
fi

0 comments on commit 8bb372f

Please sign in to comment.
Something went wrong with that request. Please try again.