From 12c82281dc172e6ef8e07963c93c767d16a732c6 Mon Sep 17 00:00:00 2001 From: Tristan Ancelet Date: Wed, 11 Dec 2024 10:25:13 -0600 Subject: [PATCH] New changes --- .gitignore | 3 +- lib/manager.rb | 56 ---- lib/rubyqa.rb | 15 +- lib/{ => rubyqa}/host.rb | 9 +- lib/rubyqa/interaction.rb | 51 ++++ lib/rubyqa/manager.rb | 61 +++++ lib/rubyqa/overrides.rb | 22 ++ lib/{ => rubyqa}/resource.rb | 2 +- lib/{ => rubyqa}/runner.rb | 18 +- lib/rubyqa/templates/report.rb | 24 ++ lib/rubyqa/templates/test.rb | 12 + lib/{ => rubyqa}/test.rb | 31 +-- lib/rubyqa/tests/avo.rb | 466 +++++++++++++++++++++++++++++++++ lib/rubyqa/tests/gpc.rb | 166 ++++++++++++ rubyqa.gemspec | 7 +- templates/base.erb | 0 16 files changed, 837 insertions(+), 106 deletions(-) delete mode 100644 lib/manager.rb rename lib/{ => rubyqa}/host.rb (86%) create mode 100644 lib/rubyqa/interaction.rb create mode 100644 lib/rubyqa/manager.rb create mode 100644 lib/rubyqa/overrides.rb rename lib/{ => rubyqa}/resource.rb (99%) rename lib/{ => rubyqa}/runner.rb (89%) create mode 100755 lib/rubyqa/templates/report.rb create mode 100644 lib/rubyqa/templates/test.rb rename lib/{ => rubyqa}/test.rb (69%) create mode 100755 lib/rubyqa/tests/avo.rb create mode 100644 lib/rubyqa/tests/gpc.rb mode change 100644 => 100755 templates/base.erb diff --git a/.gitignore b/.gitignore index e42ba2b..3c7d288 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ * !.gitignore !lib -!lib/* -!tests +!lib/** !rubyqa.gemspec !templates !templates/* diff --git a/lib/manager.rb b/lib/manager.rb deleted file mode 100644 index 7e4d213..0000000 --- a/lib/manager.rb +++ /dev/null @@ -1,56 +0,0 @@ -module RubyQA - class Manager - attr_reader :resources, :hosts - @@tests = Array.new - @@hosts = Array.new - - def self.tests - @@tests - end - - def self.hosts - @@hosts - end - - def self.add_host(host) - @@hosts << host - register_resources(host) - update_resources(host) - end - - def self.register_resources (host) - Resource.all_resources.each do |resource| - host.add_resource(resource) - end - end - - def self.update_resources (host) - host.update_resources - end - - def self.new_test(name, options={}, &test_proc) - if not options.has_key? :disabled - @@tests << Test.new(name, **options, &test_proc) - elsif options[:disabled] != true - @@tests << Test.new(name, **options, &test_proc) - end - end - - def self.run_tests - @@hosts.each do |host| - tests = @@tests.select{|test| test.valid_host(host)} - tests.each do |test| - test.run(host) - end - end - end - - def self.report (template = nil) - if template - puts template.result(binding) - else - puts @@tests.map(&:report).join("\n") - end - end - end -end diff --git a/lib/rubyqa.rb b/lib/rubyqa.rb index 857f207..eac3bbf 100644 --- a/lib/rubyqa.rb +++ b/lib/rubyqa.rb @@ -1,8 +1,11 @@ #!/usr/bin/env ruby -$LOAD_PATH << __dir__ -require 'manager' -require 'resource' -require 'runner' -require 'host' -require 'test' +require_relative 'rubyqa/overrides' +require_relative 'rubyqa/templates/report' +require_relative 'rubyqa/templates/test' +require_relative 'rubyqa/interaction' +require_relative 'rubyqa/manager' +require_relative 'rubyqa/resource' +require_relative 'rubyqa/runner' +require_relative 'rubyqa/host' +require_relative 'rubyqa/test' diff --git a/lib/host.rb b/lib/rubyqa/host.rb similarity index 86% rename from lib/host.rb rename to lib/rubyqa/host.rb index 0c7c335..8c2c724 100644 --- a/lib/host.rb +++ b/lib/rubyqa/host.rb @@ -1,5 +1,4 @@ module RubyQA - require 'net/ssh' class Host DEFAULT_HOSTDATA={ :hostname => "", @@ -45,11 +44,11 @@ module RubyQA def name if @data[:hostname] != "" - return @data[:hostname] - elsif @resources['facts'].data['networking']['hostname'] != "" - return @resources['facts'].data['networking']['hostname'] + @data[:hostname] + elsif @resources['facts'].data['hostname'] != "" + @resources['facts'].data['hostname'] else - return @data[:ip] + self.exec('hostname') end end diff --git a/lib/rubyqa/interaction.rb b/lib/rubyqa/interaction.rb new file mode 100644 index 0000000..68291d1 --- /dev/null +++ b/lib/rubyqa/interaction.rb @@ -0,0 +1,51 @@ +module RubyQA + module Interactions + + def get_string_response(prompt) + puts "#{prompt}: " + + while true + answer = STDIN.gets.strip + + if get_user_verification "Is \"#{answer}\" correct?" + return answer + end + end + end + + def get_user_verification(prompt) + print "#{prompt} (y/n): " + while true + answer = STDIN.gets.strip.downcase + + case answer + when /(y|yes)/ + return true + when /(n|no)/ + return false + else + puts "#{answer} is not a valid option. Please try again" + end + end + end + + def get_integer_response(prompt) + print "#{prompt}: " + while true + answer = STDIN.gets.strip + + if not answer =~ /^[[:digit:]]+$/ + puts "#{answer} is not a valid number. Please try again" + continue + end + + answer = answer.to_i + + if get_user_verification "Is #{answer} correct?" + return answer + end + end + end + + end +end diff --git a/lib/rubyqa/manager.rb b/lib/rubyqa/manager.rb new file mode 100644 index 0000000..fc54546 --- /dev/null +++ b/lib/rubyqa/manager.rb @@ -0,0 +1,61 @@ +module RubyQA + + class Manager + attr_reader :resources, :hosts + @@tests = Array.new + @@hosts = Array.new + + class << self + def tests + @@tests + end + + def hosts + @@hosts + end + + def add_host(host) + @@hosts << host + register_resources(host) + update_resources(host) + end + + def register_resources (host) + Resource.all_resources.each do |resource| + host.add_resource(resource) + end + end + + def update_resources (host) + host.update_resources + end + + def new_test(name, options={}, &test_proc) + if not options.has_key? :disabled + @@tests << Test.new(name, **options, &test_proc) + elsif options[:disabled] != true + @@tests << Test.new(name, **options, &test_proc) + end + end + + def run_tests + @@hosts.each do |host| + tests = @@tests.select{|test| test.valid_host(host)} + tests.each do |test| + test.run(host) + end + end + end + + def report (template = nil) + if template + template.result(binding) + else + @@tests.map(&:report).join("\n") + end + end + end + end + RubyQA::Template::BASE_REPORT_TEMPLATE.def_method(RubyQA::Manager, 'render') + +end diff --git a/lib/rubyqa/overrides.rb b/lib/rubyqa/overrides.rb new file mode 100644 index 0000000..1abf10b --- /dev/null +++ b/lib/rubyqa/overrides.rb @@ -0,0 +1,22 @@ +module RubyQA::Overrides + class ::String + def red () "\e[0;31m#{self}\e[0m" end + def red_background ()"\e[0;41m#{self}\e[0m" end + def green () "\e[0;32m#{self}\e[0m" end + def green_background ()"\e[0;42m#{self}\e[0m" end + def yellow () "\e[0;33m#{self}\e[0m" end + def yellow_background ()"\e[0;43m#{self}\e[0m" end + def blue () "\e[0;34m#{self}\e[0m" end + def blue_background ()"\e[0;44m#{self}\e[0m" end + def magenta () "\e[0;35m#{self}\e[0m" end + def magenta_background ()"\e[0;45m#{self}\e[0m" end + def cyan () "\e[0;36m#{self}\e[0m" end + def cyan_background ()"\e[0;46m#{self}\e[0m" end + def white () "\e[0;37m#{self}\e[0m" end + def white_background ()"\e[0;47m#{self}\e[0m" end + def blinking () "\e[5m#{self}\e[25m" end + def hidden () "\e[8m#{self}\e[28m" end + def bold ()"\e[1m#{self}\e[22m" end + def underline () "\e[4m#{self}\e[24m" end + end +end diff --git a/lib/resource.rb b/lib/rubyqa/resource.rb similarity index 99% rename from lib/resource.rb rename to lib/rubyqa/resource.rb index c5113ff..666cb53 100644 --- a/lib/resource.rb +++ b/lib/rubyqa/resource.rb @@ -89,7 +89,7 @@ module RubyQA # Handles gathering DRBD related information from the drbdadm utility. REQUIREMENTS = { - :cluster => true + cluster: true } def initialize (host) diff --git a/lib/runner.rb b/lib/rubyqa/runner.rb similarity index 89% rename from lib/runner.rb rename to lib/rubyqa/runner.rb index 383baec..1c510bf 100644 --- a/lib/runner.rb +++ b/lib/rubyqa/runner.rb @@ -1,4 +1,5 @@ module RubyQA + require 'base64' class CommandRunner # Handles creating an interface for running arbitrary commands on a host/target # @@ -39,26 +40,17 @@ module RubyQA if @data[:password].empty? raise "Password was not provided for host" end + encoded_command = Base64.encode(command) sudo_command = <$command_file </dev/null -rm $command_file -else -#{command} -fi > sudo-output +echo "#{@data[:password]}" | sudo -S bash -c "echo \"#{encoded_command}\" | base64 -d | bash" EOT - run_command(sudo_command) - run_command('cat sudo-output') + run_command(sudo_command) end end - require 'net/ssh' class SSH_Runner < CommandRunner + require 'net/ssh' def initialize_runner begin if @data[:password] diff --git a/lib/rubyqa/templates/report.rb b/lib/rubyqa/templates/report.rb new file mode 100755 index 0000000..1c08ce2 --- /dev/null +++ b/lib/rubyqa/templates/report.rb @@ -0,0 +1,24 @@ +module RubyQA::Templates + require 'erb' + BASE_REPORT=ERB.new(< +QA Runner: <%= ENV['USER'] %> +QA Tests: <%= Manager.tests.count %> +<% Manager.tests.each do |test| -%> +- "<%= test.name %>" +<% end -%> +QA Hosts: +<% Manager.hosts.each do |host| -%> +- <%= host.name %> (<%= host[:ip] %>) +<% end -%> + +############### +# Tests Begin # +############### + +<% Manager.tests.each do |test| -%> +<%= test.report %> +<% end -%> +ERB diff --git a/lib/rubyqa/templates/test.rb b/lib/rubyqa/templates/test.rb new file mode 100644 index 0000000..b47bf13 --- /dev/null +++ b/lib/rubyqa/templates/test.rb @@ -0,0 +1,12 @@ +module RubyQA::Template + BASE_TEST_TEMPLATE = ERB.new < '-' +============================================== +Test : "<%= @name %>" +<%- if not @description.empty? -%> +Description : "<%= @description %>" +<% end -%> +============================================== +<% @tests.each do |hostname,data| -%> +<%= hostname %> : <%= data[:status] %> <% if data[:context].has_key? :note %> (<%= data[:context][:note] %>) <% end %> +<% end -%> +EOF diff --git a/lib/test.rb b/lib/rubyqa/test.rb similarity index 69% rename from lib/test.rb rename to lib/rubyqa/test.rb index 4fc3d15..851ad81 100644 --- a/lib/test.rb +++ b/lib/rubyqa/test.rb @@ -10,7 +10,7 @@ module RubyQA attr_reader :name, :options, :proc - def initialize (name, options ={}, &test_block) + def initialize (name, options = {}, &test_block) @tests = Hash.new @name = name @options = DEFAULT_OPTIONS.merge(options) @@ -48,9 +48,9 @@ module RubyQA test_status = "N/A" context = Hash.new if @test.call(host, context) - test_status = "PASSED" + test_status = "PASSED".green else - test_status = "FAILED" + test_status = "FAILED".red end @tests[host.name]={ :host => host, @@ -63,28 +63,15 @@ module RubyQA if @tests.count == 0 return end - template = nil if @template.nil? - template = ERB.new < '-' -============================================== -Test : "<%= @name %>" -<% if not @description.empty? -%> -Description : "<%= @description %> -<% end -%> -============================================== -<% @tests.each do |hostname,data| -%> -<%= hostname %> : <%= data[:status] %> <% if data[:context].has_key? :note %> (<%= data[:context][:note] %>) <% end %> -<% end -%> - -EOF - - else - template = @template - end - - template.result(binding) + self.render + else + @template.result(binding) + end end end + RubyQA::Template::BASE_TEST_TEMPLATE.def_method(RubyQA::Test, 'render') + end diff --git a/lib/rubyqa/tests/avo.rb b/lib/rubyqa/tests/avo.rb new file mode 100755 index 0000000..4b558a2 --- /dev/null +++ b/lib/rubyqa/tests/avo.rb @@ -0,0 +1,466 @@ +require 'rubyqa' + +include RubyQA + +require 'time' +require 'English' + +info_template = ERB.new <" +============================================== + +<% @tests.each do |hostname,data| -%> +<%= hostname %> +---------------------------------------------- + Serial: <%= data[:context][:serial] %> + System Date: <%= data[:context][:date] %> + HWCLOCK: <%= data[:context][:hwclock] %> + Timezone: <%= data[:context][:timezone] %> + MM: <%= data[:context][:maintenance_mode] %> + Standby: <%= data[:context][:standby] %> + Gold-Images: +<% data[:context][:gold_images].each do |image| -%> + - <%= image %> +<% end -%> + Full-Backups: +<% data[:context][:full_backups].each do |image| -%> + - <%= image %> +<% end -%> + DRBD Syncs: +<% data[:context][:drbd_syncs].each do |sync_name, sync_data| -%> + - <%= sync_name %> (Sync Status: <%= sync_data['disk_state'] %>) +<% end -%> + +<% end -%> +EOF + +Manager.new_test("Get Information", template: info_template){|host,context| + context[:serial] = host.resources['facts']['values']['dmi']['product']['serial_number'].downcase + context[:date] = Time.parse(host.exec('date').strip) + context[:hwclock] = Time.parse(host.exec('sudo hwclock').strip) + context[:timezone] = host.resources['facts']['values']['timezone'] + context[:gold_images] = host.exec('sudo ls -1 /mnt/gold-images/ | grep tar.gz').split(/\n/).map(&:strip).reject(&:empty?) + context[:full_backups] = host.exec('sudo ls -1 /mnt/kvm_backups/ | grep tar.gz').split(/\n/).map(&:strip).reject(&:empty?) + context[:drbd_syncs] = host.resources['facts']['values']['drbd_status'].sort_by{|name,data| name} + context[:maintenance_mode] = host.exec("sudo pcs property show maintenance-mode | awk '/maint/ { print $2 }'").strip + context[:standby] = host.resources['facts']['values']['corosync']['is_standby'] ? "true" : "false" +} + + +network_template = ERB.new <-' +============================================== +Test : "<%= @name %>" +============================================== +<% @tests.each do |hostname, data| -%> +<%= hostname %>: +---------------------------------------------- +<% data[:context][:interfaces].each do |name,info| -%> + <%= name %> <% if info.has_key? :mode %>(<%= info[:mode] %>)<% end %>: +<% if info[:mode] == "static" -%> +<% if info.has_key? :address -%> + IP: <%= info[:address] %> +<% end -%> +<% if info.has_key? :gateway -%> + Gateway: <%= info[:gateway] %> +<% end -%> +<% if info.has_key? :netmask -%> + Netmask: <%= info[:netmask] %> +<% end -%> +<% if info.has_key? :dns -%> + DNS: <%= info[:dns] %> +<% end -%> +<% end -%> +<% end -%> + +<% end -%> +EOF + +Manager.new_test("Networking Configurations Host", template: network_template) {|host,context| + contents = host.exec('cat /etc/network/interfaces') + interfaces = Hash.new + interface_regex = /iface (?\S+) inet (?\S+)/ + address_regex = /address (?
\S+)/ + netmask_regex = /netmask (?\S+)/ + gateway_regex = /gateway (?\S+)/ + + + current_interface = "" + contents.split(/\n/).each do |line| + case line + when interface_regex + current_interface = $LAST_MATCH_INFO['name'] + mode = $LAST_MATCH_INFO['mode'] + interfaces[current_interface]=Hash.new + interfaces[current_interface][:mode] = mode + + when address_regex + interfaces[current_interface][:address] = $LAST_MATCH_INFO['address'] + + when netmask_regex + interfaces[current_interface][:netmask] = $LAST_MATCH_INFO['netmask'] + + when gateway_regex + interfaces[current_interface][:gateway] = $LAST_MATCH_INFO['gateway'] + end + end + + contents = host.exec('sudo ipmitool lan print') + current_interface="IPMI" + interfaces[current_interface]=Hash.new + mode_regex = /IP Address Source[[:space:]]+:[[:space:]](?\S+)/ + address_regex = /IP Address[[:space:]]+:[[:space:]]+(?
\S+)/ + netmask_regex = /Subnet Mask[[:space:]]+:[[:space:]]+(?\S+)/ + gateway_regex = /Default Gateway IP[[:space:]]+:[[:space:]]+(?\S+)/ + + contents.split(/\n/).each do |line| + case line + when mode_regex + interfaces[current_interface][:mode] = $LAST_MATCH_INFO['mode'].downcase + + when address_regex + interfaces[current_interface][:address] = $LAST_MATCH_INFO['address'] + + when netmask_regex + interfaces[current_interface][:netmask] = $LAST_MATCH_INFO['netmask'] + + when gateway_regex + interfaces[current_interface][:gateway] = $LAST_MATCH_INFO['gateway'] + end + end + + ipconfig_script=<[A-Z]{3}[0-9]+).xml/ +CACHE_MODE_REGEX = /cache=\'(?\S+)\'/ +Manager.new_test("Check VM disk cache is writeback"){|host, context| + pass = true + failed_vms = Array.new + + host.exec("sudo grep cache /etc/libvirt/qemu/*.xml").split(/\n/).each do |line| + name = VM_NAME_REGEX.match(line)['name'] + mode = CACHE_MODE_REGEX.match(line)['mode'] + + if mode != "writeback" + pass = false + failed_vms << name + end + end + + if ! failed_vms.empty? + context[:note] = "failed vms: #{failed_vms.join(",")}" + end + + pass +} + +Manager.new_test("Manual RVC Patch Applied to domain.rb"){|host, context| + pass = true + pass = host.exec("sudo grep Log.error /opt/rvc/lib/rvc/domain.rb") =~ /Log.error[[:space:]]*\{[[:space:]]*e(\.message)?[[:space:]]*\}/ + if not pass + context[:note] = "Manual Patch not applied (sudo sed -i 's/Log.error(e)/Log.error { e }/g' /opt/rvc/lib/rvc/domain.rb)" + end + pass +} + +Manager.new_test("Test IPMI Credentials on self (over network)") {|host,context| + fencenode=host.resources['facts']['values']['pcs_resources']["fence_#{host.name}"]['instance_attributes'] + + pass = true + + test_result = host.exec("set +o history; ipmitool -I lanplus -H #{fencenode["ipaddr"]} -U #{fencenode['userid']} -P #{fencenode['passwd']} chassis status >/dev/null 2>&1 && echo 'yes' || echo 'no'").strip + + case test_result + when 'yes' + pass = true + when 'no' + pass = false + end + + if pass == false + ping_test = host.exec("ping -c 2 #{fencenode["ipaddr"]} >/dev/null 2>&1 && echo 'yes' || echo 'no'").strip + case ping_test + when 'yes' + context[:note] = "IPMI Pingable, Creds Failed" + when 'no' + context[:note] = "IPMI Not Pingable. Possibly unplugged" + end + end + + pass +} + +Manager.new_test("Test IPMI Credentials on partner") {|host,context| + if host.name =~ /red1/ + hostname = host.name.sub('red1','red0') + elsif host.name =~ /red0/ + hostname = host.name.sub('red0','red1') + end + + pass = true + + begin + fencenode=host.resources['facts']['values']['pcs_resources']["fence_#{hostname}"]['instance_attributes'] + rescue + puts "Fact retrieval failed on #{host.name}. Failing test" + return false + end + + test_result = host.exec("set +o history; ipmitool -I lanplus -H #{fencenode["ipaddr"]} -U #{fencenode['userid']} -P #{fencenode['passwd']} chassis status >/dev/null 2>&1 && echo 'yes' || echo 'no'").strip + + case test_result + when 'yes' + pass = true + when 'no' + pass = false + end + + if pass == false + ping_test = host.exec("ping -c 2 #{fencenode["ipaddr"]} >/dev/null 2>&1 && echo 'yes' || echo 'no'").strip + case ping_test + when 'yes' + context[:note] = "Other Node IPMI is pingable, Creds Failed" + when 'no' + context[:note] = "Other Node IPMI is not Pingable. Possibly unplugged" + end + end + + pass +} + +Manager.new_test("No Cluster Irregularities", description: "Nothing Disabled that shouldn't be"){|node,context| + pass = true + disabled_items = Array.new + + ## Stonith + ## .values.pcs_resources.fence_avo00000223red0.meta_attributes["target-role"] + begin + node_stonith_state = node.resources['facts']['values']['pcs_resources']["fence_#{node.name}"]['meta_attributes']['target-role'] + rescue + puts "Fact retrieval failed on #{host.name}. Failing test" + return false + end + + + if node_stonith_state == "Stopped" + disabled_items << "Stonith" + end + + if disabled_items.count !=0 + context[:note] = "Disabled Items: #{disabled_items.join(',')}" + pass = false + end + + pass +} + +Manager.new_test("Puppet Agent disabled (pre-ship only)"){|node,context| + pass = true + + ssl_dir_exists = node.exec('[[ -f $( sudo puppet config print agent_disabled_lockfile ) ]] && echo "yes" || echo "no"') + + case ssl_dir_exists + when /no/ + pass = false + context[:note] = "Before shipping node you need to disable the puppet agent on #{node.name} (sudo puppet agent --disable)" + end + + pass +} + + +Manager.new_test("SSL Directory Cleaned (pre-ship only)"){|node,context| + pass = true + + ssl_dir_exists = node.exec('[[ -d /etc/puppetlabs/puppet/ssl ]] && echo "yes" || echo "no"') + + case ssl_dir_exists + when /yes/ + pass = false + context[:note] = "Before shipping node you need to delete the ssl dir on #{node.name} (sudo rm -rf /etc/puppetlabs/puppet/ssl)" + end + + pass +} + +Manager.new_test("Ensure VMs not running (pre-ship only)"){|host,context| + pass = true + vms = host.exec("sudo virsh list --name").split(/\n/).map(&:strip).sort! + + if vms.count > 0 + pass = false + context[:note] = "VMs are still running: #{vms.join(',')} shut them down before shipping (for VM in #{vms.join(' ')}; do sudo rvc kvm shutdown $VM; done" + end + + pass +} + + +Manager.new_test("Cluster in Pre-Ship Condition (pre-ship only)"){|host,context| + pass = true + maintenance_mode = host.exec("sudo pcs property show maintenance-mode | awk '/maint/ { print $2 }'").strip + standby = host.resources['facts']['values']['corosync']['is_standby'] ? "true" : "false" + + case host.name + when /red1/ + if maintenance_mode =~ /false/ and standby =~ /false/ + context[:note] = "Node is not in standby and cluster is not in maintenance-mode" + pass = false + elsif maintenance_mode =~ /false/ and standby =~ /true/ + context[:note] = "Cluster isn't in maintenance-mode" + pass = false + elsif maintenance_mode =~ /true/ and standby =~ /false/ + context[:note] = "Node isn't in standby" + pass = false + end + + when /red0/ + if maintenance_mode =~ /false/ + context[:note] = "Cluster isn't in maintenance-mode" + pass = false + end + end + + pass +} + diff --git a/lib/rubyqa/tests/gpc.rb b/lib/rubyqa/tests/gpc.rb new file mode 100644 index 0000000..795504a --- /dev/null +++ b/lib/rubyqa/tests/gpc.rb @@ -0,0 +1,166 @@ +#!/usr/bin/ruby + +require 'rubyqa' +require 'erb' + +include RubyQA + +info_template = ERB.new < +============================================== + +<% @tests.each do |hostname,data| -%> +<%= hostname %> +---------------------------------------------- + Serials: <%= data[:context][:serial] %> + System Date: <%= data[:context][:date] %> + HWCLOCK: <%= data[:context][:hwclock] %> + Timezone: <%= data[:context][:timezone] %> + eSocket Status: <%= data[:context][:esocket_status] %> + eSocket Patch: <%= data[:context][:esocket_patch] %> + keystore md5s: +<% data[:context][:keystore_md5s].each do |line| -%> + - <%= line %> +<% end -%> +<% end -%> +EOF +Manager.new_test("Get Information", template: info_template){|host,context| + serial_matcher = /\"(\S+)\"/ + serials = host.exec("sudo facter 2>/dev/null | grep serial | cut -d '>' -f 2 | tr -d ','") + + context[:serial] = serials.scan(serial_matcher).map{|serial| "\"#{serial.first}\""}.join(',') + context[:date] = Time.parse(host.exec('date').strip) + context[:hwclock] = Time.parse(host.exec('sudo hwclock').strip) + context[:timezone] = host.exec('readlink -f /etc/localtime').strip.gsub(/(\/usr\/share|\/etc)\/zoneinfo\//,'') + context[:esocket_status] = host.exec('sudo lxc-attach -n ${HOSTNAME/red?/aci0} -- systemctl is-active esocket').strip + context[:esocket_patch] = host.exec('sudo lxc-attach -n ${HOSTNAME/red?/aci0} -- ls -1 /home/esocket/eSocket.POS').split(/\n/).select{|line| line =~ /patch/}.last + context[:keystore_md5s] = host.exec('sudo lxc-attach -n ${HOSTNAME/red?/aci0} -- md5sum /home/esocket/eSocket.POS/keystore/*').split(/\n/).map(&:strip) +} + +firewall_template = ERB.new < +============================================== +<% @tests.each do |hostname,data| -%> +<%= hostname %> +---------------------------------------------- +<% data[:context][:chains].each do |name, info| -%> + Chain <%= name %> +<% info.each do |rule| -%> + <%= rule[:line] %> +<% end -%> +<% end -%> + +<% end -%> +EOF + +filter_regex = /^target/ +chain_regex = /Chain (?\S+)/ +rule_regex = /(?\S+)[[:space:]]+(?\S+)[[:space:]]+(?\S+)[[:space:]]+(?\S+)[[:space:]]+(?\S+)/ +Manager.new_test("Firewall Forward Rules", template: firewall_template) {|host,context| + host_firewall_rules = host.exec("sudo itptables -L FORWARD") + context[:chains]=Hash.new + + current_chain = "" + host_firewall_rules.split(/\n/).each do |line| + case line + when chain_regex + info = chain_regex.match(line) + current_chain = info['name'] + context[:chains][current_chain] = Array.new + + when filter_regex + next + + when rule_regex + rule = Hash.new + info = rule_regex.match(line) + rule[:target]=info['target'] + rule[:source]=info['source'] + rule[:destination]=info['destination'] + rule[:protocols]=info['protocols'] + rule[:line]=line + context[:chains][current_chain] << rule + end + end +} + +Manager.new_test("Check if hostname is correctly configured") {|host,context| + pass = true + name = host.name + hosts = host.exec("cat /etc/hosts") + hostname = host.exec("cat /etc/hostname").strip + not_configured_in = Array.new + + if not hosts =~ /127\.0\.0\.1.*#{name}/ + pass = false + not_configured_in << "/etc/hosts" + end + + if not hostname =~ /#{name}/ + pass = false + not_configured_in << "/etc/hostname" + end + + if not_configured_in.count > 0 + context[:note] = "Hostname was not configured in #{not_configured_in.join(',')}" + end + + pass +} + +Manager.new_test("Puppet Certs Generated") {|host| + +} + +Manager.new_test("OpenVPN Certs Generated") {|host, context| + listing = host.exec('ls -1 /etc/openvpn/mgmt/easy-rsa/keys/') + files_not_generated = Array.new + pass = true + + if ! listing =~ /my.crt/ + pass = false + files_not_generated << 'my.crt' + end + + if ! listing =~ /my.key/ + pass = false + files_not_generated << 'my.key' + end + + if not pass + context[:note] = "Files not created: #{files_not_generated.join(',')}" + end + + pass + +} + +Manager.new_test("Ensure netcat installed on aci, prx & host"){|host, context| + pass = true + + aci_listing = host.exec("sudo lxc-attach -n ${HOSTNAME/red?/aci0} -- apt list --installed") + prx_listing = host.exec("sudo lxc-attach -n ${HOSTNAME/red?/prx0} -- apt list --installed") + host_listing = host.exec("sudo apt list --installed") + not_installed_on = Array.new + + if not aci_listing =~ /netcat/ + not_installed_on << "aci" + end + + if not prx_listing =~ /netcat/ + not_installed_on << "prx" + end + + if not host_listing =~ /netcat/ + not_installed_on << "host" + end + + if not_installed_on.count > 0 + pass = false + context[:note] = "netcat not installed on #{not_installed_on.join(',')}" + end + + pass +} diff --git a/rubyqa.gemspec b/rubyqa.gemspec index 2b48e76..41ada69 100644 --- a/rubyqa.gemspec +++ b/rubyqa.gemspec @@ -1,3 +1,4 @@ +require 'find' Gem::Specification.new do |s| s.name = "RubyQA" s.version = '0.1.0' @@ -6,5 +7,9 @@ Gem::Specification.new do |s| s.authors = ['Tristan Ancelet'] s.email = 'tristanancelet@yahoo.com' s.homepatge = 'https://git.arcanium.tech/tristan/RubyQA' - s.files = Dir.glob('lib/*.rb') + s.executables += [ 'generate-qa','impromptu-qa'] + s.files = Find::find('lib').select{|file| file =~ /rb/} + + s.add_dependency 'net-ssh', '~> 7.1', '>= 7.1.0' + s.add_dependency 'net-ssh-telnet', '~> 0.3', '>= 0.3.0' end diff --git a/templates/base.erb b/templates/base.erb old mode 100644 new mode 100755