dropsonde 0.0.2 → 0.0.6

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,3 +1,6 @@
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
@@ -5,10 +8,10 @@ class Dropsonde::Metrics::Modules
5
8
  end
6
9
 
7
10
  def self.description
8
- <<~EOF
11
+ <<~DESCRIPTION
9
12
  This group of metrics exports name & version information about the public
10
13
  modules installed in all environments, ignoring private modules.
11
- EOF
14
+ DESCRIPTION
12
15
  end
13
16
 
14
17
  def self.schema
@@ -18,49 +21,49 @@ class Dropsonde::Metrics::Modules
18
21
  {
19
22
  "fields": [
20
23
  {
21
- "description": "The module name",
22
- "mode": "NULLABLE",
23
- "name": "name",
24
- "type": "STRING"
24
+ "description": 'The module name',
25
+ "mode": 'NULLABLE',
26
+ "name": 'name',
27
+ "type": 'STRING',
25
28
  },
26
29
  {
27
- "description": "The module slug (author-name)",
28
- "mode": "NULLABLE",
29
- "name": "slug",
30
- "type": "STRING"
30
+ "description": 'The module slug (author-name)',
31
+ "mode": 'NULLABLE',
32
+ "name": 'slug',
33
+ "type": 'STRING',
31
34
  },
32
35
  {
33
- "description": "The module version",
34
- "mode": "NULLABLE",
35
- "name": "version",
36
- "type": "STRING"
37
- }
36
+ "description": 'The module version',
37
+ "mode": 'NULLABLE',
38
+ "name": 'version',
39
+ "type": 'STRING',
40
+ },
38
41
  ],
39
- "description": "List of modules in all environments.",
40
- "mode": "REPEATED",
41
- "name": "modules",
42
- "type": "RECORD"
42
+ "description": 'List of modules in all environments.',
43
+ "mode": 'REPEATED',
44
+ "name": 'modules',
45
+ "type": 'RECORD',
43
46
  },
44
47
  {
45
48
  "fields": [
46
49
  {
47
- "description": "The class name",
48
- "mode": "NULLABLE",
49
- "name": "name",
50
- "type": "STRING"
50
+ "description": 'The class name',
51
+ "mode": 'NULLABLE',
52
+ "name": 'name',
53
+ "type": 'STRING',
51
54
  },
52
55
  {
53
- "description": "How many nodes it is declared on",
54
- "mode": "NULLABLE",
55
- "name": "count",
56
- "type": "INTEGER"
57
- }
56
+ "description": 'How many nodes it is declared on',
57
+ "mode": 'NULLABLE',
58
+ "name": 'count',
59
+ "type": 'INTEGER',
60
+ },
58
61
  ],
59
- "description": "List of classes and counts in all environments.",
60
- "mode": "REPEATED",
61
- "name": "classes",
62
- "type": "RECORD"
63
- }
62
+ "description": 'List of classes and counts in all environments.',
63
+ "mode": 'REPEATED',
64
+ "name": 'classes',
65
+ "type": 'RECORD',
66
+ },
64
67
  ]
65
68
  end
66
69
 
@@ -68,43 +71,73 @@ class Dropsonde::Metrics::Modules
68
71
  # run just before generating this metric
69
72
  end
70
73
 
71
- def self.run
74
+ def self.run(puppetdb_session = nil)
72
75
  # 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|
76
+ environments = Puppet.lookup(:environments).list.map { |e| e.name }
77
+ modules = environments.map { |env|
78
+ Puppet.lookup(:environments).get(env).modules.map do |mod|
76
79
  next unless mod.forge_module?
77
80
 
78
81
  {
79
- :name => mod.name,
80
- :slug => mod.forge_slug,
81
- :version => mod.version,
82
+ name: mod.name,
83
+ slug: mod.forge_slug,
84
+ version: mod.version,
82
85
  }
83
86
  end
84
- end.flatten.compact.uniq
87
+ }.flatten.compact.uniq
85
88
 
86
- if Dropsonde.puppetDB
89
+ if puppetdb_session
87
90
  # classes and how many nodes they're enforced on
88
- results = Dropsonde.puppetDB.request( '',
89
- 'resources[certname, type, title] { type = "Class" }'
90
- ).data
91
+ results = puppetdb_session.puppet_db.request('', 'resources[type, title] { type = "Class" }').data
91
92
 
92
- # select only classes from public modules
93
- classes = results.map do |klass|
94
- next unless modules.find {|mod| mod[:name] == klass['title'].split('::').first.downcase }
93
+ # select only classes from public modules.
94
+ # Use uniq to reduce the iteration over very large datasets
95
+ classes = results.uniq.map { |klass|
96
+ title = klass['title']
97
+ modname = title.split('::').first.downcase
98
+ next unless modules.find { |mod| mod[:name] == modname }
95
99
 
96
100
  {
97
- :name => klass['title'],
98
- :count => results.count {|row| row['title'] == klass['title']},
101
+ name: title,
102
+ count: results.count { |row| row['title'] == title },
99
103
  }
100
- end.compact.uniq
104
+ }.compact
101
105
  else
