dropsonde 0.0.5 → 0.0.8

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.
@@ -1,14 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # modules plugin
1
4
  class Dropsonde::Metrics::Modules
2
5
  def self.initialize_modules
3
6
  # require any libraries needed here -- no need to load puppet; it's already initialized
4
7
  # All plugins are initialized before any metrics are generated.
8
+ require 'puppet/info_service'
9
+ require 'puppet/info_service/class_information_service'
5
10
  end
6
11
 
7
12
  def self.description
8
- <<~EOF
13
+ <<~DESCRIPTION
9
14
  This group of metrics exports name & version information about the public
10
15
  modules installed in all environments, ignoring private modules.
11
- EOF
16
+ DESCRIPTION
12
17
  end
13
18
 
14
19
  def self.schema
@@ -18,49 +23,89 @@ class Dropsonde::Metrics::Modules
18
23
  {
19
24
  "fields": [
20
25
  {
21
- "description": "The module name",
22
- "mode": "NULLABLE",
23
- "name": "name",
24
- "type": "STRING"
26
+ "description": 'The module name',
27
+ "mode": 'NULLABLE',
28
+ "name": 'name',
29
+ "type": 'STRING',
25
30
  },
26
31
  {
27
- "description": "The module slug (author-name)",
28
- "mode": "NULLABLE",
29
- "name": "slug",
30
- "type": "STRING"
32
+ "description": 'The module slug (author-name)',
33
+ "mode": 'NULLABLE',
34
+ "name": 'slug',
35
+ "type": 'STRING',
31
36
  },
32
37
  {
33
- "description": "The module version",
34
- "mode": "NULLABLE",
35
- "name": "version",
36
- "type": "STRING"
37
- }
38
+ "description": 'The module version',
39
+ "mode": 'NULLABLE',
40
+ "name": 'version',
41
+ "type": 'STRING',
42
+ },
38
43
  ],
39
- "description": "List of modules in all environments.",
40
- "mode": "REPEATED",
41
- "name": "modules",
42
- "type": "RECORD"
44
+ "description": 'List of modules in all environments.',
45
+ "mode": 'REPEATED',
46
+ "name": 'modules',
47
+ "type": 'RECORD',
43
48
  },
44
49
  {
45
50
  "fields": [
46
51
  {
47
- "description": "The class name",
48
- "mode": "NULLABLE",
49
- "name": "name",
50
- "type": "STRING"
52
+ "description": 'The class name',
53
+ "mode": 'NULLABLE',
54
+ "name": 'name',
55
+ "type": 'STRING',
51
56
  },
52
57
  {
53
- "description": "How many nodes it is declared on",
54
- "mode": "NULLABLE",
55
- "name": "count",
56
- "type": "INTEGER"
57
- }
58
+ "description": 'How many nodes it is declared on',
59
+ "mode": 'NULLABLE',
60
+ "name": 'count',
61
+ "type": 'INTEGER',
62
+ },
58
63
  ],
59
- "description": "List of classes and counts in all environments.",
60
- "mode": "REPEATED",
61
- "name": "classes",
62
- "type": "RECORD"
63
- }
64
+ "description": 'List of classes and counts in all environments.',
65
+ "mode": 'REPEATED',
66
+ "name": 'classes',
67
+ "type": 'RECORD',
68
+ },
69
+ {
70
+ "fields": [
71
+ {
72
+ "description": 'The module name',
73
+ "mode": 'NULLABLE',
74
+ "name": 'name',
75
+ "type": 'STRING',
76
+ },
77
+ {
78
+ "description": 'The module slug (author-name)',
79
+ "mode": 'NULLABLE',
80
+ "name": 'slug',
81
+ "type": 'STRING',
82
+ },
83
+ {
84
+ "description": 'The module version',
85
+ "mode": 'NULLABLE',
86
+ "name": 'version',
87
+ "type": 'STRING',
88
+ },
89
+ ],
90
+ "description": 'List of modules whose classes are not declared in any environments.',
91
+ "mode": 'REPEATED',
92
+ "name": 'unused_modules',
93
+ "type": 'RECORD',
94
+ },
95
+ {
96
+ "fields": [
97
+ {
98
+ "description": 'The class name',
99
+ "mode": 'NULLABLE',
100
+ "name": 'name',
101
+ "type": 'STRING',
102
+ },
103
+ ],
104
+ "description": 'List of unused classes in all environments.',
105
+ "mode": 'REPEATED',
106
+ "name": 'unused_classes',
107
+ "type": 'RECORD',
108
+ },
64
109
  ]
