sculpt: add button for restarting a component

Fixes #3885
This commit is contained in:
Norman Feske 2020-09-10 15:21:56 +02:00
parent c9f2847420
commit 5dc8e330b6
8 changed files with 176 additions and 33 deletions

View File

@ -108,8 +108,43 @@ struct Sculpt::Deploy
*/
deploy.for_each_sub_node("start", [&] (Xml_node node) {
Start_name const name = node.attribute_value("name", Start_name());
if (!_runtime_info.abandoned_by_user(name))
append_xml_node(node);
if (_runtime_info.abandoned_by_user(name))
return;
xml.node("start", [&] () {
/*
* Copy attributes
*/
xml.attribute("name", name);
/* override version with restarted version, after restart */
using Version = Child_state::Version;
Version version = _runtime_info.restarted_version(name);
if (version.value == 0)
version = Version { node.attribute_value("version", 0U) };
if (version.value > 0)
xml.attribute("version", version.value);
auto copy_attribute = [&] (auto attr)
{
if (node.has_attribute(attr)) {
using Value = String<128>;
xml.attribute(attr, node.attribute_value(attr, Value()));
}
};
copy_attribute("caps");
copy_attribute("ram");
copy_attribute("cpu");
copy_attribute("pkg");
/* copy start-node content */
node.with_raw_content([&] (char const *start, size_t length) {
xml.append(start, length); });
});
});
/*

View File

@ -24,13 +24,16 @@ void Graph::_gen_selected_node_content(Xml_generator &xml, Start_name const &nam
if (removable) {
gen_named_node(xml, "frame", "operations", [&] () {
xml.node("vbox", [&] () {
xml.node("hbox", [&] () {
gen_named_node(xml, "button", "remove", [&] () {
_remove_item.gen_button_attr(xml, "remove");
_action_item.gen_button_attr(xml, "remove");
xml.node("label", [&] () {
xml.attribute("text", "Remove");
});
});
xml.attribute("text", "Remove"); }); });
gen_named_node(xml, "button", "restart", [&] () {
_action_item.gen_button_attr(xml, "restart");
xml.node("label", [&] () {
xml.attribute("text", "Restart"); }); });
});
});
}
@ -276,8 +279,8 @@ Dialog::Hover_result Graph::hover(Xml_node hover)
_ram_fs_dialog.match_sub_dialog(hover, "depgraph", "frame", "vbox", "frame", "vbox"),
_node_button_item.match(hover, "depgraph", "frame", "vbox", "button", "name"),
_add_button_item .match(hover, "depgraph", "button", "name"),
_remove_item .match(hover, "depgraph", "frame", "vbox",
"frame", "vbox", "button", "name"));
_action_item .match(hover, "depgraph", "frame", "vbox",
"frame", "hbox", "button", "name"));
if (_add_button_item.hovered("global+")) {
@ -335,11 +338,11 @@ void Graph::click(Action &action)
_runtime_state.toggle_selection(_node_button_item._hovered,
_runtime_config);
_remove_item.reset();
_action_item.reset();
}
if (_remove_item.hovered("remove"))
_remove_item.propose_activation_on_click();
if (_action_item.hovered("remove") || _action_item.hovered("restart"))
_action_item.propose_activation_on_click();
}
@ -352,11 +355,11 @@ void Graph::clack(Action &action, Ram_fs_dialog::Action &ram_fs_action)
if (_storage_dialog->clack(action) == Clack_result::CONSUMED)
return;
if (_remove_item.hovered("remove")) {
if (_action_item.hovered("remove")) {
_remove_item.confirm_activation_on_clack();
_action_item.confirm_activation_on_clack();
if (_remove_item.activated("remove")) {
if (_action_item.activated("remove")) {
action.remove_deployed_component(_runtime_state.selected());
/*
@ -366,8 +369,16 @@ void Graph::clack(Action &action, Ram_fs_dialog::Action &ram_fs_action)
_runtime_state.toggle_selection(_runtime_state.selected(),
_runtime_config);
}
} else {
_remove_item.reset();
}
if (_action_item.hovered("restart")) {
_action_item.confirm_activation_on_clack();
if (_action_item.activated("restart"))
action.restart_deployed_component(_runtime_state.selected());
}
_action_item.reset();
}

View File

@ -51,8 +51,8 @@ struct Sculpt::Graph : Dialog
Depot_deploy::Children const &_deploy_children;
Hoverable_item _node_button_item { };
Hoverable_item _add_button_item { };
Activatable_item _remove_item { };
Hoverable_item _add_button_item { };
Activatable_item _action_item { };
/*
* Defined when '+' button is hovered
@ -106,6 +106,7 @@ struct Sculpt::Graph : Dialog
struct Action : Storage_dialog::Action
{
virtual void remove_deployed_component(Start_name const &) = 0;
virtual void restart_deployed_component(Start_name const &) = 0;
virtual void toggle_launcher_selector(Rect) = 0;
};

View File

@ -642,6 +642,17 @@ struct Sculpt::Main : Input_event_handler,
_deploy.update_managed_deploy_config(_manual_deploy_rom.xml());
}
/*
* Graph::Action interface
*/
void restart_deployed_component(Start_name const &name) override
{
_runtime_state.restart(name);
/* update config/managed/deploy with the component 'name' removed */
_deploy.update_managed_deploy_config(_manual_deploy_rom.xml());
}
/*
* Graph::Action interface
*/

