Hi Chris,
many thanks for your feedback and suggestions :)
Adding context-sensitive button with corresponding menu item sounds good! In fact, this
is the proper way how context menu items should be handled, since WebAdmin data grids work
with context-sensitive "action buttons" which are reflected within the context
menu. I agree that this is a very useful feature to have in plugin API, it should be
implemented as part of next major PoC revision.
I have a question though: when the user clicks the custom button or corresponding context
menu item, will your plugin handle common dialog UI (everything except actual content) on
its own, or do you prefer to have some kind of "dialog UI" API to create the
standard-looking dialog frame for you? I like the idea of using iframe to present custom
content though :)
As for grouping multiple context-sensitive buttons and corresponding context menu items,
this makes sense and we have something like this already implemented in WebAdmin (in Host
main tab, Power Management drop-down group button, but this is defined not to appear in
context menu though). I think this should be part of "custom context-sensitive button
/ menu item" plugin API feature.
> If needed we can help work on an implementation for the dialog popup plugin framework
as we’d like to get it in relatively quickly so we can move forward on the content side of
our plugin.
If I understand correctly, this is about providing "dialog UI" API to create
the standard-looking dialog frame + custom content iframe. I'd like to help with this
as well, but you can play with the code and propose a patch on top of PoC rev.5
[
http://gerrit.ovirt.org/8120].
> For the reverse proxy, would it be easier from a customer standpoint to just include
a reverse proxy servlet such as this one?
Actually, I was under the impression that oVirt Engine (3.1) package (ovirt-engine) takes
care of setting up Apache web server, which redirects all traffic to Engine JBoss AS by
default (but maybe I am wrong here). However, if this is the case, an efficient way to
work around same-origin policy (cross-origin issues) is to configure Apache's reverese
proxy module (mod_proxy), as discussed in another thread on engine-devel:
http://lists.ovirt.org/pipermail/engine-devel/2012-September/002511.html
This way, you could use Apache to bring Engine and arbitrary (local or remote) web
services on same origin. What do you think?
On the other hand, using a special-purpose reverse proxy servlet directly in Engine, not
sure if that's a good idea though.. (I'd prefer configuring "front-end"
web server, such as Apache, for this purpose)
Chris, adding the proxy servlet brings a new dependency that can be
difficult to package. The one that you propose in particular is not
included in many distributions, including Fedora.
On the other hand using the web server doesn't bring any additional
dependency, as mod_proxy is already part of almost any installation of
Apache. We already use it by default to redirect requests to the
application server as Vojtech said.
Imagine for example that the engine is running in
. All what will be needed is dropping a new
netapp-plugin.conf file in the /etc/httpd/conf.d directory with a
content similar to this one:
ProxyPass /myservice
This can be easily done in the package of your plugin.
There are some things that we need to solve to make this work (currently
we send *all* the requests to the application server), but we want to
solve them anyway.
I'm also sending a couple of updates:
UI Plugins Follow-Up Meeting: scheduled for Tuesday, October 9 at 4pm GMT+1 (I'll
send a meeting invitation soon, also planning to re-write oVirt UI Plugins wiki soon after
that)
PoC rev.5 patch update: many thanks to Juan and Doron for reviewing the recent patch
[
http://gerrit.ovirt.org/8120]! I'll update the patch accordingly as revision 5.1
Planned items to be included in PoC revision 6:
* adding custom context-sensitive button (either separately or within a button
group), along with corresponding context menu item representation
* update custom main tab to show the actual content (iframe) from the given URL
* adding custom sub tab that shows content from the given URL
More items to consider having in PoC revision 6:
* "dialog UI" API to create the standard-looking dialog frame + custom
content iframe
* Engine REST API exposed through plugin API
* integration with Tasks pane to provide visual feedback on custom operations
* extending existing dialogs (e.g. Edit Cluster Policy - custom properties)
Let me know what you think.
Regards,
Vojtech
----- Original Message -----
From: "Christopher Morrissey" <Christopher.Morrissey(a)netapp.com>
To: vszocs(a)redhat.com
Cc: "George Costea" <George.Costea(a)netapp.com>, "Dustin
Schoenbrun" <Dustin.Schoenbrun(a)netapp.com>, engine-devel(a)ovirt.org
Sent: Tuesday, October 2, 2012 6:15:34 PM
Subject: RE: [Engine-devel] UI Plugins: PoC patch revision 5 is here
Hi Vojtech,
The patch is great and has cleared up some questions I had about the implementation. I’ve
been able to get the sample plugins working and waded through the code changes, so feel
like I have a good understanding of what was delivered. Thanks a lot for putting this all
together. I do have a few questions and suggestions for you, though.
Will there eventually be a way to add one or more buttons to the context sensitive
buttons and menus throughout the interface? For example, we’d like to allow a button to
provision a datastore when a data center, cluster, or host is selected. When the button is
clicked, it would open a dialog with an iframe in which we would load our interface to
perform the associated action. Some contexts would have several buttons, such as a storage
domain (provision, resize, dedupe management, destroy) so it would probably make sense for
a drop down menu to be available as well so that the number of buttons above the table
doesn’t get too cluttered. We’d also like to add these same actions to the menus that
popup when a contextual object is right clicked.
If needed we can help work on an implementation for the dialog popup plugin framework as
we’d like to get it in relatively quickly so we can move forward on the content side of
our plugin.
For the reverse proxy, would it be easier from a customer standpoint to just include a
reverse proxy servlet such as this one?
http://sourceforge.net/projects/j2ep/
I’m just thinking that setting up and managing a plugin for the HTTP server may be more
complex than including one internally that would be transparent to a customer.
Thanks,
Chris
From: Vojtech Szocs < vszocs(a)redhat.com >
Date: September 21, 2012 4:37:31 PM EDT
To: engine-devel < engine-devel(a)ovirt.org >
Subject: [Engine-devel] UI Plugins: PoC patch revision 5 is here
<blockquote>
Hi guys,
it's been a while but here comes the latest revision of UI Plugins proof-of-concept
patch (please find it attached).
This revision was originally meant to focus solely on server-side components of the
plugin infrastructure. However, I ended up implementing all the major concepts and ideas
as discussed on engine-devel mailing list, impacting both client-side and server-side
parts of the plugin infrastructure. As a result, UI plugin infrastructure should be pretty
much complete now, so we can focus on specific plugin API features in upcoming PoC
revisions.
There's a whole bunch of changes and improvements in this revision, so I'll try
to cover all the relevant parts step by step. If you have any comments, questions or
ideas, please let me know!
So here we go... (or if you just want to get the patch, find the link at the end of this
message)
0. Added new Engine configuration values
UI plugin data path is represented by ConfigValues.UIPluginDataPath enum option
("UIPluginDataPath" in vdc_options table), and resolved relative to
ConfigValues.DataDir if possible. Following (default) values:
* UIPluginDataPath = ui-plugins
* DataDir = /usr/share/ovirt-engine
result in UI plugin data path: /usr/share/ovirt-engine/ui-plugins
UI plugin config path is represented by ConfigValues.UIPluginConfigPath enum option
("UIPluginConfigPath" in vdc_options table), and resolved relative to
ConfigValues.ConfigDir if possible. Following (default) values:
* UIPluginConfigPath = ui-plugins
* ConfigDir = /etc/ovirt-engine
result in UI plugin config path: /etc/ovirt-engine/ui-plugins
1. Processing UI plugin data on the server
PluginDataManager is the class responsible for reading, validating and caching UI plugin
descriptor/configuration data on the server (Engine). It has two main responsibilities:
* return a snapshot of currently valid plugin data ( getCurrentData method)
* reload plugin data from local file system if necessary ( reloadData method)
The reloadData method doesn't modify "live" plugin data directly. Instead,
it creates a local working copy of current plugin data, updates this copy as it
reads/validates plugin descriptor and configuration files, and attempts to update
"live" plugin data through conditional reference re-assignment (using
java.util.concurrent.atomic.AtomicReference.compareAndSet method).
In other words, reloadData method makes no attempts with regard to Java lock-based
synchronization, in favor of dealing with "live" data through AtomicReference
(reference that involves atomic volatile reads and writes):
* In the best case, a thread will succeed in updating "live" data (
AtomicReference.compareAndSet == true), which means that "live" data remained
unchanged since this thread acquired a reference of current plugin data.
* In the worst case, a thread will NOT succeed in updating "live" data (
AtomicReference.compareAndSet == false), which means that "live" data was
already changed by another thread since this thread acquired a reference of current plugin
data.
In my opinion, when dealing with external resources like the local file system, this is a
good compromise between performance and up-to-date data. While we might not get
"completely-up-to-date" data at the given point in time ( reloadData +
getCurrentData ), we are guaranteed to get "recently-up-to-date" and consistent
data. In other words, the requirement of "completely-up-to-date" data would
involve synchronized statements that would hurt performance. In my (very humble) opinion,
the benefit of having "completely-up-to-date" data, at the cost of reduced
performance, is not really worth it, especially in our case when the user can just hit
refresh (F5) to reload WebAdmin and its plugin data.
Plugin descriptor files are expected to be placed in UI plugin data path, for example:
/usr/share/ovirt-engine/ui-plugins/foo.json
Following descriptor file attributes are implemented and recognized by the plugin
infrastructure:
* name : A name that uniquely identifies the plugin (required attribute).
* url : URL of plugin host page that invokes the plugin code (required attribute).
* config : Default configuration object associated with the plugin (optional
attribute).
* resourcePath : Path to plugin static resources, relative to UI plugin data path
(optional attribute). This is used when serving plugin files through Engine
PluginResourceServlet (more on this below).
Plugin configuration files are expected to be placed in UI plugin config path, for
example: /etc/engine/ui-plugins/foo-config.json
Note that plugin configuration files follow the
"<descriptorFileName>-config.json" convention.
Following configuration file attributes are implemented and recognized by the plugin
infrastructure:
* config : Custom configuration object associated with the plugin (optional
attribute). This overrides the default plugin descriptor configuration, if any.
* enabled : Indicates whether the plugin should be loaded on WebAdmin startup
(optional attribute). Default value is 'true'.
* order : Defines the relative order in which the plugin will be loaded on WebAdmin
startup (optional attribute). Default value is Integer.MAX_VALUE (lowest order).
The concept of merging custom configuration ( config attribute in foo-config.json ), if
any, on top of default configuration ( config attribute in foo.json ), if any, remains
unchanged. This makes the plugin configuration quite flexible - in my opinion, the added
complexity of handling/merging such configuration is definitely worth the effort.
The enabled attribute is straight-forward, allowing users to turn the given plugin off,
if necessary. In future, users should still be able to load such plugins through WebAdmin
GUI.
The order attribute controls the order in which plugins are loaded on WebAdmin startup.
Since plugin resources are fetched asynchronously by the browser, this is basically a way
of imposing some degree of determinism in the "generally-non-deterministic"
plugin environment, which is helpful when troubleshooting problems with multiple plugins.
This attribute is also helpful due to file listing methods in java.io.File giving no
guarantees that files would be listed in any particular order (otherwise we could just go
for the "NN-<descriptorFileName>.json" convention, with NN being the order
number).
2. Modified behavior of WebadminDynamicHostingServlet
WebadminDynamicHostingServlet is the servlet used to serve WebAdmin application host page
(HTML page that bootstraps WebAdmin JavaScript code).
In addition to its former behavior, as part of handling the given request,
WebadminDynamicHostingServlet :
* reloads descriptor/configuration data from local file system if necessary, and
obtains a snapshot of currently valid plugin data (
PluginDataManager.reloadAndGetCurrentData )
* embeds all plugin meta-data, suitable for use in client-side plugin infrastructure,
into WebAdmin host page as "pluginDefinitions" JavaScript array (
PluginDefinitions )
As a result, reloading UI plugin descriptor/configuration data is as simple as refreshing
(F5) WebAdmin application in the browser (no need to restart Engine).
3. Added servlet for serving plugin static resources
PluginResourceServlet is the servlet used to serve UI plugin static files (plugin host
page, 3rd party JavaScript, etc.) from the local file system.
For example, requesting URL:
*
http://<EngineManagerHost>:8700/webadmin/webadmin/plugin/foo/content/start.html
will send the content of:
* /usr/share/ovirt-engine/ui-plugins/<resourcePath>/content/start.html
to the client.
As shown in the above example:
* /webadmin/webadmin/plugin/ is the servlet root path for PluginResourceServlet
* in the extra path beyond the servlet root path ( /foo/content/start.html ):
* /foo represents the name of the plugin
* /content/start.html represents the path to requested resource, relative to
"UIPluginDataPath / <resourcePath>"
Note that each plugin using PluginResourceServlet to serve its static files must declare
non-empty resourcePath attribute in within the plugin descriptor.
Also note that PluginResourceServlet , unlike WebadminDynamicHostingServlet , does NOT
reload descriptor/configuration data from local file system as part of handling the given
request. In other words, it's assumed that plugin data has already been (re)loaded
when serving WebAdmin application host page, with subsequent requests to
PluginResourceServlet reading the current plugin information.
Until we solve the cross-origin issue in a clean way, PluginResourceServlet should be
used to serve all plugin resources from local file system.
4. Plugin lifecycle improved to deal with misbehaving plugins
PluginState enum has been modified to deal with plugins that allow uncaught exceptions to
escape from plugin event handler functions (e.g. "UiInit"):
* removed state INITIALIZED
* added state INITIALIZING : The plugin is (currently) being initialized by calling
UiInit event handler function.
* added state IN_USE : Plugin's UiInit event handler function has completed
successfully, we can now call other event handler functions as necessary. The plugin is in
use now.
* added state FAILED : An uncaught exception escaped while calling an event handler
function, which indicates internal error within the plugin code. The plugin is removed
from service.
I've attached a simple state diagram that illustrates different states and
transitions between them (green color is initial state, red color is end state).
Uncaught exceptions in plugin event handler functions will be caught and handled by the
plugin infrastructure. This prevents a misbehaving plugin from breaking WebAdmin
application, since WebAdmin is the caller (initiator) of the function call. In such case,
the plugin will be removed from service.
Update on cross-origin issue (consequence of same-origin policy)
In order for the plugin to access WebAdmin plugin API, plugin host page (e.g. start.html
) must be served from URL on same origin as Engine origin. Otherwise, plugin code running
in the context of an iframe'd host page will fail to evaluate
"parent.pluginApi" expression, with "parent" being top-level
(WebAdmin) window, and "pluginApi" being the global plugin API object exposed by
WebAdmin.
This is why PluginResourceServlet , available on Engine origin, should be used to serve
all plugin resources from local file system.
There's only one issue that remains to be solved: cross-origin "plugin vs.
remote service" communication, with "remote service" being anything other
than Engine (REST API). In future, we'll address this with Apache reverse proxy
configuration, so that users can configure Apache server (placed in front of Engine JBoss
AS) to put arbitrary (local or remote non-Engine) services on same origin. However, this
requires a change in current Apache configuration. Until then, users can manually edit the
Engine Apache configuration file ( /etc/httpd/conf.d/ovirt-engine.conf ).
I've attached some sample plugin files for you to experiment with. Instead of
attaching actual patch file (92 kB) to this email, I've submitted the patch to oVirt
Gerrit:
http://gerrit.ovirt.org/8120
Let me know what you think!
Cheers,
Vojtech
</blockquote>
<blockquote>
<ui-plugin-sample-files.tar.gz>
</blockquote>
<blockquote>
<ui-plugin-lifecycle.png>
</blockquote>
<blockquote>
_______________________________________________
Engine-devel mailing list
Engine-devel(a)ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-devel
</blockquote>
_______________________________________________
Engine-devel mailing list
Engine-devel(a)ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-devel
--
Dirección Comercial: C/Jose Bardasano Baos, 9, Edif. Gorbea 3, planta
3ºD, 28016 Madrid, Spain
Inscrita en el Reg. Mercantil de Madrid – C.I.F. B82657941 - Red Hat S.L.