Verified Commit ebb71412 authored by Štěpán Henek's avatar Štěpán Henek 🐻

netboot plugin logic integrated

parent 7e2f1e34
......@@ -33,12 +33,11 @@ from . import handlers
class CommonPage(ConfigPageMixin):
def _prepare_render_args(self, args):
args['PLUGIN_NAME'] = SubordinatesPlugin.PLUGIN_NAME
args['PLUGIN_STYLES'] = SubordinatesPlugin.PLUGIN_STYLES
args['PLUGIN_STATIC_SCRIPTS'] = SubordinatesPlugin.PLUGIN_STATIC_SCRIPTS
args['PLUGIN_DYNAMIC_SCRIPTS'] = SubordinatesPlugin.PLUGIN_DYNAMIC_SCRIPTS
args["PLUGIN_NAME"] = SubordinatesPlugin.PLUGIN_NAME
args["PLUGIN_STYLES"] = SubordinatesPlugin.PLUGIN_STYLES
args["PLUGIN_STATIC_SCRIPTS"] = SubordinatesPlugin.PLUGIN_STATIC_SCRIPTS
args["PLUGIN_DYNAMIC_SCRIPTS"] = SubordinatesPlugin.PLUGIN_DYNAMIC_SCRIPTS
def render(self, **kwargs):
self._prepare_render_args(kwargs)
......@@ -63,17 +62,20 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
super(SubordinatesSetupPage, self).save(no_messages=True, *args, **kwargs)
data = self.form.callback_results
if data["result"]:
messages.success(_(
"Token was successfully added and client '%(controller_id)s' "
"should be visible in a moment."
) % dict(controller_id=data["controller_id"]))
messages.success(
_(
"Token was successfully added and client '%(controller_id)s' "
"should be visible in a moment."
)
% dict(controller_id=data["controller_id"])
)
else:
messages.error(_("Failed to add token."))
return data["result"]
def _check_and_get_controller_id(self):
if bottle.request.method != 'POST':
if bottle.request.method != "POST":
messages.error(_("Wrong HTTP method."))
bottle.redirect(reverse("config_page", page_name="remote"))
......@@ -92,15 +94,14 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
def _ajax_delete(self):
controller_id = self._check_and_get_controller_id()
res = current_state.backend.perform(
"subordinates", "del", {"controller_id": controller_id})
res = current_state.backend.perform("subordinates", "del", {"controller_id": controller_id})
if res["result"]:
return bottle.template(
"subordinates/_subordinates_message.html.j2",
message={
"classes": ['success'],
"classes": ["success"],
"text": _("Subordinate '%(controller_id)s' was successfully deleted.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
},
template_adapter=bottle.Jinja2Template,
)
......@@ -108,44 +109,43 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
return bottle.template(
"subordinates/_subordinates_message.html.j2",
message={
"classes": ['error'],
"classes": ["error"],
"text": _("Failed to delete subordinate '%(controller_id)s'.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
},
template_adapter=bottle.Jinja2Template,
)
def _ajax_set_enabled(self, enabled):
controller_id = self._check_and_get_controller_id()
res = current_state.backend.perform("subordinates", "set_enabled", {
"controller_id": controller_id,
"enabled": enabled,
})
res = current_state.backend.perform(
"subordinates", "set_enabled", {"controller_id": controller_id, "enabled": enabled}
)
if res["result"]:
if enabled:
message = {
"classes": ['success'],
"classes": ["success"],
"text": _("Subordinate '%(controller_id)s' was sucessfuly enabled.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
}
else:
message = {
"classes": ['success'],
"classes": ["success"],
"text": _("Subordinate '%(controller_id)s' was sucessfuly disabled.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
}
else:
if enabled:
message = {
"classes": ['error'],
"classes": ["error"],
"text": _("Failed to enable subordinate '%(controller_id)s'.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
}
else:
message = {
"classes": ['error'],
"classes": ["error"],
"text": _("Failed to disable subordinate '%(controller_id)s'.")
% dict(controller_id=controller_id)
% dict(controller_id=controller_id),
}
return bottle.template(
......@@ -177,9 +177,9 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
return False
return ConfigPageMixin.is_enabled_static(cls)
def get_page_form(self, form_name: str, data: dict, controller_id: str) -> typing.Tuple[
fapi.ForisAjaxForm, typing.Callable[[dict], typing.Tuple['str', 'str']]
]:
def get_page_form(
self, form_name: str, data: dict, controller_id: str
) -> typing.Tuple[fapi.ForisAjaxForm, typing.Callable[[dict], typing.Tuple["str", "str"]]]:
"""Returns appropriate foris form and handler to generate response
"""
form: fapi.ForisAjaxForm
......@@ -189,16 +189,16 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
def prepare_message(results: dict) -> dict:
if results["result"]:
message = {
"classes": ['success'],
"classes": ["success"],
"text": _("Device '%(controller_id)s' was sucessfully updated.")
% dict(controller_id=data["controller_id"])
% dict(controller_id=data["controller_id"]),
}
else:
message = {
"classes": ['error'],
"classes": ["error"],
"text": _("Failed to update subordinate '%(controller_id)s'.")
% dict(controller_id=data["controller_id"])
% dict(controller_id=data["controller_id"]),
}
return message
......@@ -213,21 +213,22 @@ class SubordinatesSetupPage(CommonPage, handlers.SubordinatesConfigHandler):
def prepare_message(results: dict) -> dict:
if results["result"]:
message = {
"classes": ['success'],
"classes": ["success"],
"text": _("Subsubordinate '%(controller_id)s' was sucessfully updated.")
% dict(controller_id=data["controller_id"])
% dict(controller_id=data["controller_id"]),
}
else:
message = {
"classes": ['error'],
"classes": ["error"],
"text": _("Failed to update subsubordinate '%(controller_id)s'.")
% dict(controller_id=data["controller_id"])
% dict(controller_id=data["controller_id"]),
}
return message
form.url = reverse(
"config_ajax_form", page_name="subordinates-setup", form_name="subsub-form")
"config_ajax_form", page_name="subordinates-setup", form_name="subsub-form"
)
return form, prepare_message
raise bottle.HTTPError(404, "No form '%s' not found." % form_name)
......@@ -272,9 +273,9 @@ class SubordinatesWifiPage(CommonPage):
return self._ajax_list_subordinates()
raise ValueError("Unknown AJAX action.")
def get_page_form(self, form_name: str, data: dict, controller_id: str) -> typing.Tuple[
fapi.ForisAjaxForm, typing.Callable[[dict], typing.Tuple['str', 'str']]
]:
def get_page_form(
self, form_name: str, data: dict, controller_id: str
) -> typing.Tuple[fapi.ForisAjaxForm, typing.Callable[[dict], typing.Tuple["str", "str"]]]:
"""Returns appropriate foris form and handler to generate response
"""
if form_name == "wifi-form":
......@@ -283,15 +284,12 @@ class SubordinatesWifiPage(CommonPage):
def prepare_message(results: dict) -> dict:
if results["result"]:
message = {
"classes": ['success'],
"text": _("Wifi settings was sucessfully updated.")
"classes": ["success"],
"text": _("Wifi settings was sucessfully updated."),
}
else:
message = {
"classes": ['error'],
"text": _("Failed to update Wifi settings.")
}
message = {"classes": ["error"], "text": _("Failed to update Wifi settings.")}
return message
form.url = reverse(
......@@ -302,14 +300,69 @@ class SubordinatesWifiPage(CommonPage):
raise bottle.HTTPError(404, "No form '%s' not found." % form_name)
# This represents a plugin page
class SubordinatesNetbootPage(CommonPage, handlers.NetbootConfigHandler):
slug = "subordinates-netboot"
menu_order = 3
template = "subordinates/subordinates_netboot"
template_type = "jinja2"
def save(self, *args, **kwargs):
# Handle form result here
return super().save(*args, **kwargs)
def render(self, **kwargs):
self._prepare_render_args(kwargs)
return super().render(**kwargs)
def _action_list(self):
res = current_state.backend.perform("netboot", "list")
return bottle.template(
"subordinates/_subordinates_list_netboot.html.j2",
records=res["devices"],
template_adapter=bottle.Jinja2Template,
)
def _action_generic(self, action):
if bottle.request.method != "POST":
messages.error(_("Wrong HTTP method."))
bottle.redirect(reverse("config_page", page_name="remote"))
form = self.get_serial_form(bottle.request.POST.decode())
if not form.data["serial"]:
raise bottle.HTTPError(404, "serial not found")
res = current_state.backend.perform("netboot", action, {"serial": form.data["serial"]})
bottle.response.set_header("Content-Type", "application/json")
return res
def _action_revoke(self):
return self._action_generic("revoke")
def _action_accept(self):
return self._action_generic("accept")
def call_ajax_action(self, action):
if action == "list":
return self._action_list()
if action == "revoke":
return self._action_revoke()
if action == "accept":
return self._action_accept()
raise ValueError("Unknown AJAX action.")
class SubordinatesJoinedPage(JoinedPages):
userfriendly_title = gettext("Managed devices")
slug = "subordinates"
no_url = True
subpages: typing.Iterable[typing.Type['ConfigPageMixin']] = [
subpages: typing.Iterable[typing.Type["ConfigPageMixin"]] = [
SubordinatesSetupPage,
SubordinatesWifiPage,
SubordinatesNetbootPage,
]
@classmethod
......@@ -330,13 +383,12 @@ class SubordinatesPlugin(ForisPlugin):
PLUGIN_NAME = "subordinates"
DIRNAME = os.path.dirname(os.path.abspath(__file__))
PLUGIN_STYLES = [
]
PLUGIN_STATIC_SCRIPTS = [
"js/subordinates.js", # static js file
]
PLUGIN_DYNAMIC_SCRIPTS = [
"subordinates.js", # dynamic js file (a template which will be rendered to javascript)
PLUGIN_STYLES: typing.List[str] = []
PLUGIN_STATIC_SCRIPTS: typing.List[str] = ["js/subordinates.js"] # static js file
PLUGIN_DYNAMIC_SCRIPTS: typing.List[str] = [
"subordinates.js" # dynamic js file (a template which will be rendered to javascript)
]
def __init__(self, app):
......
......@@ -18,10 +18,10 @@ import base64
import bottle
import typing
from foris import fapi
from foris import fapi, validators
from foris.form import File, Hidden, Textbox
from foris.state import current_state
from foris.utils.translators import gettext as _
from foris.utils.translators import gettext as _, gettext_dummy as gettext
from foris.config_handlers.base import BaseConfigHandler
from foris.config_handlers.wifi import WifiEditForm
......@@ -195,3 +195,33 @@ class SubordinatesWifiHandler(BaseConfigHandler):
def get_form(self):
ajax_form = WifiEditForm(self.data)
return ajax_form.foris_form
class NetbootConfigHandler(BaseConfigHandler):
STATE_ACCEPTED = gettext("accepted")
STATE_INCOMMING = gettext("incomming")
userfriendly_title = gettext("Netboot")
def get_form(self):
form = fapi.ForisForm("netboot", {})
form.add_section(
name="main_section",
title=self.userfriendly_title,
)
def form_cb(data):
return "save_result", {}
form.add_callback(form_cb)
return form
def get_serial_form(self, data=None):
generate_serial_form = fapi.ForisForm("serial_form", data)
serial_section = generate_serial_form.add_section("serial_section", title=None)
serial_section.add_field(
Textbox, name="serial", label=" ", required=True,
validators=[validators.MacAddress()],
)
return generate_serial_form
// TODO separete reasonable part for dynamic js
Foris.initNetbootRecordsForms = () => {
$(".record-form").submit((e) => {
e.preventDefault();
let form = $(e.currentTarget);
let action = $(e.originalEvent.explicitOriginalTarget).val();
switch (action) {
case "revoke":
$.ajax({
type: "POST",
url: form.attr('action'),
data: `${form.serialize()}&action=${action}`,
success: (data) => {
if (data.result) {
Foris.loadNetbootList();
// TODO success message
} else {
// TODO error message
}
},
});
break;
case "accept":
$.ajax({
type: "POST",
url: form.attr('action'),
data: `${form.serialize()}&action=${action}`,
success: (data) => {
if (data.result) {
Foris.loadNetbootList();
// TODO success message
} else {
// TODO error message
}
},
});
form.find("button").prop('disabled', true);
break;
}
});
}
Foris.addWsHanlder("netboot", (msg) => {
switch(msg.action) {
case "revoke":
case "accept":
Foris.loadNetbootList();
break;
}
});
// Update chart after page is rendred
$(document).ready(function() {
Foris.initNetbootRecordsForms();
Foris.loadNetbootList();
});
Foris.messages.subordinatesSubordinatesFailed = (controller_id) => {
return `{% trans controller_id="${controller_id}" %}Connection to '{{ controller_id }}' was interrupted.{% endtrans %}`;
};
Foris.netbootMessages = {
}
Foris.loadNetbootList = () => {
$.get('{{ url("config_ajax", page_name="subordinates-netboot") }}', {action: "list"})
.done((response) => {
$("#records-table").replaceWith(response);
Foris.initNetbootRecordsForms();
})
}
{% if records %}
<table id="records-table">
<thead><th>{% trans %}Serial{% endtrans %}</th><th>{% trans %}Paired{% endtrans %}</th><th></th></thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.serial }}</td>
{% if record.state == "accepted" %}
<td title="{% trans %}Paired{% endtrans %}">
<i class="fas fa-check"></i>
</td>
{% else %}
<td title="{% trans %}Unpaired{% endtrans %}">
<i class="fas fa-times"></i>
</td>
{% endif %}
<td>
<form action="{{ url("config_ajax", page_name="subordinates-netboot") }}" method="post" class="record-form">
<input type="hidden" name="serial" value="{{ record.serial }}">
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
{% if record.state == "accepted" %}
<button name="action" value="revoke" type="submit">{% trans %}Revoke{% endtrans %}</button>
{% else %}
<button name="action" value="accept" type="submit">{% trans %}Accept{% endtrans %}</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p id="records-table">{% trans %}No suitable devices found.{% endtrans %}</p>
{% endif %}
{% macro sub_buttons(controller_id, enabled, form_class) -%}
<form action="{{ url("config_ajax", page_name="subordinates") }}" method="post" class="subordinate-buttons {{ form_class }}">
<form action="{{ url("config_ajax", page_name="subordinates-setup") }}" method="post" class="subordinate-buttons {{ form_class }}">
<input type="hidden" name="controller_id" value="{{ controller_id }}">
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
{% if enabled %}
......
{% extends 'config/base.html.j2' %}
{% block config_base %}
{% if is_xhr is not defined %}
<div id="page-subordinates-netboot" class="config-page">
{% endif %}
{% include '_messages.html.j2' %}
<p>{% trans %}Manage devices which can be booted from this device through network.{% endtrans %}</p>
</form>
<h3>{% trans %}Devices suitable for netboot{% endtrans %}</h3>
<div id="records-table">
{% trans %}Loading devices...{% endtrans %}
</div>
<br />
<div class="message info">
{%- trans setup_url=url("config_page", page_name="subordinates-setup") %}
Note that if everything goes well the accepted devices should <strong>eventually appear</strong> on <a href="{{ setup_url }}">Set up</a> page.
{% endtrans -%}
</div>
{% if is_xhr is not defined %}
</div>
{% endif %}
{% endblock %}
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 to comment