65
110
  end
66
111
 
@@ -68,46 +113,76 @@ class Dropsonde::Metrics::Modules
68
113
  # run just before generating this metric
69
114
  end
70
115
 
71
- def self.run
116
+ def self.run(puppetdb_session = nil)
72
117
  # return an array of hashes representing the data to be merged into the combined checkin
73
- environments = Puppet.lookup(:environments).list.map{|e|e.name}
74
- modules = environments.map do |env|
75
- Puppet.lookup(:environments).get(env).modules.map do|mod|
118
+ environments = Puppet.lookup(:environments).list.map { |e| e.name }
119
+ modules = environments.map { |env|
120
+ Puppet.lookup(:environments).get(env).modules.map do |mod|
76
121
  next unless mod.forge_module?
77
122
 
78
123
  {
79
- :name => mod.name,
80
- :slug => mod.forge_slug,
81
- :version => mod.version,
124
+ name: mod.name,
125
+ slug: mod.forge_slug,
126
+ version: mod.version,
82
127
  }
83
128
  end
84
- end.flatten.compact.uniq
129
+ }.flatten.compact.uniq
85
130
 
86
- if Dropsonde.puppetDB
131
+ if puppetdb_session
87
132
  # classes and how many nodes they're enforced on
88
- results = Dropsonde.puppetDB.request( '',
89
- 'resources[type, title] { type = "Class" }'
90
- ).data
133
+ results = puppetdb_session.puppet_db.request('', 'resources[type, title] { type = "Class" }').data
91
134
 
92
135
  # select only classes from public modules.
93
136
  # Use uniq to reduce the iteration over very large datasets
94
- classes = results.uniq.map do |klass|
137
+ classes = results.uniq.map { |klass|
95
138
  title = klass['title']
96
139
  modname = title.split('::').first.downcase
97
- next unless modules.find {|mod| mod[:name] == modname }
140
+ next unless modules.find { |mod| mod[:name] == modname }
98
141
 
99
142
  {
100
- :name => title,
101
- :count => results.count {|row| row['title'] == title},
143
+ name: title,
144
+ count: results.count { |row| row['title'] == title },
102
145
  }
103
- end.compact
146
+ }.compact
147
+
148
+ # now lets get a list of all classes so we can identify which are unused
149
+ infoservice = Puppet::InfoService::ClassInformationService.new
150
+ env_hash = {}
151
+ environments.each do |env|
152
+ manifests = Puppet.lookup(:environments).get(env).modules.reduce([]) do |acc, mod|
153
+ next acc unless mod.forge_module?
154
+
155
+ acc.concat mod.all_manifests
156
+ end
157
+ env_hash[env] = manifests
158
+ end
159
+
160
+ klasses_per_env = infoservice.classes_per_environment(env_hash)
161
+
162
+ installed_classes = klasses_per_env.reduce([]) do |klasses, (_key, env)|
163
+ names = env.reduce([]) do |acc, (_file, contents)|
164
+ acc.concat(contents[:classes].map { |c| c[:name] })
165
+ end
166
+
167
+ klasses.concat names
168
+ end
169
+
170
+ unused_modules = installed_classes.map { |c| c.split('::').first }.sort.uniq
171
+ classes.each { |c| unused_modules.delete(c[:name].split('::').first.downcase) }
172
+
173
+ unused_classes = installed_classes.dup
174
+ classes.each { |c| unused_classes.delete(c[:name].downcase) }
104
175
  else
105
176
  classes = []
177
+ unused_modules = []
178
+ unused_classes = []
106
179
  end
107
180
 
108
181
  [
109
- { :modules => modules },
110
- { :classes => classes },
182
+ { modules: modules },
183
+ { classes: classes },
184
+ { unused_modules: unused_modules },
185
+ { unused_classes: unused_classes },
111
186
  ]
112
187
  end
113
188
 
