Feature URL: https://www.aeolusproject.org/redmine/issues/3391
This patch adds 'service_dependecies' action to deployables controller, where user can see diagram of assembly service's dependencies declared in deployable XML. For now it's proof of concept for the feature.
Reviewer needs to install those RPMs: - graphviz - graphviz-ruby - gem ruby-graphviz (will need to be packaged if we want to use this patch. Packaging of it will be good also for DC, where it's used for drawing instance states diagram)
Also I will add tests when community approve this patch.
Reviewer could run into https://bugzilla.redhat.com/show_bug.cgi?id=831201 during reviewing (easy workaround - fix line 196 in deployable.rb with 'audrey_error = nil').
From: Jozef Zigmund jzigmund@redhat.com
--- src/Gemfile.in | 1 + src/app/controllers/deployables_controller.rb | 13 +++++++ src/app/models/deployable.rb | 1 + src/app/util/deployable_xml.rb | 35 ++++++++++++++++++++ .../deployables/service_dependencies.html.haml | 11 ++++++ src/app/views/deployables/show.html.haml | 1 + src/config/initializers/mime_types.rb | 1 + src/config/locales/en.yml | 1 + src/config/routes.rb | 1 + 9 files changed, 65 insertions(+), 0 deletions(-) create mode 100644 src/app/views/deployables/service_dependencies.html.haml
diff --git a/src/Gemfile.in b/src/Gemfile.in index 1d90a91..7bb3704 100644 --- a/src/Gemfile.in +++ b/src/Gemfile.in @@ -23,6 +23,7 @@ gem 'compass' gem 'compass-960-plugin', :require=> 'ninesixty' gem 'delayed_job', '~> 2.1.4' gem 'paranoia' +gem 'graphviz'
platforms :ruby_18 do gem 'fastercsv' diff --git a/src/app/controllers/deployables_controller.rb b/src/app/controllers/deployables_controller.rb index 3e3b76a..e4d3f91 100644 --- a/src/app/controllers/deployables_controller.rb +++ b/src/app/controllers/deployables_controller.rb @@ -283,6 +283,19 @@ class DeployablesController < ApplicationController redirect_to_original({"deployables_preset_filter" => params[:deployables_preset_filter], "deployables_search" => params[:deployables_search]}) end
+ def service_dependencies + deployable = Deployable.find(params[:id]) + deployable_xml = DeployableXML.new(deployable.xml) + respond_to do |format| + format.html do + render :locals => {:deployable => deployable} + end + format.png do + render :text => deployable_xml.display_digraph + end + end + end + private
def set_header diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index 9c6aff9..b052b44 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -15,6 +15,7 @@ #
require 'util/conductor' +require 'util/deployable_xml'
class Deployable < ActiveRecord::Base class << self diff --git a/src/app/util/deployable_xml.rb b/src/app/util/deployable_xml.rb index cdbbc39..ab46251 100644 --- a/src/app/util/deployable_xml.rb +++ b/src/app/util/deployable_xml.rb @@ -17,6 +17,7 @@ =begin XML Wrapper objects for the deployable XML format =end +require "graphviz"
class ValidationError < RuntimeError; end
@@ -100,6 +101,8 @@ end
class AssemblyXML
+ attr_accessor :root + def initialize(xmlstr_or_node = "") if xmlstr_or_node.is_a? Nokogiri::XML::Node @root = xmlstr_or_node @@ -202,6 +205,38 @@ class DeployableXML @relax_file = "#{File.dirname(File.expand_path(__FILE__))}/deployable-rng.xml" end
+ def display_digraph + graph = GraphViz.new(:G, :type => :digraph) + if has_reference? + draw_nodes(graph) + draw_edges(graph) + else + draw_nodes(graph) + end + graph.output :png => String + end + + def draw_nodes(graph) + assemblies.each do |assembly| + graph.add_node(assembly.name) + end + end + + def draw_edges(graph) + assemblies.each do |assembly| + references = assembly.root.xpath(".//reference") + unless references.empty? + references.each do |reference| + graph.add_edge(assembly.name, reference.attributes['assembly'].value) + end + end + end + end + + def has_reference? + not @root.xpath("//reference").empty? + end + def name @root["name"] if @root end diff --git a/src/app/views/deployables/service_dependencies.html.haml b/src/app/views/deployables/service_dependencies.html.haml new file mode 100644 index 0000000..075de04 --- /dev/null +++ b/src/app/views/deployables/service_dependencies.html.haml @@ -0,0 +1,11 @@ += render :partial => 'layouts/admin_nav' +%header.page-header + .obj_actions + .button-group + - if check_privilege(Privilege::MODIFY, deployable) + = link_to t('deployables.show.edit'), edit_polymorphic_path([@catalog, deployable]), :class => 'button', :id => 'edit_button' + = link_to t('deployables.show.edit_xml'), edit_polymorphic_path([@catalog, deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' + = button_to t('delete'), polymorphic_path([@catalog, deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete' + %h1.catalog_entries= deployable.name +%p + = image_tag(service_dependencies_deployable_path(deployable, :format => :png)) \ No newline at end of file diff --git a/src/app/views/deployables/show.html.haml b/src/app/views/deployables/show.html.haml index a7ee021..d28db60 100644 --- a/src/app/views/deployables/show.html.haml +++ b/src/app/views/deployables/show.html.haml @@ -3,6 +3,7 @@ .obj_actions .button-group - if check_privilege(Privilege::MODIFY, @deployable) + = link_to t(".dependencies_diagram"), service_dependencies_deployable_path(@deployable, :catalog_id => @catalog, :pushed_count => @pushed_count), :class => 'button' = link_to t('.edit'), edit_polymorphic_path([@catalog, @deployable]), :class => 'button', :id => 'edit_button' = link_to t('.edit_xml'), edit_polymorphic_path([@catalog, @deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' = button_to t('.delete'), polymorphic_path([@catalog, @deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete' diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb index 18ecf33..bd77cce 100644 --- a/src/config/initializers/mime_types.rb +++ b/src/config/initializers/mime_types.rb @@ -20,3 +20,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/png", :png diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml index 9295373..1684721 100644 --- a/src/config/locales/en.yml +++ b/src/config/locales/en.yml @@ -1036,6 +1036,7 @@ en: n_a: "N/A" uuids: "UUIDs" show_hide_uuids: "Show/Hide" + dependencies_diagram: Dependencies Diagram form: choose_xml: Choose Deployable XML file catalog: Catalog diff --git a/src/config/routes.rb b/src/config/routes.rb index e7f26c5..18ed7f2 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -237,6 +237,7 @@ Conductor::Application.routes.draw do end member do get :definition + get :service_dependencies end end
On 07/04/2012 06:03 PM, jzigmund@redhat.com wrote:
From: Jozef Zigmund jzigmund@redhat.com
src/Gemfile.in | 1 + src/app/controllers/deployables_controller.rb | 13 +++++++ src/app/models/deployable.rb | 1 + src/app/util/deployable_xml.rb | 35 ++++++++++++++++++++ .../deployables/service_dependencies.html.haml | 11 ++++++ src/app/views/deployables/show.html.haml | 1 + src/config/initializers/mime_types.rb | 1 + src/config/locales/en.yml | 1 + src/config/routes.rb | 1 + 9 files changed, 65 insertions(+), 0 deletions(-) create mode 100644 src/app/views/deployables/service_dependencies.html.haml
diff --git a/src/Gemfile.in b/src/Gemfile.in index 1d90a91..7bb3704 100644 --- a/src/Gemfile.in +++ b/src/Gemfile.in @@ -23,6 +23,7 @@ gem 'compass' gem 'compass-960-plugin', :require=> 'ninesixty' gem 'delayed_job', '~> 2.1.4' gem 'paranoia' +gem 'graphviz'
platforms :ruby_18 do gem 'fastercsv' diff --git a/src/app/controllers/deployables_controller.rb b/src/app/controllers/deployables_controller.rb index 3e3b76a..e4d3f91 100644 --- a/src/app/controllers/deployables_controller.rb +++ b/src/app/controllers/deployables_controller.rb @@ -283,6 +283,19 @@ class DeployablesController < ApplicationController redirect_to_original({"deployables_preset_filter" => params[:deployables_preset_filter], "deployables_search" => params[:deployables_search]}) end
def service_dependencies
deployable = Deployable.find(params[:id])
deployable_xml = DeployableXML.new(deployable.xml)
respond_to do |format|
format.html do
render :locals => {:deployable => deployable}
end
format.png do
render :text => deployable_xml.display_digraph
end
end
end
private
def set_header
diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index 9c6aff9..b052b44 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -15,6 +15,7 @@ #
require 'util/conductor' +require 'util/deployable_xml'
class Deployable < ActiveRecord::Base class << self diff --git a/src/app/util/deployable_xml.rb b/src/app/util/deployable_xml.rb index cdbbc39..ab46251 100644 --- a/src/app/util/deployable_xml.rb +++ b/src/app/util/deployable_xml.rb @@ -17,6 +17,7 @@ =begin XML Wrapper objects for the deployable XML format =end +require "graphviz"
class ValidationError < RuntimeError; end
@@ -100,6 +101,8 @@ end
class AssemblyXML
- attr_accessor :root
- def initialize(xmlstr_or_node = "") if xmlstr_or_node.is_a? Nokogiri::XML::Node @root = xmlstr_or_node
@@ -202,6 +205,38 @@ class DeployableXML @relax_file = "#{File.dirname(File.expand_path(__FILE__))}/deployable-rng.xml" end
- def display_digraph
- graph = GraphViz.new(:G, :type => :digraph)
- if has_reference?
draw_nodes(graph)
draw_edges(graph)
- else
draw_nodes(graph)
- end
- graph.output :png => String
- end
- def draw_nodes(graph)
- assemblies.each do |assembly|
graph.add_node(assembly.name)
- end
- end
- def draw_edges(graph)
- assemblies.each do |assembly|
references = assembly.root.xpath(".//reference")
unless references.empty?
references.each do |reference|
graph.add_edge(assembly.name, reference.attributes['assembly'].value)
end
end
- end
- end
- def has_reference?
not @root.xpath("//reference").empty?
- end
- def name @root["name"] if @root end
diff --git a/src/app/views/deployables/service_dependencies.html.haml b/src/app/views/deployables/service_dependencies.html.haml new file mode 100644 index 0000000..075de04 --- /dev/null +++ b/src/app/views/deployables/service_dependencies.html.haml @@ -0,0 +1,11 @@ += render :partial => 'layouts/admin_nav' +%header.page-header
- .obj_actions
- .button-group
- if check_privilege(Privilege::MODIFY, deployable)
= link_to t('deployables.show.edit'), edit_polymorphic_path([@catalog, deployable]), :class => 'button', :id => 'edit_button'
= link_to t('deployables.show.edit_xml'), edit_polymorphic_path([@catalog, deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button'
= button_to t('delete'), polymorphic_path([@catalog, deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete'
- %h1.catalog_entries= deployable.name
+%p
- = image_tag(service_dependencies_deployable_path(deployable, :format => :png))
\ No newline at end of file diff --git a/src/app/views/deployables/show.html.haml b/src/app/views/deployables/show.html.haml index a7ee021..d28db60 100644 --- a/src/app/views/deployables/show.html.haml +++ b/src/app/views/deployables/show.html.haml @@ -3,6 +3,7 @@ .obj_actions .button-group - if check_privilege(Privilege::MODIFY, @deployable)
= link_to t(".dependencies_diagram"), service_dependencies_deployable_path(@deployable, :catalog_id => @catalog, :pushed_count => @pushed_count), :class => 'button' = link_to t('.edit'), edit_polymorphic_path([@catalog, @deployable]), :class => 'button', :id => 'edit_button' = link_to t('.edit_xml'), edit_polymorphic_path([@catalog, @deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' = button_to t('.delete'), polymorphic_path([@catalog, @deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete'
diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb index 18ecf33..bd77cce 100644 --- a/src/config/initializers/mime_types.rb +++ b/src/config/initializers/mime_types.rb @@ -20,3 +20,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/png", :png diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml index 9295373..1684721 100644 --- a/src/config/locales/en.yml +++ b/src/config/locales/en.yml @@ -1036,6 +1036,7 @@ en: n_a: "N/A" uuids: "UUIDs" show_hide_uuids: "Show/Hide"
dependencies_diagram: Dependencies Diagram form: choose_xml: Choose Deployable XML file catalog: Catalog
diff --git a/src/config/routes.rb b/src/config/routes.rb index e7f26c5..18ed7f2 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -237,6 +237,7 @@ Conductor::Application.routes.draw do end member do get :definition
endget :service_dependencies end
Hi Jozef, I didn't have time to do a proper review yet, but have a few findings about this so far: - graphviz has many pkg dependencies (it added 41 new pkgs). Maybe it could be optional - if graphviz is not installed, one some warning about missing graphviz would be displayed. - there is also already packaged graphviz-ruby gem - couldn't this be used? I didn't check this, maybe it's not sufficient, but it could save us some packaging - it seems that this code generates dependencies between assemblies, what we need are dependencies between services: so nodes would be services, edges would represent param references, assembly would be represented by a frame around services belonging to this assembly.
I checked deployable xml schema and what confuses me is that it seems that a service param can reference a service _or_ an assembly (according to param definition in app/util/deployable-rng.xml). If a param references assembly, then we can't resolve dependencies between services. Dan, you are most familiar with this - what do you think about it? As I understand it, referencing whole assemblies should be disabled.
Jan
On 07/13/2012 02:22 PM, Jan Provazník wrote:
On 07/04/2012 06:03 PM, jzigmund@redhat.com wrote:
From: Jozef Zigmund jzigmund@redhat.com
src/Gemfile.in | 1 + src/app/controllers/deployables_controller.rb | 13 +++++++ src/app/models/deployable.rb | 1 + src/app/util/deployable_xml.rb | 35 ++++++++++++++++++++ .../deployables/service_dependencies.html.haml | 11 ++++++ src/app/views/deployables/show.html.haml | 1 + src/config/initializers/mime_types.rb | 1 + src/config/locales/en.yml | 1 + src/config/routes.rb | 1 + 9 files changed, 65 insertions(+), 0 deletions(-) create mode 100644 src/app/views/deployables/service_dependencies.html.haml
diff --git a/src/Gemfile.in b/src/Gemfile.in index 1d90a91..7bb3704 100644 --- a/src/Gemfile.in +++ b/src/Gemfile.in @@ -23,6 +23,7 @@ gem 'compass' gem 'compass-960-plugin', :require=> 'ninesixty' gem 'delayed_job', '~> 2.1.4' gem 'paranoia' +gem 'graphviz'
platforms :ruby_18 do gem 'fastercsv' diff --git a/src/app/controllers/deployables_controller.rb b/src/app/controllers/deployables_controller.rb index 3e3b76a..e4d3f91 100644 --- a/src/app/controllers/deployables_controller.rb +++ b/src/app/controllers/deployables_controller.rb @@ -283,6 +283,19 @@ class DeployablesController < ApplicationController redirect_to_original({"deployables_preset_filter" => params[:deployables_preset_filter], "deployables_search" => params[:deployables_search]}) end
def service_dependencies
deployable = Deployable.find(params[:id])
deployable_xml = DeployableXML.new(deployable.xml)
respond_to do |format|
format.html do
render :locals => {:deployable => deployable}
end
format.png do
render :text => deployable_xml.display_digraph
end
end
end
private
def set_header
diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index 9c6aff9..b052b44 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -15,6 +15,7 @@ #
require 'util/conductor' +require 'util/deployable_xml'
class Deployable < ActiveRecord::Base class << self diff --git a/src/app/util/deployable_xml.rb b/src/app/util/deployable_xml.rb index cdbbc39..ab46251 100644 --- a/src/app/util/deployable_xml.rb +++ b/src/app/util/deployable_xml.rb @@ -17,6 +17,7 @@ =begin XML Wrapper objects for the deployable XML format =end +require "graphviz"
class ValidationError < RuntimeError; end
@@ -100,6 +101,8 @@ end
class AssemblyXML
- attr_accessor :root
- def initialize(xmlstr_or_node = "") if xmlstr_or_node.is_a? Nokogiri::XML::Node @root = xmlstr_or_node
@@ -202,6 +205,38 @@ class DeployableXML @relax_file = "#{File.dirname(File.expand_path(__FILE__))}/deployable-rng.xml" end
- def display_digraph
- graph = GraphViz.new(:G, :type => :digraph)
- if has_reference?
draw_nodes(graph)
draw_edges(graph)
- else
draw_nodes(graph)
- end
- graph.output :png => String
- end
- def draw_nodes(graph)
- assemblies.each do |assembly|
graph.add_node(assembly.name)
- end
- end
- def draw_edges(graph)
- assemblies.each do |assembly|
references = assembly.root.xpath(".//reference")
unless references.empty?
references.each do |reference|
graph.add_edge(assembly.name,
reference.attributes['assembly'].value)
end
end
- end
- end
- def has_reference?
not @root.xpath("//reference").empty?
- end
- def name @root["name"] if @root end
diff --git a/src/app/views/deployables/service_dependencies.html.haml b/src/app/views/deployables/service_dependencies.html.haml new file mode 100644 index 0000000..075de04 --- /dev/null +++ b/src/app/views/deployables/service_dependencies.html.haml @@ -0,0 +1,11 @@ += render :partial => 'layouts/admin_nav' +%header.page-header
- .obj_actions
- .button-group
- if check_privilege(Privilege::MODIFY, deployable)
= link_to t('deployables.show.edit'),
edit_polymorphic_path([@catalog, deployable]), :class => 'button', :id => 'edit_button'
= link_to t('deployables.show.edit_xml'),
edit_polymorphic_path([@catalog, deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button'
= button_to t('delete'), polymorphic_path([@catalog,
deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete'
- %h1.catalog_entries= deployable.name
+%p
- = image_tag(service_dependencies_deployable_path(deployable,
:format => :png)) \ No newline at end of file diff --git a/src/app/views/deployables/show.html.haml b/src/app/views/deployables/show.html.haml index a7ee021..d28db60 100644 --- a/src/app/views/deployables/show.html.haml +++ b/src/app/views/deployables/show.html.haml @@ -3,6 +3,7 @@ .obj_actions .button-group - if check_privilege(Privilege::MODIFY, @deployable)
= link_to t(".dependencies_diagram"),
service_dependencies_deployable_path(@deployable, :catalog_id => @catalog, :pushed_count => @pushed_count), :class => 'button' = link_to t('.edit'), edit_polymorphic_path([@catalog, @deployable]), :class => 'button', :id => 'edit_button' = link_to t('.edit_xml'), edit_polymorphic_path([@catalog, @deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' = button_to t('.delete'), polymorphic_path([@catalog, @deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete' diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb index 18ecf33..bd77cce 100644 --- a/src/config/initializers/mime_types.rb +++ b/src/config/initializers/mime_types.rb @@ -20,3 +20,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/png", :png diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml index 9295373..1684721 100644 --- a/src/config/locales/en.yml +++ b/src/config/locales/en.yml @@ -1036,6 +1036,7 @@ en: n_a: "N/A" uuids: "UUIDs" show_hide_uuids: "Show/Hide"
dependencies_diagram: Dependencies Diagram form: choose_xml: Choose Deployable XML file catalog: Catalog
diff --git a/src/config/routes.rb b/src/config/routes.rb index e7f26c5..18ed7f2 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -237,6 +237,7 @@ Conductor::Application.routes.draw do end member do get :definition
endget :service_dependencies end
Hi Jozef, I didn't have time to do a proper review yet, but have a few findings about this so far:
- graphviz has many pkg dependencies (it added 41 new pkgs). Maybe it
could be optional - if graphviz is not installed, one some warning about missing graphviz would be displayed.
- there is also already packaged graphviz-ruby gem - couldn't this be
used? I didn't check this, maybe it's not sufficient, but it could save us some packaging
- it seems that this code generates dependencies between assemblies,
what we need are dependencies between services: so nodes would be services, edges would represent param references, assembly would be represented by a frame around services belonging to this assembly.
I checked deployable xml schema and what confuses me is that it seems that a service param can reference a service _or_ an assembly (according to param definition in app/util/deployable-rng.xml). If a param references assembly, then we can't resolve dependencies between services. Dan, you are most familiar with this - what do you think about it? As I understand it, referencing whole assemblies should be disabled.
Jan
Services can depend on other services with in the same assembly. We don't have code that allows cross assembly references. If you are going to represent relationships do it service to service within an assembly.
There was talk to allow cross assembly relationships, but I doubt it will ever come to fruition.
Visual representation of the relationships is a cool idea.
Dan
On 14/07/2012, at 5:14 AM, Dan Radez wrote: <snip>
Visual representation of the relationships is a cool idea.
Hmmm, this sounds like the kind of thing well suited to being done client side, using vectors, and using javascript.
Using something like this:
Nifty examples:
http://raphaeljs.com/polar-clock.html http://raphaeljs.com/chart.html
Decent idea? :)
+ Justin
Dan
-- Aeolus Community Manager http://www.aeolusproject.org
On 07/14/2012 12:29 AM, Justin Clift wrote:
On 14/07/2012, at 5:14 AM, Dan Radez wrote:
<snip> > Visual representation of the relationships is a cool idea.
Hmmm, this sounds like the kind of thing well suited to being done client side, using vectors, and using javascript.
Using something like this:
Nifty examples:
http://raphaeljs.com/polar-clock.html http://raphaeljs.com/chart.html
Decent idea? :)
- Justin
Dan
-- Aeolus Community Manager http://www.aeolusproject.org
+1 for raphael.js. It is indeed a decent idea! ;)
Imre
On Sat, 2012-07-14 at 08:29 +1000, Justin Clift wrote:
On 14/07/2012, at 5:14 AM, Dan Radez wrote:
<snip> > Visual representation of the relationships is a cool idea.
Hmmm, this sounds like the kind of thing well suited to being done client side, using vectors, and using javascript.
Using something like this:
Nifty examples:
http://raphaeljs.com/polar-clock.html http://raphaeljs.com/chart.html
Decent idea? :)
Not bad idea, but it's another JS lib, and it wont work when users have disabled JS in browser. Graphviz solution is server-side so it works with disabled JS.
- Justin
Dan
-- Aeolus Community Manager http://www.aeolusproject.org
On 17/07/2012, at 2:12 AM, Jozef Zigmund wrote:
Not bad idea, but it's another JS lib, and it wont work when users have disabled JS in browser. Graphviz solution is server-side so it works with disabled JS.
Heh... when you say "another JS lib" does that mean we require JS already? :)
With graphviz... any idea how cross platform is it?
From memory, it's a pita on non-*nix. :(
+ Justin
-- Aeolus Community Manager http://www.aeolusproject.org
On 07/16/2012 06:12 PM, Jozef Zigmund wrote:
On Sat, 2012-07-14 at 08:29 +1000, Justin Clift wrote:
On 14/07/2012, at 5:14 AM, Dan Radez wrote:
<snip> > Visual representation of the relationships is a cool idea.
Hmmm, this sounds like the kind of thing well suited to being done client side, using vectors, and using javascript.
Using something like this:
Nifty examples:
http://raphaeljs.com/polar-clock.html http://raphaeljs.com/chart.html
Decent idea? :)
Not bad idea, but it's another JS lib, and it wont work when users have disabled JS in browser. Graphviz solution is server-side so it works with disabled JS.
Conductor should be functional w/o JS. But displaying dependency graph doesn't seem to be core functionality requirement - I think it's OK to have this feature only-JS if we decide it's better.
We already use JS lib on logs page - Flot, but I don't think it can be used for rendering directed graphs. What we need is a lib which can render directed graphs where nodes represent services and also can wrap set of nodes inside frame which represents assembly. Maybe something similar to this (but assembly should work as node too): http://www.adp-gmbh.ch/misc/tools/graphviz/cluster.html
If Raphael or other JS lib can be used, I would give it a chance. BTW, now I found this: http://code.google.com/p/canviz/
- Justin
Dan
-- Aeolus Community Manager http://www.aeolusproject.org
On Fri, 2012-07-13 at 15:14 -0400, Dan Radez wrote:
On 07/13/2012 02:22 PM, Jan Provazník wrote:
On 07/04/2012 06:03 PM, jzigmund@redhat.com wrote:
From: Jozef Zigmund jzigmund@redhat.com
src/Gemfile.in | 1 + src/app/controllers/deployables_controller.rb | 13 +++++++ src/app/models/deployable.rb | 1 + src/app/util/deployable_xml.rb | 35 ++++++++++++++++++++ .../deployables/service_dependencies.html.haml | 11 ++++++ src/app/views/deployables/show.html.haml | 1 + src/config/initializers/mime_types.rb | 1 + src/config/locales/en.yml | 1 + src/config/routes.rb | 1 + 9 files changed, 65 insertions(+), 0 deletions(-) create mode 100644 src/app/views/deployables/service_dependencies.html.haml
diff --git a/src/Gemfile.in b/src/Gemfile.in index 1d90a91..7bb3704 100644 --- a/src/Gemfile.in +++ b/src/Gemfile.in @@ -23,6 +23,7 @@ gem 'compass' gem 'compass-960-plugin', :require=> 'ninesixty' gem 'delayed_job', '~> 2.1.4' gem 'paranoia' +gem 'graphviz'
platforms :ruby_18 do gem 'fastercsv' diff --git a/src/app/controllers/deployables_controller.rb b/src/app/controllers/deployables_controller.rb index 3e3b76a..e4d3f91 100644 --- a/src/app/controllers/deployables_controller.rb +++ b/src/app/controllers/deployables_controller.rb @@ -283,6 +283,19 @@ class DeployablesController < ApplicationController redirect_to_original({"deployables_preset_filter" => params[:deployables_preset_filter], "deployables_search" => params[:deployables_search]}) end
def service_dependencies
deployable = Deployable.find(params[:id])
deployable_xml = DeployableXML.new(deployable.xml)
respond_to do |format|
format.html do
render :locals => {:deployable => deployable}
end
format.png do
render :text => deployable_xml.display_digraph
end
end
end
private
def set_header
diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index 9c6aff9..b052b44 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -15,6 +15,7 @@ #
require 'util/conductor' +require 'util/deployable_xml'
class Deployable < ActiveRecord::Base class << self diff --git a/src/app/util/deployable_xml.rb b/src/app/util/deployable_xml.rb index cdbbc39..ab46251 100644 --- a/src/app/util/deployable_xml.rb +++ b/src/app/util/deployable_xml.rb @@ -17,6 +17,7 @@ =begin XML Wrapper objects for the deployable XML format =end +require "graphviz"
class ValidationError < RuntimeError; end
@@ -100,6 +101,8 @@ end
class AssemblyXML
- attr_accessor :root
- def initialize(xmlstr_or_node = "") if xmlstr_or_node.is_a? Nokogiri::XML::Node @root = xmlstr_or_node
@@ -202,6 +205,38 @@ class DeployableXML @relax_file = "#{File.dirname(File.expand_path(__FILE__))}/deployable-rng.xml" end
- def display_digraph
- graph = GraphViz.new(:G, :type => :digraph)
- if has_reference?
draw_nodes(graph)
draw_edges(graph)
- else
draw_nodes(graph)
- end
- graph.output :png => String
- end
- def draw_nodes(graph)
- assemblies.each do |assembly|
graph.add_node(assembly.name)
- end
- end
- def draw_edges(graph)
- assemblies.each do |assembly|
references = assembly.root.xpath(".//reference")
unless references.empty?
references.each do |reference|
graph.add_edge(assembly.name,
reference.attributes['assembly'].value)
end
end
- end
- end
- def has_reference?
not @root.xpath("//reference").empty?
- end
- def name @root["name"] if @root end
diff --git a/src/app/views/deployables/service_dependencies.html.haml b/src/app/views/deployables/service_dependencies.html.haml new file mode 100644 index 0000000..075de04 --- /dev/null +++ b/src/app/views/deployables/service_dependencies.html.haml @@ -0,0 +1,11 @@ += render :partial => 'layouts/admin_nav' +%header.page-header
- .obj_actions
- .button-group
- if check_privilege(Privilege::MODIFY, deployable)
= link_to t('deployables.show.edit'),
edit_polymorphic_path([@catalog, deployable]), :class => 'button', :id => 'edit_button'
= link_to t('deployables.show.edit_xml'),
edit_polymorphic_path([@catalog, deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button'
= button_to t('delete'), polymorphic_path([@catalog,
deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete'
- %h1.catalog_entries= deployable.name
+%p
- = image_tag(service_dependencies_deployable_path(deployable,
:format => :png)) \ No newline at end of file diff --git a/src/app/views/deployables/show.html.haml b/src/app/views/deployables/show.html.haml index a7ee021..d28db60 100644 --- a/src/app/views/deployables/show.html.haml +++ b/src/app/views/deployables/show.html.haml @@ -3,6 +3,7 @@ .obj_actions .button-group - if check_privilege(Privilege::MODIFY, @deployable)
= link_to t(".dependencies_diagram"),
service_dependencies_deployable_path(@deployable, :catalog_id => @catalog, :pushed_count => @pushed_count), :class => 'button' = link_to t('.edit'), edit_polymorphic_path([@catalog, @deployable]), :class => 'button', :id => 'edit_button' = link_to t('.edit_xml'), edit_polymorphic_path([@catalog, @deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' = button_to t('.delete'), polymorphic_path([@catalog, @deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete' diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb index 18ecf33..bd77cce 100644 --- a/src/config/initializers/mime_types.rb +++ b/src/config/initializers/mime_types.rb @@ -20,3 +20,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/png", :png diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml index 9295373..1684721 100644 --- a/src/config/locales/en.yml +++ b/src/config/locales/en.yml @@ -1036,6 +1036,7 @@ en: n_a: "N/A" uuids: "UUIDs" show_hide_uuids: "Show/Hide"
dependencies_diagram: Dependencies Diagram form: choose_xml: Choose Deployable XML file catalog: Catalog
diff --git a/src/config/routes.rb b/src/config/routes.rb index e7f26c5..18ed7f2 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -237,6 +237,7 @@ Conductor::Application.routes.draw do end member do get :definition
endget :service_dependencies end
Hi Jozef, I didn't have time to do a proper review yet, but have a few findings about this so far:
- graphviz has many pkg dependencies (it added 41 new pkgs). Maybe it
could be optional - if graphviz is not installed, one some warning about missing graphviz would be displayed.
- there is also already packaged graphviz-ruby gem - couldn't this be
used? I didn't check this, maybe it's not sufficient, but it could save us some packaging
- it seems that this code generates dependencies between assemblies,
what we need are dependencies between services: so nodes would be services, edges would represent param references, assembly would be represented by a frame around services belonging to this assembly.
I checked deployable xml schema and what confuses me is that it seems that a service param can reference a service _or_ an assembly (according to param definition in app/util/deployable-rng.xml). If a param references assembly, then we can't resolve dependencies between services. Dan, you are most familiar with this - what do you think about it? As I understand it, referencing whole assemblies should be disabled.
Jan
Services can depend on other services with in the same assembly. We don't have code that allows cross assembly references. If you are going to represent relationships do it service to service within an assembly.
What about referencing through return parameter(s) from other assembly? Is it possible to use it ? I've read this in https://www.aeolusproject.org/redmine/projects/aeolus/wiki/Deployable_XML
There was talk to allow cross assembly relationships, but I doubt it will ever come to fruition.
Visual representation of the relationships is a cool idea.
Dan
On 07/13/2012 09:14 PM, Dan Radez wrote:
On 07/13/2012 02:22 PM, Jan Provazník wrote:
On 07/04/2012 06:03 PM, jzigmund@redhat.com wrote:
From: Jozef Zigmund jzigmund@redhat.com
src/Gemfile.in | 1 + src/app/controllers/deployables_controller.rb | 13 +++++++ src/app/models/deployable.rb | 1 + src/app/util/deployable_xml.rb | 35 ++++++++++++++++++++ .../deployables/service_dependencies.html.haml | 11 ++++++ src/app/views/deployables/show.html.haml | 1 + src/config/initializers/mime_types.rb | 1 + src/config/locales/en.yml | 1 + src/config/routes.rb | 1 + 9 files changed, 65 insertions(+), 0 deletions(-) create mode 100644 src/app/views/deployables/service_dependencies.html.haml
diff --git a/src/Gemfile.in b/src/Gemfile.in index 1d90a91..7bb3704 100644 --- a/src/Gemfile.in +++ b/src/Gemfile.in @@ -23,6 +23,7 @@ gem 'compass' gem 'compass-960-plugin', :require=> 'ninesixty' gem 'delayed_job', '~> 2.1.4' gem 'paranoia' +gem 'graphviz'
platforms :ruby_18 do gem 'fastercsv' diff --git a/src/app/controllers/deployables_controller.rb b/src/app/controllers/deployables_controller.rb index 3e3b76a..e4d3f91 100644 --- a/src/app/controllers/deployables_controller.rb +++ b/src/app/controllers/deployables_controller.rb @@ -283,6 +283,19 @@ class DeployablesController < ApplicationController redirect_to_original({"deployables_preset_filter" => params[:deployables_preset_filter], "deployables_search" => params[:deployables_search]}) end
def service_dependencies
deployable = Deployable.find(params[:id])
deployable_xml = DeployableXML.new(deployable.xml)
respond_to do |format|
format.html do
render :locals => {:deployable => deployable}
end
format.png do
render :text => deployable_xml.display_digraph
end
end
end
private
def set_header
diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index 9c6aff9..b052b44 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -15,6 +15,7 @@ #
require 'util/conductor' +require 'util/deployable_xml'
class Deployable < ActiveRecord::Base class << self diff --git a/src/app/util/deployable_xml.rb b/src/app/util/deployable_xml.rb index cdbbc39..ab46251 100644 --- a/src/app/util/deployable_xml.rb +++ b/src/app/util/deployable_xml.rb @@ -17,6 +17,7 @@ =begin XML Wrapper objects for the deployable XML format =end +require "graphviz"
class ValidationError < RuntimeError; end
@@ -100,6 +101,8 @@ end
class AssemblyXML
- attr_accessor :root
- def initialize(xmlstr_or_node = "") if xmlstr_or_node.is_a? Nokogiri::XML::Node @root = xmlstr_or_node
@@ -202,6 +205,38 @@ class DeployableXML @relax_file = "#{File.dirname(File.expand_path(__FILE__))}/deployable-rng.xml" end
- def display_digraph
- graph = GraphViz.new(:G, :type => :digraph)
- if has_reference?
draw_nodes(graph)
draw_edges(graph)
- else
draw_nodes(graph)
- end
- graph.output :png => String
- end
- def draw_nodes(graph)
- assemblies.each do |assembly|
graph.add_node(assembly.name)
- end
- end
- def draw_edges(graph)
- assemblies.each do |assembly|
references = assembly.root.xpath(".//reference")
unless references.empty?
references.each do |reference|
graph.add_edge(assembly.name,
reference.attributes['assembly'].value)
end
end
- end
- end
- def has_reference?
not @root.xpath("//reference").empty?
- end
- def name @root["name"] if @root end
diff --git a/src/app/views/deployables/service_dependencies.html.haml b/src/app/views/deployables/service_dependencies.html.haml new file mode 100644 index 0000000..075de04 --- /dev/null +++ b/src/app/views/deployables/service_dependencies.html.haml @@ -0,0 +1,11 @@ += render :partial => 'layouts/admin_nav' +%header.page-header
- .obj_actions
- .button-group
- if check_privilege(Privilege::MODIFY, deployable)
= link_to t('deployables.show.edit'),
edit_polymorphic_path([@catalog, deployable]), :class => 'button', :id => 'edit_button'
= link_to t('deployables.show.edit_xml'),
edit_polymorphic_path([@catalog, deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button'
= button_to t('delete'), polymorphic_path([@catalog,
deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete'
- %h1.catalog_entries= deployable.name
+%p
- = image_tag(service_dependencies_deployable_path(deployable,
:format => :png)) \ No newline at end of file diff --git a/src/app/views/deployables/show.html.haml b/src/app/views/deployables/show.html.haml index a7ee021..d28db60 100644 --- a/src/app/views/deployables/show.html.haml +++ b/src/app/views/deployables/show.html.haml @@ -3,6 +3,7 @@ .obj_actions .button-group - if check_privilege(Privilege::MODIFY, @deployable)
= link_to t(".dependencies_diagram"),
service_dependencies_deployable_path(@deployable, :catalog_id => @catalog, :pushed_count => @pushed_count), :class => 'button' = link_to t('.edit'), edit_polymorphic_path([@catalog, @deployable]), :class => 'button', :id => 'edit_button' = link_to t('.edit_xml'), edit_polymorphic_path([@catalog, @deployable], :edit_xml => true), :class => 'button', :id => 'edit_xml_button' = button_to t('.delete'), polymorphic_path([@catalog, @deployable]), :method => 'delete', :confirm => "#{t'catalog_entries.show.confirm_delete'}", :class => 'button danger', :id => 'delete' diff --git a/src/config/initializers/mime_types.rb b/src/config/initializers/mime_types.rb index 18ecf33..bd77cce 100644 --- a/src/config/initializers/mime_types.rb +++ b/src/config/initializers/mime_types.rb @@ -20,3 +20,4 @@ # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register "image/svg+xml", :svg +Mime::Type.register "image/png", :png diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml index 9295373..1684721 100644 --- a/src/config/locales/en.yml +++ b/src/config/locales/en.yml @@ -1036,6 +1036,7 @@ en: n_a: "N/A" uuids: "UUIDs" show_hide_uuids: "Show/Hide"
dependencies_diagram: Dependencies Diagram form: choose_xml: Choose Deployable XML file catalog: Catalog
diff --git a/src/config/routes.rb b/src/config/routes.rb index e7f26c5..18ed7f2 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -237,6 +237,7 @@ Conductor::Application.routes.draw do end member do get :definition
endget :service_dependencies end
Hi Jozef, I didn't have time to do a proper review yet, but have a few findings about this so far:
- graphviz has many pkg dependencies (it added 41 new pkgs). Maybe it
could be optional - if graphviz is not installed, one some warning about missing graphviz would be displayed.
- there is also already packaged graphviz-ruby gem - couldn't this be
used? I didn't check this, maybe it's not sufficient, but it could save us some packaging
- it seems that this code generates dependencies between assemblies,
what we need are dependencies between services: so nodes would be services, edges would represent param references, assembly would be represented by a frame around services belonging to this assembly.
I checked deployable xml schema and what confuses me is that it seems that a service param can reference a service _or_ an assembly (according to param definition in app/util/deployable-rng.xml). If a param references assembly, then we can't resolve dependencies between services. Dan, you are most familiar with this - what do you think about it? As I understand it, referencing whole assemblies should be disabled.
Jan
Services can depend on other services with in the same assembly. We don't have code that allows cross assembly references. If you are going to represent relationships do it service to service within an assembly.
There was talk to allow cross assembly relationships, but I doubt it will ever come to fruition.
Hm, from what I know, Audrey now waits with launching services of an assembly until all params for all services of the assembly are gathered. So if assembly1 references assembly2, assembly2 can't reference (directly or indirectly) assembly1 because of deadlock. Isn't it too big limitation?
Anyway, in current situation, conductor should make sure that: - there is no deadlock between services in an assembly - there is no deadlock between whole assemblies Right?
Visual representation of the relationships is a cool idea.
Dan
Hi Jozef, I didn't have time to do a proper review yet, but have a few findings about this so far:
- graphviz has many pkg dependencies (it added 41 new pkgs). Maybe it
could be optional - if graphviz is not installed, one some warning about missing graphviz would be displayed.
Yes this can be added if we decide to use graphviz for displaying the dependencies.
- there is also already packaged graphviz-ruby gem - couldn't this be
used? I didn't check this, maybe it's not sufficient, but it could save us some packaging
It's packaged bindings (Ruby extension) to graphviz, not the rubygem (http://rubygems.org/gems/ruby-graphviz)
In fact we could use the same way as DC is using graphviz. It displays graphs (instance states) throught DOT file. Then we dont need to package anything, just add RPM dependency to graphviz and graphviz-ruby packages.
- it seems that this code generates dependencies between assemblies,
what we need are dependencies between services: so nodes would be services, edges would represent param references, assembly would be represented by a frame around services belonging to this assembly.
The output picture was not described so I've made it as proof of concept.
I checked deployable xml schema and what confuses me is that it seems that a service param can reference a service _or_ an assembly (according to param definition in app/util/deployable-rng.xml).
There is:
<element name="reference"> <attribute name="assembly"><text/></attribute> <choice> <attribute name="parameter"><text/></attribute> <attribute name="service"><text/></attribute> </choice> </element>
If a param references assembly, then we can't resolve dependencies between services.
If you check https://www.aeolusproject.org/redmine/projects/aeolus/wiki/Deployable_XML If I get it right, it can refer to other assembly return parameter(s) what is basically referencing to other assembly's service.
Dan, you are most familiar with this - what do you think about it? As I understand it, referencing whole assemblies should be disabled.
Jan
aeolus-devel@lists.fedorahosted.org