View File

@ -25,6 +25,10 @@ namespace Sculpt { struct Child_state; }
struct Sculpt::Child_state : Noncopyable
{
public:
struct Version { unsigned value; };
private:
Registry<Child_state>::Element _element;
@ -37,7 +41,7 @@ struct Sculpt::Child_state : Noncopyable
Ram_quota _ram_quota = _initial_ram_quota;
Cap_quota _cap_quota = _initial_cap_quota;
struct Version { unsigned value; } _version { 0 };
Version _version { 0 };
public:

View File

@ -30,6 +30,8 @@ class Sculpt::Runtime_state : public Runtime_info
{
public:
using Version = Child_state::Version;
struct Info
{
bool selected;
@ -45,6 +47,8 @@ class Sculpt::Runtime_state : public Runtime_info
unsigned long assigned_caps;
unsigned long avail_caps;
Version version;
};
private:
@ -57,11 +61,14 @@ class Sculpt::Runtime_state : public Runtime_info
{
Start_name const name;
Info info { .selected = false,
.tcb = false,
.tcb_updated = false,
.assigned_ram = 0, .avail_ram = 0,
.assigned_caps = 0, .avail_caps = 0 };
Info info { .selected = false,
.tcb = false,
.tcb_updated = false,
.assigned_ram = 0,
.avail_ram = 0,
.assigned_caps = 0,
.avail_caps = 0,
.version = { 0 }};
bool abandoned_by_user = false;
@ -79,11 +86,27 @@ class Sculpt::Runtime_state : public Runtime_info
struct Abandoned_child : Interface
{
Start_name const name;
Abandoned_child(Start_name const &name) : name(name) { };
};
Registry<Registered<Abandoned_child> > _abandoned_children { };
/**
* Child that was interactively restarted
*/
struct Restarted_child : Interface
{
Start_name const name;
Version version;
Restarted_child(Start_name const &name, Version const &version)
: name(name), version(version) { };
};
Registry<Registered<Restarted_child> > _restarted_children { };
/**
* Child that was interactively launched
*/
@ -115,20 +138,25 @@ class Sculpt::Runtime_state : public Runtime_info
construction.construct(alloc, pkg_path, info, space);
}
void gen_deploy_start_node(Xml_generator &xml) const
void gen_deploy_start_node(Xml_generator &xml, Runtime_state const &state) const
{
if (!launched)
return;
gen_named_node(xml, "start", name, [&] () {
Version const version = state.restarted_version(name);
if (version.value > 0)
xml.attribute("version", version.value);
/* interactively constructed */
if (construction.constructed()) {
xml.attribute("pkg", construction->path);
xml.attribute("xpos", construction->affinity_location.xpos());
xml.attribute("ypos", construction->affinity_location.ypos());
xml.attribute("width", construction->affinity_location.width());
xml.attribute("xpos", construction->affinity_location.xpos());
xml.attribute("ypos", construction->affinity_location.ypos());
xml.attribute("width", construction->affinity_location.width());
xml.attribute("height", construction->affinity_location.height());
xml.node("route", [&] () {
@ -178,15 +206,17 @@ class Sculpt::Runtime_state : public Runtime_info
Xml_node const ram = node.sub_node("ram");
child.info.assigned_ram = max(ram.attribute_value("assigned", Number_of_bytes()),
ram.attribute_value("quota", Number_of_bytes()));
child.info.avail_ram = ram.attribute_value("avail", Number_of_bytes());
child.info.avail_ram = ram.attribute_value("avail", Number_of_bytes());
}
if (node.has_sub_node("caps")) {
Xml_node const caps = node.sub_node("caps");
child.info.assigned_caps = max(caps.attribute_value("assigned", 0UL),
caps.attribute_value("quota", 0UL));
child.info.avail_caps = caps.attribute_value("avail", 0UL);
child.info.avail_caps = caps.attribute_value("avail", 0UL);
}
child.info.version.value = node.attribute_value("version", 0U);
}
static bool element_matches_xml_node(Child const &elem, Xml_node node)
@ -240,9 +270,28 @@ class Sculpt::Runtime_state : public Runtime_info
bool abandoned_by_user(Start_name const &name) const override
{
bool result = false;
_abandoned_children.for_each([&] (Abandoned_child const &child) {
if (!result && child.name == name)
result = true; });
return result;
}
/**
* Return version of restarted child
*
* If the returned value is version 0, the child has not been
* restarted.
*/
Version restarted_version(Start_name const &name) const override
{
Version result { 0 };
_restarted_children.for_each([&] (Restarted_child const &child) {
if (!result.value && child.name == name)
result = child.version; });
return result;
}
@ -252,7 +301,7 @@ class Sculpt::Runtime_state : public Runtime_info
void gen_launched_deploy_start_nodes(Xml_generator &xml) const override
{
_launched_children.for_each([&] (Launched_child const &child) {
child.gen_deploy_start_node(xml); });
child.gen_deploy_start_node(xml, *this); });
}
Info info(Start_name const &name) const
@ -363,6 +412,30 @@ class Sculpt::Runtime_state : public Runtime_info
new (_alloc) Registered<Abandoned_child>(_abandoned_children, name);
}
void restart(Start_name const &name)
{
/* determin current version from most recent state report */
Version current_version { 0 };
_children.for_each([&] (Child const &child) {
if (child.name == name)
current_version = child.info.version; });
Version const next_version { current_version.value + 1 };
bool already_restarted = false;
_restarted_children.for_each([&] (Restarted_child &child) {
if (child.name == name) {
already_restarted = true;
child.version = next_version;
}
});
if (!already_restarted)
new (_alloc)
Registered<Restarted_child>(_restarted_children, name, next_version);
}
void launch(Start_name const &name, Path const &launcher)
{
new (_alloc) Registered<Launched_child>(_launched_children, name, launcher);
@ -431,6 +504,9 @@ class Sculpt::Runtime_state : public Runtime_info
_launched_children.for_each([&] (Launched_child &child) {
destroy(_alloc, &child); });
_restarted_children.for_each([&] (Restarted_child &child) {
destroy(_alloc, &child); });
}
};

View File

@ -19,6 +19,7 @@
#include <model/storage_devices.h>
#include <model/storage_target.h>
#include <model/nic_target.h>
#include <model/child_state.h>
namespace Sculpt {
@ -29,6 +30,8 @@ namespace Sculpt {
struct Runtime_info : Interface
{
using Version = Child_state::Version;
/**
* Return true if specified child is present in the runtime subsystem
*/
@ -36,6 +39,8 @@ namespace Sculpt {
virtual bool abandoned_by_user(Start_name const &) const = 0;
virtual Version restarted_version(Start_name const &) const = 0;
virtual void gen_launched_deploy_start_nodes(Xml_generator &) const = 0;
};

View File

@ -57,7 +57,7 @@ struct Sculpt::Activatable_item : Hoverable_item
if (!_selected.valid() || !_activated.valid())
Hoverable_item::gen_button_attr(xml, id);
if (_selected.valid() && _selected == _hovered)
if (_selected.valid() && _selected == _hovered && _selected == id)
xml.attribute("selected", "yes");
}
};