@@ -119,19 +194,29 @@ class Dropsonde::Metrics::Modules
119
194
  versions = ['1.3.2', '0.0.1', '0.1.2', '1.0.0', '3.0.2', '7.10', '6.1.0', '2.1.0', '1.4.0']
120
195
  classes = ['', '::Config', '::Service', '::Server', '::Client', '::Packages']
121
196
  [
122
- :modules => Dropsonde::Cache.modules
123
- .sample(rand(100))
124
- .map {|item| {
125
- :name => item.split('-').last,
126
- :slug => item,
127
- :version => versions.sample,
128
- }},
129
- :classes => Dropsonde::Cache.modules
130
- .sample(rand(500))
131
- .map {|item| {
132
- :name => item.split('-').last.capitalize + classes.sample,
133
- :count => rand(750),
134
- }},
197
+ modules: Dropsonde::Cache.modules
198
+ .sample(rand(100))
199
+ .map do |item|
200
+ {
201
+ name: item.split('-').last,
202
+ slug: item,
203
+ version: versions.sample,
204
+ }
205
+ end,
206
+ classes: Dropsonde::Cache.modules
207
+ .sample(rand(500))
208
+ .map do |item|
209
+ {
210
+ name: item.split('-').last.capitalize + classes.sample,
211
+ count: rand(750),
212
+ }
213
+ end,
214
+ unused_modules: Dropsonde::Cache.modules
215
+ .sample(rand(500))
216
+ .map { |item| item.split('-').last },
217
+ unused_classes: Dropsonde::Cache.modules
218
+ .sample(rand(500))
219
+ .map { |item| item.split('-').last.capitalize + classes.sample },
135
220
  ]
136
221
  end
137
222
 
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # platforms plugin
1
4
  class Dropsonde::Metrics::Platforms
2
5
  def self.initialize_platforms
3
6
  # require any libraries needed here -- no need to load puppet; it's already initialized
@@ -5,11 +8,11 @@ class Dropsonde::Metrics::Platforms
5
8
  end
6
9
 
7
10
  def self.description
8
- <<~EOF
11
+ <<~DESCRIPTION
9
12
  This group of metrics generates usage patterns by platform.
10
13
  Currently implemented is a list of classes, the platforms
11
14
  they are declared on, and a count of each combination.
12
- EOF
15
+ DESCRIPTION
13
16
  end
14
17
 
15
18
  def self.schema
@@ -19,29 +22,29 @@ class Dropsonde::Metrics::Platforms
19
22
  {
20
23
  "fields": [
21
24
  {
22
- "description": "The class name name",
23
- "mode": "NULLABLE",
24
- "name": "name",
25
- "type": "STRING"
25
+ "description": 'The class name name',
26
+ "mode": 'NULLABLE',
27
+ "name": 'name',
28
+ "type": 'STRING',
26
29
  },
27
30
  {
28
- "description": "The osfamily of the node the class is declared on",
29
- "mode": "NULLABLE",
30
- "name": "platform",
31
- "type": "STRING"
31
+ "description": 'The osfamily of the node the class is declared on',
32
+ "mode": 'NULLABLE',
33
+ "name": 'platform',
34
+ "type": 'STRING',
32
35
  },
33
36
  {
34
- "description": "The number of time this combination is declared",
35
- "mode": "NULLABLE",
36
- "name": "count",
37
- "type": "INTEGER"
37
+ "description": 'The number of time this combination is declared',
38
+ "mode": 'NULLABLE',
39
+ "name": 'count',
40
+ "type": 'INTEGER',
38
41
  },
39
42
  ],
40
43
  "description": "List of all classes in the infrastructure and platforms they're declared on.",
41
- "mode": "REPEATED",
42
- "name": "class_platforms",
43
- "type": "RECORD"
44
- }
44
+ "mode": 'REPEATED',
45
+ "name": 'class_platforms',
46
+ "type": 'RECORD',
47
+ },
45
48
  ]
46
49
  end
47
50
 
@@ -49,44 +52,43 @@ class Dropsonde::Metrics::Platforms
49
52
  # run just before generating this metric
50
53
  end
51
54
 
52
- def self.run
55
+ def self.run(puppetdb_session = nil)
53
56
  # skip this metric if we don't have an active PuppetDB connection
54
- return unless Dropsonde.puppetDB
57
+ return unless puppetdb_session
55
58
 