102
106
  classes = []
103
107
  end
104
108
 
105
109
  [
106
- { :modules => modules },
107
- { :classes => classes },
110
+ { modules: modules },
111
+ { classes: classes },
112
+ ]
113
+ end
114
+
115
+ def self.example
116
+ # this method is used to generate a table filled with randomized data to
117
+ # make it easier to write data aggregation queries without access to the
118
+ # actual private data that users have submitted.
119
+
120
+ 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']
121
+ classes = ['', '::Config', '::Service', '::Server', '::Client', '::Packages']
122
+ dropsonde_cache = Dropsonde::Cache.new('foo', 7, true)
123
+ [
124
+ modules: dropsonde_cache.modules
125
+ .sample(rand(100))
126
+ .map do |item|
127
+ {
128
+ name: item.split('-').last,
129
+ slug: item,
130
+ version: versions.sample,
131
+ }
132
+ end,
133
+ classes: dropsonde_cache.modules
134
+ .sample(rand(500))
135
+ .map do |item|
136
+ {
137
+ name: item.split('-').last.capitalize + classes.sample,
138
+ count: rand(750),
139
+ }
140
+ end,
108
141
  ]
109
142
  end
110
143
 
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ # platforms plugin
4
+ class Dropsonde::Metrics::Platforms
5
+ def self.initialize_platforms
6
+ # require any libraries needed here -- no need to load puppet; it's already initialized
7
+ # All plugins are initialized before any metrics are generated.
8
+ end
9
+
10
+ def self.description
11
+ <<~DESCRIPTION
12
+ This group of metrics generates usage patterns by platform.
13
+ Currently implemented is a list of classes, the platforms
14
+ they are declared on, and a count of each combination.
15
+ DESCRIPTION
16
+ end
17
+
18
+ def self.schema
19
+ # return an array of hashes of a partial schema to be merged into the complete schema
20
+ # See https://cloud.google.com/bigquery/docs/schemas#specifying_a_json_schema_file
21
+ [
22
+ {
23
+ "fields": [
24
+ {
25
+ "description": 'The class name name',
26
+ "mode": 'NULLABLE',
27
+ "name": 'name',
28
+ "type": 'STRING',
29
+ },
30
+ {
31
+ "description": 'The osfamily of the node the class is declared on',
32
+ "mode": 'NULLABLE',
33
+ "name": 'platform',
34
+ "type": 'STRING',
35
+ },
36
+ {
37
+ "description": 'The number of time this combination is declared',
38
+ "mode": 'NULLABLE',
39
+ "name": 'count',
40
+ "type": 'INTEGER',
41
+ },
42
+ ],
43
+ "description": "List of all classes in the infrastructure and platforms they're declared on.",
44
+ "mode": 'REPEATED',
45
+ "name": 'class_platforms',
46
+ "type": 'RECORD',
47
+ },
48
+ ]
49
+ end
50
+
51
+ def self.setup
52
+ # run just before generating this metric
53
+ end
54
+
55
+ def self.run(puppetdb_session = nil)
56
+ # skip this metric if we don't have an active PuppetDB connection
57
+ return unless puppetdb_session
58
+
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
61
+
62
+ # All public Forge modules that are installed.
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
68
+
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
+ }
82
+ }.compact
83
+
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
89
+
90
+ [
91
+ class_platforms: data,
92
+ ]
93
+ end
94
+
95
+ def self.example
96
+ # this method is used to generate a table filled with randomized data to
97
+ # make it easier to write data aggregation queries without access to the
98
+ # actual private data that users have submitted.
99
+
100
+ platforms = %w[RedHat Debian Windows Suse FreeBSD Darwin Archlinux AIX]
101
+ classes = ['', '::Config', '::Service', '::Server', '::Client', '::Packages']
102
+
103
+ dropsonde_cache = Dropsonde::Cache.new('foo', 7, true)
104
+ data = dropsonde_cache.modules
105
+ .sample(rand(35))
106
+ .map { |item|
107
+ name = item.split('-').last.capitalize + classes.sample
108
+
109
+ rand(5).times.map do
110
+ {
111
+ name: name,
112
+ platform: platforms.sample,
113
+ count: rand(1000),
114
+ }
115
+ end
116
+ }.flatten
117
+
118
+ [
119
+ class_platforms: data.uniq,
120
+ ]
121
+ end
122
+
123
+ def self.cleanup
124
+ # run just after generating this metric
125
+ end
126
+ end
@@ -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,46 @@ 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 },
73
+ ]
74
+ end
75
+
76
+ def self.example
77
+ # this method is used to generate a table filled with randomized data to
78
+ # make it easier to write data aggregation queries without access to the
79
+ # actual private data that users have submitted.
80
+ [
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) },
88
+ ].shuffle,
70
89
  ]
71
90
  end
72
91