foreman_statistics 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +40 -0
- data/Rakefile +47 -0
- data/app/controllers/concerns/foreman_statistics/parameters/trend.rb +20 -0
- data/app/controllers/foreman_statistics/api/v2/statistics_controller.rb +33 -0
- data/app/controllers/foreman_statistics/api/v2/trends_controller.rb +58 -0
- data/app/controllers/foreman_statistics/react_controller.rb +19 -0
- data/app/controllers/foreman_statistics/statistics_controller.rb +24 -0
- data/app/controllers/foreman_statistics/trends_controller.rb +58 -0
- data/app/helpers/foreman_statistics/trends_helper.rb +53 -0
- data/app/models/concerns/foreman_statistics/compute_resource_decorations.rb +9 -0
- data/app/models/concerns/foreman_statistics/environment_decorations.rb +9 -0
- data/app/models/concerns/foreman_statistics/general_setting_decorations.rb +17 -0
- data/app/models/concerns/foreman_statistics/hostgroup_decorations.rb +9 -0
- data/app/models/concerns/foreman_statistics/model_decorations.rb +9 -0
- data/app/models/concerns/foreman_statistics/operatingsystem_decorations.rb +9 -0
- data/app/models/concerns/foreman_statistics/setting_decorations.rb +9 -0
- data/app/models/foreman_statistics/fact_trend.rb +57 -0
- data/app/models/foreman_statistics/foreman_trend.rb +41 -0
- data/app/models/foreman_statistics/trend.rb +38 -0
- data/app/models/foreman_statistics/trend_counter.rb +11 -0
- data/app/services/foreman_statistics/statistics.rb +21 -0
- data/app/services/foreman_statistics/statistics/base.rb +39 -0
- data/app/services/foreman_statistics/statistics/count_facts.rb +17 -0
- data/app/services/foreman_statistics/statistics/count_hosts.rb +9 -0
- data/app/services/foreman_statistics/statistics/count_numerical_fact_pair.rb +43 -0
- data/app/services/foreman_statistics/statistics/count_puppet_classes.rb +23 -0
- data/app/services/foreman_statistics/trend_importer.rb +63 -0
- data/app/views/foreman_statistics/api/v2/trends/base.json.rabl +4 -0
- data/app/views/foreman_statistics/api/v2/trends/create.json.rabl +3 -0
- data/app/views/foreman_statistics/api/v2/trends/index.json.rabl +3 -0
- data/app/views/foreman_statistics/api/v2/trends/main.json.rabl +5 -0
- data/app/views/foreman_statistics/api/v2/trends/show.json.rabl +3 -0
- data/app/views/foreman_statistics/api/v2/trends/update.json.rabl +3 -0
- data/app/views/foreman_statistics/layouts/application_react.html.erb +16 -0
- data/app/views/foreman_statistics/trends/_empty_data.html.erb +7 -0
- data/app/views/foreman_statistics/trends/_fields.html.erb +7 -0
- data/app/views/foreman_statistics/trends/_form.html.erb +8 -0
- data/app/views/foreman_statistics/trends/_hosts.html.erb +13 -0
- data/app/views/foreman_statistics/trends/edit.html.erb +46 -0
- data/app/views/foreman_statistics/trends/index.html.erb +39 -0
- data/app/views/foreman_statistics/trends/new.html.erb +4 -0
- data/app/views/foreman_statistics/trends/show.html.erb +25 -0
- data/app/views/foreman_statistics/trends/welcome.html.erb +12 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20200605153005_migrate_core_types.rb +15 -0
- data/db/migrate_foreman/20121012170851_create_trends.rb +25 -0
- data/db/migrate_foreman/20121012170936_create_trend_counters.rb +14 -0
- data/db/migrate_foreman/20150202094307_add_range_to_trend_counters.rb +6 -0
- data/db/migrate_foreman/20181031155025_add_trend_counter_created_at_unique_constraint.rb +9 -0
- data/lib/foreman_statistics.rb +4 -0
- data/lib/foreman_statistics/engine.rb +104 -0
- data/lib/foreman_statistics/version.rb +3 -0
- data/lib/tasks/foreman_statistics_tasks.rake +78 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_statistics.po +19 -0
- data/locale/foreman_statistics.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/factories/foreman_statistics_factories.rb +68 -0
- data/test/fixtures/permissions.yml +26 -0
- data/test/fixtures/settings.yml +6 -0
- data/test/functional/foreman_statistics/api/v2/statistics_controller_test.rb +19 -0
- data/test/functional/foreman_statistics/api/v2/trends_controller_test.rb +74 -0
- data/test/functional/foreman_statistics/statistics_controller_test.rb +23 -0
- data/test/functional/foreman_statistics/trends_controller_test.rb +115 -0
- data/test/models/foreman_statistics/trend_counter_test.rb +10 -0
- data/test/models/foreman_statistics/trend_test.rb +22 -0
- data/test/test_plugin_helper.rb +6 -0
- data/test/unit/foreman_statistics/access_permissions_test.rb +16 -0
- data/test/unit/foreman_statistics/statistics_test.rb +82 -0
- data/test/unit/foreman_statistics_test.rb +11 -0
- data/test/unit/tasks/foreman_statistics_tasks_test.rb +205 -0
- metadata +199 -0
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+

