dropsonde 0.0.2 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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