56
- classes = Dropsonde.puppetDB.request( '', 'resources[certname, title] { type = "Class" }').data
57
- facts = Dropsonde.puppetDB.request( '', 'facts[certname, value] { name = "osfamily" }').data
59
+ classes = puppetdb_session.puppet_db.request('', 'resources[certname, title] { type = "Class" }').data
60
+ facts = puppetdb_session.puppet_db.request('', 'facts[certname, value] { name = "osfamily" }').data
58
61
 
59
62
  # All public Forge modules that are installed.
60
- modules = Puppet.lookup(:environments).list.map {|env|
61
- env.modules.select {|mod|
62
- mod.forge_module?
63
- }.map {|fmod|
64
- fmod.name
65
- }}.flatten.uniq
66
-
67
- data = classes.map {|item|
68
- # filter out any that don't come from public Forge modules
69
- mod = item['title'].split('::').first.downcase
70
- next unless modules.include? mod
71
-
72
- item['platform'] = facts.find {|fact|
73
- fact['certname'] == item['certname']
74
- }['value']
63
+ modules = Puppet.lookup(:environments).list.map { |env|
64
+ env.modules.select { |mod| mod.forge_module? }.map do |fmod|
65
+ fmod.name
66
+ end
67
+ }.flatten.uniq
75
68
 
76
- {
77
- :name => item['title'],
78
- :platform => item['platform'],
79
- }
69
+ data = classes.map { |item|
70
+ # filter out any that don't come from public Forge modules
71
+ mod = item['title'].split('::').first.downcase
72
+ next unless modules.include? mod
73
+
74
+ item['platform'] = facts.find { |fact|
75
+ fact['certname'] == item['certname']
76
+ }['value']
77
+
78
+ {
79
+ name: item['title'],
80
+ platform: item['platform'],
81
+ }
80
82
  }.compact
81
83
 
82
- data.each {|item|
83
- item['count'] = data.select {|i|
84
- i[:name] == item[:name] and i[:platform] == item[:platform]
85
- }.count
86
- }
84
+ data.each do |item|
85
+ item[:count] = data.select { |i|
86
+ i[:name] == item[:name] and i[:platform] == item[:platform]
87
+ }.count
88
+ end
87
89
 
88
90
  [
89
- :class_platforms => data,
91
+ class_platforms: data,
90
92
  ]
91
93
  end
92
94
 
@@ -95,25 +97,25 @@ class Dropsonde::Metrics::Platforms
95
97
  # make it easier to write data aggregation queries without access to the
96
98
  # actual private data that users have submitted.
97
99
 
98
- platforms = ['RedHat', 'Debian', 'Windows', 'Suse', 'FreeBSD', 'Darwin', 'Archlinux', 'AIX']
100
+ platforms = %w[RedHat Debian Windows Suse FreeBSD Darwin Archlinux AIX]
99
101
  classes = ['', '::Config', '::Service', '::Server', '::Client', '::Packages']
100
102
 
101
103
  data = Dropsonde::Cache.modules
102
- .sample(rand(35))
103
- .map do |item|
104
- name = item.split('-').last.capitalize + classes.sample
105
-
106
- rand(5).times.map do
107
- {
108
- :name => name,
109
- :platform => platforms.sample,
110
- :count => rand(1000),
111
- }
112
- end
113
- end.flatten
104
+ .sample(rand(35))
105
+ .map { |item|
106
+ name = item.split('-').last.capitalize + classes.sample
107
+
108
+ rand(5).times.map do
109
+ {
110
+ name: name,
111
+ platform: platforms.sample,
112
+ count: rand(1000),
113
+ }
114
+ end
115
+ }.flatten
114
116
 
115
117
  [
116
- :class_platforms => data.uniq,
118
+ class_platforms: data.uniq,
117
119
  ]
118
120
  end
119
121
 
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # puppetfiles plugin
1
4
  class Dropsonde::Metrics::Puppetfiles
2
5
  def self.initialize_puppetfiles
3
6
  # require any libraries needed here -- no need to load puppet; it's already initialized
@@ -6,10 +9,10 @@ class Dropsonde::Metrics::Puppetfiles
6
9
  end
7
10
 
8
11
  def self.description
9
- <<~EOF
12
+ <<~DESCRIPTION
10
13
  This generates interesting stats about Puppetfiles used in your environments,
11
14
  including whether your Puppetfiles have Ruby code in them.
12
- EOF
15
+ DESCRIPTION
13
16
  end
14
17
 
15
18
  def self.schema
@@ -19,23 +22,23 @@ class Dropsonde::Metrics::Puppetfiles
19
22
  {
20
23
  "fields": [
21
24
  {
22
- "description": "The method name",
23
- "mode": "NULLABLE",
24
- "name": "name",
25
- "type": "STRING"
25
+ "description": 'The method name',
26
+ "mode": 'NULLABLE',
27
+ "name": 'name',
28
+ "type": 'STRING',
26
29
  },
27
30
  {
28
- "description": "How many times is it used",
29
- "mode": "NULLABLE",
30
- "name": "count",
31
- "type": "INTEGER"
32
- }
31
+ "description": 'How many times is it used',
32
+ "mode": 'NULLABLE',
33
+ "name": 'count',
34
+ "type": 'INTEGER',
35
+ },
33
36
  ],
34
- "description": "Ruby methods used in Puppetfiles.",
35
- "mode": "REPEATED",
36
- "name": "puppetfile_ruby_methods",
37
- "type": "RECORD"
38
- }
37
+ "description": 'Ruby methods used in Puppetfiles.',
38
+ "mode": 'REPEATED',
39
+ "name": 'puppetfile_ruby_methods',
40
+ "type": 'RECORD',
41
+ },
39
42
  ]
