class Chef::Knife::TidyServerReport
Public Instance Methods
all_environments(org)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 200 def all_environments(org) rest.get("/organizations/#{org}/environments").values end
all_orgs()
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 196 def all_orgs rest.get("organizations").keys end
check_cookbook_list(cb_list, cb, version)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 219 def check_cookbook_list(cb_list, cb, version) if cb_list[cb] cb_list[cb].each do |v| versions_not_satisfied = [] if Gem::Dependency.new("", version).match?("", v) return [v] else versions_not_satisfied.push(v) end if v == cb_list[cb].last ui.warn("Pin of #{cb} #{version} not satisfied by current versions of cookbook: [#{versions_not_satisfied.join(", ")}]") end end else ui.warn("Cookbook #{cb} #{version} is pinned in an environment, but does not exist on the server in this org.") end nil end
check_environment_pins(used_cookbooks, pins, cb_list)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 239 def check_environment_pins(used_cookbooks, pins, cb_list) pins.each do |cb, versions| versions.each do |version| next if version == "<= 0.0.0" if used_cookbooks[cb] # This pinned cookbook is in the used list, now check for a matching version. used_cookbooks[cb].each do |v| if Gem::Dependency.new("", version).match?("", v) break end end result = check_cookbook_list(cb_list, cb, version) used_cookbooks[cb].push(result[0]) if result && !used_cookbooks[cb].include?(result[0]) else # No cookbook version for that pin, look through the full cookbook list for a match result = check_cookbook_list(cb_list, cb, version) used_cookbooks[cb] = result if result end end end used_cookbooks end
cookbook_count(cb_list)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 174 def cookbook_count(cb_list) cb_count_list = {} cb_list.each do |name, versions| cb_count_list[name] = versions.count end cb_count_list end
cookbook_list(org)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 150 def cookbook_list(org) cb_list = {} rest.get("/organizations/#{org}/cookbooks?num_versions=all").each do |name, data| data["versions"].each do |version_hash| version = Gem::Version.new(version_hash["version"]).to_s if cb_list[name] && !cb_list[name].include?(version) cb_list[name].push(version) else cb_list[name] = [version] end end end cb_list end
delete_existing_reports()
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 124 def delete_existing_reports files = Dir[::File.join(tidy.reports_dir, "*.json")] unless files.empty? ui.confirm("You have existing reports in #{tidy.reports_dir}. Remove") FileUtils.rm(files, force: true) end end
ensure_reports_dir!()
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 120 def ensure_reports_dir! Dir.mkdir(tidy.reports_dir) unless Dir.exist?(tidy.reports_dir) end
environment_constraints(org)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 204 def environment_constraints(org) constraints = {} all_environments(org).each do |env| e = rest.get(env) e["cookbook_versions"].each do |cb, version| if constraints[cb] constraints[cb].push(version) unless constraints[cb].include?(version) else constraints[cb] = [version] end end end constraints end
keep_cookbook_versions(cb_list, min)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 165 def keep_cookbook_versions(cb_list, min) retain = {} cb_list.each do |name, versions| keep = versions.sort { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) }.last(min) retain[name] = keep end retain end
nodes_list(org)
click to toggle source
Need the block here to get the search method to invoke multiple searches and aggregate results for result sets over 1k.
# File lib/chef/knife/tidy_server_report.rb, line 134 def nodes_list(org) node_results = [] Chef::Search::Query.new("#{server.root_url}/organizations/#{org}").search( :node, "*:*", filter_result: { "name" => ["name"], "cookbooks" => ["cookbooks"], "ohai_time" => ["ohai_time"], "chef_packages" => ["chef_packages"], } ) do |node| node_results << node end node_results end
run()
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 25 def run ensure_reports_dir! FileUtils.rm_f(server_warnings_file_path) ui.stdout.puts(ui.color("Writing to #{tidy.reports_dir} directory", :magenta)) delete_existing_reports orgs = if config[:org_list] config[:org_list].split(",") else all_orgs end stale_orgs = [] node_threshold = config[:node_threshold].to_i keep_versions = config[:keep_versions].to_i orgs.each do |org| pre_12_3_nodes = [] unconverged_recent_nodes = [] ui.info " Organization: #{org}" cb_list = cookbook_list(org) version_count = cookbook_count(cb_list).sort_by(&:last).reverse.to_h used_cookbooks = {} nodes = nodes_list(org) db_nodes = rest.get("/organizations/#{org}/nodes") unless nodes.length == db_nodes.length ood_message = "Search index is out of date (search returned #{nodes.length} nodes while the database indicates there are #{db_nodes.length} nodes! No action will be taken for #{org}. Perhaps a 'chef-server-ctl reindex' is in order?" ui.error(ood_message) action_needed(ood_message, server_warnings_file_path) next end nodes.each do |node| # If the node hasn't checked in. unless node["chef_packages"] # If the node is under an hour old. if (Time.now.to_i - node["ohai_time"].to_i) < 3600 unconverged_recent_nodes << node["name"] end next end chef_version = Gem::Version.new(node["chef_packages"]["chef"]["version"]) # If the node has checked in within the node_threshold with a client older than 12.3 if chef_version < Gem::Version.new("12.3") && (Time.now.to_i - node["ohai_time"].to_i) <= node_threshold * 86400 pre_12_3_nodes << node["name"] end end nodes.select { |node| !node["cookbooks"].nil? }.each do |node| node["cookbooks"].each do |name, version_hash| version = Gem::Version.new(version_hash["version"]).to_s if used_cookbooks[name] used_cookbooks[name].push(version) unless used_cookbooks[name].include?(version) else used_cookbooks[name] = [version] end end end used_cookbooks = keep_cookbook_versions(cb_list, keep_versions) Chef::Log.debug("Used cookbook list before checking environments: #{used_cookbooks}") pins = environment_constraints(org) used_cookbooks = check_environment_pins(used_cookbooks, pins, cb_list) stale_nodes = [] nodes.each do |n| if (Time.now.to_i - n["ohai_time"].to_i) >= node_threshold * 86400 stale_nodes.push(n["name"]) end end stale_nodes_hash = { 'threshold_days': node_threshold, 'org_total_node_count': nodes.count, 'count': stale_nodes.count, 'list': stale_nodes } stale_orgs.push(org) if stale_nodes.count == nodes.count tidy.write_new_file(unused_cookbooks(used_cookbooks, cb_list), ::File.join(tidy.reports_dir, "#{org}_unused_cookbooks.json"), backup = false) tidy.write_new_file(version_count, ::File.join(tidy.reports_dir, "#{org}_cookbook_count.json"), backup = false) tidy.write_new_file(stale_nodes_hash, ::File.join(tidy.reports_dir, "#{org}_stale_nodes.json"), backup = false) if pre_12_3_nodes.length > 0 pre_12_3_message = "#{pre_12_3_nodes.length} nodes in organization #{org} have converged in the last #{node_threshold} days with a chef-client < 12.3. These nodes' cookbook versions WILL NOT be factored in the stale cookbooks versions report. Continuing with the server cleanup will delete cookbooks in-use by these nodes." ui.warn(pre_12_3_message) action_needed(pre_12_3_message, server_warnings_file_path) end if unconverged_recent_nodes.length > 0 unconverged_recent_message = "#{unconverged_recent_nodes.length} nodes have been created in the last hour that have yet to converge in organization #{org}. These nodes WILL NOT be factored in the stale cookbook versions report. Continuing with the server cleanup will delete cookbooks in-use by these nodes." ui.warn(unconverged_recent_message) action_needed(unconverged_recent_message, server_warnings_file_path) end end completion_message end
unused_cookbooks(used_list, cb_list)
click to toggle source
# File lib/chef/knife/tidy_server_report.rb, line 182 def unused_cookbooks(used_list, cb_list) unused_list = {} cb_list.each do |name, versions| versions.sort! { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) } if used_list[name].nil? # Not in the used list at all (Remove all versions) unused_list[name] = versions elsif used_list[name].sort != versions # Is in the used cookbook list, but version arrays do not match (Find unused versions) unused = versions - used_list[name] - [versions.last] # Don't delete the most recent version as it might not be in a run_list yet. unused_list[name] = unused unless unused.empty? end end unused_list end