|
2
|
+
|
3
|
+
# ForemanStatistics
|
4
|
+
|
5
|
+
*Introdction here*
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
See [How_to_Install_a_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin)
|
10
|
+
for how to install Foreman plugins
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
*Usage here*
|
15
|
+
|
16
|
+
## TODO
|
17
|
+
|
18
|
+
*Todo list here*
|
19
|
+
|
20
|
+
## Contributing
|
21
|
+
|
22
|
+
Fork and send a Pull Request. Thanks!
|
23
|
+
|
24
|
+
## Copyright
|
25
|
+
|
26
|
+
Copyright (c) *year* *your name*
|
27
|
+
|
28
|
+
This program is free software: you can redistribute it and/or modify
|
29
|
+
it under the terms of the GNU General Public License as published by
|
30
|
+
the Free Software Foundation, either version 3 of the License, or
|
31
|
+
(at your option) any later version.
|
32
|
+
|
33
|
+
This program is distributed in the hope that it will be useful,
|
34
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
35
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
36
|
+
GNU General Public License for more details.
|
37
|
+
|
38
|
+
You should have received a copy of the GNU General Public License
|
39
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
40
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ForemanStatistics'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
task default: :test
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rubocop/rake_task'
|
40
|
+
RuboCop::RakeTask.new
|
41
|
+
rescue StandardError => _e
|
42
|
+
puts 'Rubocop not loaded.'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default do
|
46
|
+
Rake::Task['rubocop'].execute
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
module Parameters::Trend
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def trend_params_filter
|
7
|
+
Foreman::ParameterFilter.new(Trend).tap do |filter|
|
8
|
+
filter.permit :fact_value, :fact_name,
|
9
|
+
:name,
|
10
|
+
:trendable_type, :trendable_id,
|
11
|
+
:type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def trend_params
|
17
|
+
self.class.trend_params_filter.filter_params(params, parameter_filter_context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
class StatisticsController < ::Api::V2::BaseController
|
5
|
+
resource_description do
|
6
|
+
api_version 'v2'
|
7
|
+
api_base_url '/foreman_statistics/api'
|
8
|
+
end
|
9
|
+
|
10
|
+
api :GET, '/statistics/', N_('Get statistics')
|
11
|
+
|
12
|
+
def index
|
13
|
+
@os_count = Host.authorized(:view_hosts).count_distribution :operatingsystem
|
14
|
+
@arch_count = Host.authorized(:view_hosts).count_distribution :architecture
|
15
|
+
@env_count = Host.authorized(:view_hosts).count_distribution :environment
|
16
|
+
@klass_count = Host.authorized(:view_hosts).count_habtm 'puppetclass'
|
17
|
+
@cpu_count = FactValue.authorized(:view_facts).my_facts.count_each 'processorcount'
|
18
|
+
@model_count = FactValue.authorized(:view_facts).my_facts.count_each 'manufacturer'
|
19
|
+
@mem_size = FactValue.authorized(:view_facts).my_facts.mem_average 'memorysize'
|
20
|
+
@mem_free = FactValue.authorized(:view_facts).my_facts.mem_average 'memoryfree'
|
21
|
+
@swap_size = FactValue.authorized(:view_facts).my_facts.mem_average 'swapsize'
|
22
|
+
@swap_free = FactValue.authorized(:view_facts).my_facts.mem_average 'swapfree'
|
23
|
+
@mem_totsize = FactValue.authorized(:view_facts).my_facts.mem_sum 'memorysize'
|
24
|
+
@mem_totfree = FactValue.authorized(:view_facts).my_facts.mem_sum 'memoryfree'
|
25
|
+
render :json => { :os_count => @os_count, :arch_count => @arch_count, :swap_size => @swap_size,
|
26
|
+
:env_count => @env_count, :klass_count => @klass_count, :cpu_count => @cpu_count,
|
27
|
+
:model_count => @model_count, :mem_size => @mem_size, :mem_free => @mem_free,
|
28
|
+
:swap_free => @swap_free, :mem_totsize => @mem_totsize, :mem_totfree => @mem_totfree }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
class TrendsController < ::Api::V2::BaseController
|
5
|
+
include ForemanStatistics::Parameters::Trend
|
6
|
+
|
7
|
+
before_action :find_resource, :only => %i[show destroy]
|
8
|
+
|
9
|
+
TRENDABLE_TYPES = %w[
|
10
|
+
Environment Operatingsystem Model FactName Hostgroup
|
11
|
+
ComputeResource
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
resource_description do
|
15
|
+
api_version 'v2'
|
16
|
+
api_base_url '/foreman_statistics/api'
|
17
|
+
end
|
18
|
+
|
19
|
+
api :GET, '/trends/', N_('List of trends counters')
|
20
|
+
def index
|
21
|
+
@trends = resource_scope_for_index
|
22
|
+
end
|
23
|
+
|
24
|
+
api :GET, '/trends/:id/', N_('Show a trend')
|
25
|
+
param :id, :identifier, :required => true
|
26
|
+
def show; end
|
27
|
+
|
28
|
+
api :POST, '/trends/', N_('Create a trend counter')
|
29
|
+
param :trendable_type, TRENDABLE_TYPES, :required => true
|
30
|
+
param :fact_name, String, :required => false
|
31
|
+
param :name, String, :required => false
|
32
|
+
def create
|
33
|
+
@trend = ForemanStatistics::Trend.build_trend(trend_params)
|
34
|
+
if @trend.save
|
35
|
+
process_success
|
36
|
+
else
|
37
|
+
process_resource_error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
api :DELETE, '/trends/:id/', N_('Delete a trend counter')
|
42
|
+
param :id, :identifier, :required => true
|
43
|
+
def destroy
|
44
|
+
process_response @trend.destroy
|
45
|
+
end
|
46
|
+
|
47
|
+
# Overload this method to avoid using search_for method
|
48
|
+
def resource_scope_for_index(options = {})
|
49
|
+
resource_scope(options).paginate(paginate_options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def resource_scope(options = {})
|
53
|
+
@resource_scope ||= scope_for(ForemanStatistics::Trend.types, options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
class ReactController < ::ApplicationController
|
3
|
+
layout 'foreman_statistics/layouts/application_react'
|
4
|
+
|
5
|
+
def index
|
6
|
+
render html: nil, layout: true
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def controller_permission
|
12
|
+
:foreman_statistics
|
13
|
+
end
|
14
|
+
|
15
|
+
def action_permission
|
16
|
+
:view
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
class StatisticsController < ::ApplicationController
|
3
|
+
before_action :find_stat, :only => [:show]
|
4
|
+
|
5
|
+
def index
|
6
|
+
render :json => { :charts => charts.map(&:metadata) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def show
|
10
|
+
render :json => @stat.metadata.merge(:data => @stat.calculate.map(&:values))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def find_stat
|
16
|
+
@stat = charts.detect { |ch| ch.id.to_s == params[:id] }
|
17
|
+
@stat || not_found
|
18
|
+
end
|
19
|
+
|
20
|
+
def charts
|
21
|
+
ForemanStatistics::Statistics.charts(Organization.current.try(:id), Location.current.try(:id))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
class TrendsController < ApplicationController
|
3
|
+
include Parameters::Trend
|
4
|
+
|
5
|
+
before_action :find_resource, :only => %i[show edit update destroy]
|
6
|
+
|
7
|
+
def index
|
8
|
+
@trends = Trend.types.includes(:trendable).sort_by { |e| e.type_name.downcase }.paginate(:page => params[:page], :per_page => params[:per_page])
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
@trend = Trend.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def show
|
16
|
+
render 'foreman_statistics/trends/_empty_data' if @trend.values.joins(:trend_counters).empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def create
|
20
|
+
@trend = Trend.build_trend(trend_params)
|
21
|
+
if @trend.save
|
22
|
+
process_success
|
23
|
+
else
|
24
|
+
process_error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
filter = self.class.trend_params_filter
|
30
|
+
trend_attrs = params[:trend].values.map { |t| filter.filter_params(ActionController::Parameters.new(t), parameter_filter_context, :none) }
|
31
|
+
@trends = Trend.update(params[:trend].keys, trend_attrs).reject { |p| p.errors.empty? }
|
32
|
+
if @trends.empty?
|
33
|
+
process_success
|
34
|
+
else
|
35
|
+
process_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def edit; end
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
if @trend.destroy
|
43
|
+
process_success
|
44
|
+
else
|
45
|
+
process_error
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def count
|
50
|
+
ForemanStatistics::TrendImporter.update!
|
51
|
+
redirect_to trends_url
|
52
|
+
end
|
53
|
+
|
54
|
+
def resource_class
|
55
|
+
ForemanStatistics::Trend
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
module TrendsHelper
|
3
|
+
include ::CommonParametersHelper
|
4
|
+
|
5
|
+
def trendable_types
|
6
|
+
options = { _('Environment') => 'Environment', _('Operating system') => 'Operatingsystem',
|
7
|
+
_('Model') => 'Model', _('Facts') => 'FactName', _('Host group') => 'Hostgroup', _('Compute resource') => 'ComputeResource' }
|
8
|
+
existing = ForemanTrend.types.pluck(:trendable_type)
|
9
|
+
options.delete_if { |_k, v| existing.include?(v) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def trend_days_filter(trend)
|
13
|
+
form_tag trend, :id => 'days_filter', :method => :get, :class => 'form form-inline' do
|
14
|
+
content_tag(:span, (_('Trend of the last %s days.') %
|
15
|
+
select(nil, 'range', 1..Setting[:max_trend], { :selected => range },
|
16
|
+
{ :onchange => "$('#days_filter').submit();$(this).attr('disabled','disabled');;" })).html_safe)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def trend_title(trend)
|
21
|
+
if trend.fact_value.blank?
|
22
|
+
trend.to_label
|
23
|
+
else
|
24
|
+
"#{trend.type_name} - #{trend.to_label}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def chart_data(trend, from = Setting[:max_trend], _to = Time.now.utc)
|
29
|
+
chart_colors = ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE', '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']
|
30
|
+
values = trend.values
|
31
|
+
labels = {}
|
32
|
+
values.includes(:trendable).each { |v| labels[v.id] = [CGI.escapeHTML(v.to_label), trend_path(:id => v)] }
|
33
|
+
values.includes(:trend_counters).where(['trend_counters.interval_end > ? or trend_counters.interval_end is null', from])
|
34
|
+
.reorder('trend_counters.interval_start')
|
35
|
+
.each_with_index.map do |value, idx|
|
36
|
+
data = []
|
37
|
+
value.trend_counters.each do |counter|
|
38
|
+
# cut the left side of the graph
|
39
|
+
interval_start = (counter.interval_start || from) > from ? counter.interval_start : from
|
40
|
+
next_timestamp = counter.try(:interval_end) || Time.now.utc
|
41
|
+
# transform the timestamp values to flot format - from seconds in Ruby to milliseconds in flot
|
42
|
+
data << [interval_start.to_i * 1000, counter.count]
|
43
|
+
data << [next_timestamp.to_i * 1000 - 1, counter.count]
|
44
|
+
end
|
45
|
+
{ :label => labels[value.id][0], :href => labels[value.id][1], :data => data, :color => chart_colors[idx % chart_colors.size] } unless data.empty?
|
46
|
+
end.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
def range
|
50
|
+
params['range'].empty? ? Setting[:max_trend] : params['range'].to_i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ForemanStatistics
|
2
|
+
module GeneralSettingDecorations
|
3
|
+
def self.prepended(base)
|
4
|
+
class << base
|
5
|
+
prepend ClassMethodsPrepend
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethodsPrepend
|
10
|
+
def default_settings
|
11
|
+
super.concat([
|
12
|
+
set('max_trend', N_('Max days for Trends graphs'), 30, N_('Max trends'))
|
13
|
+
])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|