40
43
  end
41
44
 
@@ -43,30 +46,30 @@ class Dropsonde::Metrics::Puppetfiles
43
46
  # run just before generating this metric
44
47
  end
45
48
 
46
- def self.run
47
- methods = Dir.entries(Puppet.settings[:environmentpath]).map do |entry|
49
+ def self.run(_puppetdb_session = nil)
50
+ methods = Dir.entries(Puppet.settings[:environmentpath]).map { |entry|
48
51
  puppetfile = File.join(Puppet.settings[:environmentpath], entry, 'Puppetfile')
49
52
 
50
53
  next if entry.start_with? '.'
51
54
  next unless File.file? puppetfile
52
55
 
53
56
  tokens = Ripper.sexp(File.read(puppetfile)).flatten
54
- indices = tokens.map.with_index {|a, i| a == :command ? i : nil}.compact
57
+ indices = tokens.map.with_index { |a, i| (a == :command) ? i : nil }.compact
55
58
 
56
- indices.map {|i| tokens[i+2] }
57
- end.flatten.compact
59
+ indices.map { |i| tokens[i + 2] }
60
+ }.flatten.compact
58
61
 
59
- methods.reject! {|name| ['mod', 'forge', 'moduledir'].include? name }
62
+ methods.reject! { |name| %w[mod forge moduledir].include? name }
60
63
 
61
64
  methods = methods.uniq.map do |name|
62
65
  {
63
- :name => name,
64
- :count => methods.count(name),
66
+ name: name,
67
+ count: methods.count(name),
65
68
  }
66
69
  end
67
70
 
68
71
  [
69
- { :puppetfile_ruby_methods => methods },
72
+ { puppetfile_ruby_methods: methods },
70
73
  ]
71
74
  end
72
75
 
@@ -75,13 +78,13 @@ class Dropsonde::Metrics::Puppetfiles
75
78
  # make it easier to write data aggregation queries without access to the
76
79
  # actual private data that users have submitted.
77
80
  [
78
- :puppetfile_ruby_methods => [
79
- {:name => 'require', :count => rand(200)},
80
- {:name => 'each', :count => rand(200)},
81
- {:name => 'puts', :count => rand(200)},
82
- {:name => 'select', :count => rand(200)},
83
- {:name => 'reject', :count => rand(200)},
84
- {:name => 'read', :count => rand(200)},
81
+ puppetfile_ruby_methods: [
82
+ { name: 'require', count: rand(200) },
83
+ { name: 'each', count: rand(200) },
84
+ { name: 'puts', count: rand(200) },
85
+ { name: 'select', count: rand(200) },
86
+ { name: 'reject', count: rand(200) },
87
+ { name: 'read', count: rand(200) },
85
88
  ].shuffle,
86
89
  ]
87
90
  end