foreman_probing 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +102 -0
  4. data/Rakefile +47 -0
  5. data/app/controllers/foreman_probing/api/v2/scans_controller.rb +66 -0
  6. data/app/controllers/foreman_probing/hosts_controller.rb +14 -0
  7. data/app/controllers/foreman_probing/scans_controller.rb +47 -0
  8. data/app/helpers/concerns/foreman_probing/hosts_helper_extensions.rb +9 -0
  9. data/app/helpers/concerns/foreman_probing/subnet_helper_extensions.rb +7 -0
  10. data/app/helpers/foreman_probing/scans_helper.rb +42 -0
  11. data/app/lib/actions/create_subnets.rb +27 -0
  12. data/app/lib/actions/import_host_facts.rb +85 -0
  13. data/app/lib/actions/perform_scan.rb +26 -0
  14. data/app/lib/actions/process_host.rb +18 -0
  15. data/app/lib/actions/process_scan.rb +21 -0
  16. data/app/lib/actions/scan_host.rb +35 -0
  17. data/app/lib/actions/update_probing_facet.rb +44 -0
  18. data/app/models/concerns/foreman_probing/foreman_tasks_task_extensions.rb +9 -0
  19. data/app/models/concerns/foreman_probing/host_extensions.rb +10 -0
  20. data/app/models/concerns/foreman_probing/subnet_extensions.rb +21 -0
  21. data/app/models/foreman_probing/fact_name.rb +11 -0
  22. data/app/models/foreman_probing/port.rb +21 -0
  23. data/app/models/foreman_probing/probing_facet.rb +27 -0
  24. data/app/models/foreman_probing/scan.rb +42 -0
  25. data/app/models/foreman_probing/scan_composer.rb +75 -0
  26. data/app/models/foreman_probing/scan_host.rb +8 -0
  27. data/app/models/foreman_probing/service.rb +14 -0
  28. data/app/models/foreman_probing/targeting/direct.rb +50 -0
  29. data/app/models/foreman_probing/targeting/search.rb +23 -0
  30. data/app/models/foreman_probing/targeting/subnet.rb +17 -0
  31. data/app/models/foreman_probing/targeting/subnet_discovery.rb +12 -0
  32. data/app/models/foreman_probing/targeting.rb +12 -0
  33. data/app/overrides/dashboard/index/sample_override.html.erb.deface +4 -0
  34. data/app/overrides/open_ports_tab.rb +14 -0
  35. data/app/services/foreman_probing/fact_parser.rb +47 -0
  36. data/app/services/foreman_probing/structured_fact_importer.rb +20 -0
  37. data/app/views/dashboard/_foreman_probing_widget.html.erb +2 -0
  38. data/app/views/foreman_probing/api/v2/scans/base.json.rabl +3 -0
  39. data/app/views/foreman_probing/api/v2/scans/index.json.rabl +3 -0
  40. data/app/views/foreman_probing/api/v2/scans/show.json.rabl +3 -0
  41. data/app/views/foreman_probing/hosts/_open_ports.html.erb +19 -0
  42. data/app/views/foreman_probing/hosts/hosts/new_action.html.erb +1 -0
  43. data/app/views/foreman_probing/hosts/new_action.html.erb +1 -0
  44. data/app/views/foreman_probing/layouts/layouts/new_layout.html.erb +0 -0
  45. data/app/views/foreman_probing/layouts/new_layout.html.erb +0 -0
  46. data/app/views/foreman_probing/probing_facets/_open_ports_tab_content.html.erb +6 -0
  47. data/app/views/foreman_probing/probing_facets/_open_ports_tab_title.html.erb +4 -0
  48. data/app/views/foreman_probing/scans/index.html.erb +31 -0
  49. data/app/views/foreman_probing/scans/new.html.erb +51 -0
  50. data/app/views/foreman_probing/scans/show.html.erb +41 -0
  51. data/app/views/foreman_probing/scans/show.js.erb +0 -0
  52. data/app/views/templates/ssh/add_public_keys.erb +22 -0
  53. data/app/views/templates/ssh/register_content_host.erb +34 -0
  54. data/app/views/templates/ssh/register_puppet.erb +19 -0
  55. data/config/routes.rb +28 -0
  56. data/db/migrate/20170401154201_create_foreman_probing_facets.rb +9 -0
  57. data/db/migrate/20170401154714_create_foreman_probing_ports.rb +13 -0
  58. data/db/migrate/20170401154726_create_foreman_probing_services.rb +10 -0
  59. data/db/migrate/20170620180935_create_foreman_probing_scans.rb +15 -0
  60. data/db/migrate/20170701190511_create_foreman_probing_targetings.rb +9 -0
  61. data/db/seeds.d/70-job_templates.rb +7 -0
  62. data/lib/foreman_probing/engine.rb +124 -0
  63. data/lib/foreman_probing/version.rb +3 -0
  64. data/lib/foreman_probing.rb +5 -0
  65. data/locale/Makefile +60 -0
  66. data/locale/en/foreman_probing.po +19 -0
  67. data/locale/foreman_probing.pot +19 -0
  68. data/locale/gemspec.rb +2 -0
  69. data/test/factories/foreman_probing_factories.rb +5 -0
  70. data/test/test_plugin_helper.rb +6 -0
  71. data/test/unit/foreman_probing_test.rb +11 -0
  72. metadata +159 -0
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # ForemanProbing
2
+ A plugin enabling Foreman to detect machines on the network and make the show up
3
+ as Hosts.
4
+
5
+ ## Installation
6
+ The installation is split into four parts. General prerequisites and installing
7
+ Foreman, Smart Proxy and Smart Proxy Dynflow Core plugins
8
+
9
+ ### Prerequisites
10
+ 1. Install nmap
11
+ 2. This assumes the following directory structure
12
+
13
+ ```
14
+ $PROJECT_ROOT
15
+ +- foreman
16
+ +- foreman_probing
17
+ +- smart-proxy-probing
18
+ +- smart_proxy_dynflow
19
+ `- smart-proxy
20
+ ```
21
+
22
+ ### Installing the Foreman plugin
23
+ 1. Clone this repository
24
+ 2. Tell Foreman to load the gem
25
+
26
+ ```shell
27
+ # In Foreman's checkout
28
+ cat <<-END > bundler.d/foreman_probing.rb
29
+ gem 'foreman_probing', :path => '../foreman_probing'
30
+ END
31
+ ```
32
+
33
+ 3. Run `bundle install`
34
+ 4. Run the migrations and seeds
35
+
36
+ ```shell
37
+ bundle exec rake db:migrate
38
+ bundle exec rake db:seed
39
+ ```
40
+
41
+ ### Installing the Smart Proxy plugin
42
+ 1. Clone the `smart-proxy-probing` repository
43
+ 2. Enable the plugin
44
+ ```shell
45
+ # In smart-proxy checkout
46
+ cat <<-END > config/settings.d/probing.yml
47
+ ---
48
+ :enabled: true
49
+ END
50
+
51
+ cat <<-END > bundler.d/smart-proxy-probing.rb
52
+ gem 'smart-proxy-probing', :path => '../smart-proxy-probing'
53
+ END
54
+ ```
55
+ 3. Run `bundle install`
56
+
57
+ ### Installing the Smart Proxy Dynflow Core plugin
58
+ 1. Clone this repository
59
+ 2. Enable the plugin
60
+
61
+ ```shell
62
+ # in smart_proxy_dynflow
63
+ cat <<-END > bundler.d/foreman_probing_core.rb
64
+ gem 'foreman_probing_core', :path => '../foreman_probing'
65
+ END
66
+ ```
67
+
68
+ ## Usage
69
+ 1. Navigate to Monitor > Network scans
70
+ 2. Click Run Scan
71
+ 3. Fill out the form
72
+ 4. Run the scan
73
+ 5. Wait for it to finish
74
+ 6. Observe Hosts created for the scanned machines
75
+ 7. Use Remote Execution or Ansible to manage the hosts
76
+ 8. (optional) Use Remote Execution or Ansible to deploy agents (Puppet, Katello,
77
+ Chef, Salt minion...)
78
+
79
+ ## Ansible roles
80
+ Some of the roles need `foreman_url` parameter to be set. It would be probably best to set this parameter as global and be done with it.
81
+
82
+ ## Contributing
83
+
84
+ Fork and send a Pull Request. Thanks!
85
+
86
+ ## Copyright
87
+
88
+ Copyright (c) *2017* *Adam Ruzicka*
89
+
90
+ This program is free software: you can redistribute it and/or modify
91
+ it under the terms of the GNU General Public License as published by
92
+ the Free Software Foundation, either version 3 of the License, or
93
+ (at your option) any later version.
94
+
95
+ This program is distributed in the hope that it will be useful,
96
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
97
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
98
+ GNU General Public License for more details.
99
+
100
+ You should have received a copy of the GNU General Public License
101
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
102
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ForemanProbing'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+ task default: :test
37
+
38
+ begin
39
+ require 'rubocop/rake_task'
40
+ RuboCop::RakeTask.new
41
+ rescue => _
42
+ puts 'Rubocop not loaded.'
43
+ end
44
+
45
+ task :default do
46
+ Rake::Task['rubocop'].execute
47
+ end
@@ -0,0 +1,66 @@
1
+ module ForemanProbing
2
+ module Api::V2
3
+ class ScansController < ::Api::V2::BaseController
4
+ include ::Api::Version2
5
+ include ::Foreman::Renderer
6
+
7
+ resource_description do
8
+ resource_id 'foreman_probing'
9
+ api_version 'v2'
10
+ api_base_url '/foreman_probing/api'
11
+ end
12
+
13
+ before_action :find_resource, :only => %w{show rerun}
14
+
15
+ def resource_class
16
+ ForemanProbing::Scan
17
+ end
18
+
19
+ api :GET, '/scans', N_('List scans')
20
+ param_group :search_and_pagination, ::Api::V2::BaseController
21
+ def index
22
+ @scans = resource_scope.order(:id => 'desc').paginate(:page => params[:page])
23
+ end
24
+
25
+ api :POST, '/scans', N_('Create a scan')
26
+ param :targeting_type, String, :required => true,
27
+ :desc => N_('Type of targeting, one of %{options}') % { :options => ForemanProbing::ScanComposer::TARGETING_TYPES.join(', ') }
28
+ param :scan_type, String, :required => true, :desc => N_('Type of the scan, one of %{options}') % { :options => %w{TCP UDP ICMP}.join(', ') }
29
+ param :ports, String, :desc => N_('The ports to probe')
30
+ param :proxy_id, :identifier, :required => true, :desc => N_('The smart proxy to run the scan from')
31
+ param :direct, String, :desc => N_('Comma separated list of IPv4 addresses, subnets or ranges')
32
+ param :subnet_id, :identifier, :desc => N_('ID of subnet to scan')
33
+ param :search_query, String, :desc => N_('Scan hosts matching the search query')
34
+ def create
35
+ @composer = ScanComposer.new_from_params(params[:foreman_probing_scan])
36
+ @scan = @composer.compose!
37
+ @scan.save!
38
+ task = ForemanTasks.async_task(::Actions::ForemanProbing::PerformScan,
39
+ @scan,
40
+ @scan.ports)
41
+ @scan.task = task
42
+ @scan.save!
43
+ set_auto_refresh
44
+ redirect_to @scan
45
+ end
46
+
47
+ api :POST, '/scans/:id', N_('Rerun scan')
48
+ param :id, :identifier, :required => true, :desc => N_('ID of scan to rerun')
49
+ def rerun
50
+ composer = ScanComposer.new_from_scan(ForemanProbing::Scan.find(params['id']))
51
+ @scan = composer.compose!
52
+ @scan.save!
53
+ task = ForemanTasks.async_task(::Actions::ForemanProbing::PerformScan,
54
+ @scan,
55
+ @scan.ports)
56
+ @scan.task = task
57
+ @scan.save!
58
+ render :action => 'show'
59
+ end
60
+
61
+ api :GET, '/scans/:id', N_('Show scan')
62
+ param :id, :identifier, :required => true, :desc => N_('ID of scan to show')
63
+ def show; end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ module ForemanProbing
2
+ # Example: Plugin's HostsController inherits from Foreman's HostsController
3
+ class HostsController < ::HostsController
4
+ before_action :find_resource, :only => :open_ports
5
+
6
+ def open_ports
7
+ render :partial => 'open_ports'
8
+ end
9
+
10
+ private
11
+
12
+ define_action_permission ['open_ports'], :view
13
+ end
14
+ end
@@ -0,0 +1,47 @@
1
+ module ForemanProbing
2
+ class ScansController < ::ApplicationController
3
+
4
+ def index
5
+ @scans = resource_base.order(:id => 'desc').paginate(:page => params[:page])
6
+ end
7
+
8
+ def new
9
+ @scan = ForemanProbing::Scan.new
10
+ @scan.target_kind = params.fetch(:target_kind, 'direct')
11
+ @scan.search_query = params[:search_query]
12
+ @scan.targeting = ::ForemanProbing::Targeting.new
13
+ end
14
+
15
+ def create
16
+ @composer = ScanComposer.new_from_params(params[:foreman_probing_scan])
17
+ @scan = @composer.compose!
18
+ @scan.save!
19
+ task = ForemanTasks.async_task(::Actions::ForemanProbing::PerformScan,
20
+ @scan,
21
+ @scan.ports)
22
+ @scan.task = task
23
+ @scan.save!
24
+ set_auto_refresh
25
+ redirect_to @scan
26
+ end
27
+
28
+ def rerun
29
+ composer = ScanComposer.new_from_scan(ForemanProbing::Scan.find(params['id']))
30
+ @scan = composer.compose!
31
+
32
+ render :action => 'new'
33
+ end
34
+
35
+ def show
36
+ @scan = ::ForemanProbing::Scan.find(params['id'])
37
+ set_auto_refresh
38
+ end
39
+
40
+ private
41
+
42
+ def set_auto_refresh
43
+ @auto_refresh = @scan.task.try(:pending?)
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ module ForemanProbing
2
+ module HostsHelperExtensions
3
+ def host_title_actions(*args)
4
+ title_actions(button_group(link_to(_('Probe'), new_foreman_probing_scan_path(:search_query => "name = #{args.first.name}", :target_kind => 'host'), :id => :run_button, :class => 'btn btn-default')))
5
+
6
+ super(*args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module ForemanProbing
2
+ module SubnetHelperExtensions
3
+ def multiple_actions
4
+ super + [[_('Probe'), new_job_invocation_path, false]]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module ForemanProbing
2
+ module ScansHelper
3
+ def scan_task_buttons(scan)
4
+ task = scan.task
5
+ task_authorizer = Authorizer.new(User.current, :collection => [task])
6
+ buttons = []
7
+ buttons << link_to(_('Rerun'), rerun_foreman_probing_scan_path(scan), :class => 'btn btn-default', :title => _('Refresh this page'))
8
+ buttons << link_to(_('Refresh'), {}, :class => 'btn btn-default', :title => _('Refresh this page'))
9
+ # if authorized_for(hash_for_new_job_invocation_path)
10
+ # buttons << link_to(_('Rerun'), rerun_job_invocation_path(:id => job_invocation.id),
11
+ # :class => 'btn btn-default',
12
+ # :title => _('Rerun the job'))
13
+ # end
14
+ if authorized_for(:permission => :view_foreman_tasks, :auth_object => task, :authorizer => task_authorizer)
15
+ buttons << link_to(_('Task'), foreman_tasks_task_path(task),
16
+ :class => 'btn btn-default',
17
+ :title => _('See the task details'))
18
+ end
19
+ if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task, :authorizer => task_authorizer)
20
+ buttons << link_to(_('Cancel Task'), cancel_foreman_tasks_task_path(task),
21
+ :class => 'btn btn-danger',
22
+ :title => _('Try to cancel the task'),
23
+ :disabled => !task.cancellable?,
24
+ :method => :post)
25
+ end
26
+ return buttons
27
+ end
28
+
29
+ def targeting_label(targeting)
30
+ case targeting
31
+ when ::ForemanProbing::Targeting::Search
32
+ "with search query '#{targeting.raw_targets}'"
33
+ when ::ForemanProbing::Targeting::Direct
34
+ "direct '#{targeting.raw_targets}'"
35
+ when ::ForemanProbing::Targeting::SubnetDiscovery
36
+ 'subnet discovery'
37
+ when ::ForemanProbing::Targeting::Subnet
38
+ "subnet #{targeting.subnet.name}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class CreateSubnets < Actions::EntryAction
4
+ middleware.use Actions::Middleware::KeepCurrentUser
5
+ def run
6
+ proxy = SmartProxy.find(input[:proxy_id])
7
+ output[:subnet_ids] = input[:scan][:proxy_output][:local_addresses].map do |str, hash|
8
+ network_addr = IPAddr.new(hash[:addr]).mask(hash[:cidr]).to_s
9
+ subnet = Subnet.where(:network => network_addr, :mask => hash[:netmask]).first
10
+ if subnet.nil?
11
+ subnet = Subnet.new
12
+ subnet.network = network_addr
13
+ subnet.mask = hash[:netmask]
14
+ subnet.name = "#{str} at #{proxy.name}"
15
+ subnet.location_ids = proxy.location_ids
16
+ subnet.organization_ids = proxy.organization_ids
17
+ subnet.save!
18
+ end
19
+ proxy.subnets << subnet
20
+ proxy.save!
21
+ subnet.id
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,85 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class ImportHostFacts < ::Dynflow::Action
4
+ middleware.use Actions::Middleware::KeepCurrentUser
5
+ def run
6
+ facts = input[:facts]
7
+ # If we're not scanning an already existing host and it is down, we don't want to import it to Foreman
8
+ unless (input[:options][:host_id].nil? && facts.fetch(:status, {})[:state] == 'down')
9
+ host = determine_host(facts)
10
+ facts = try_match_interface_names(host, facts)
11
+ facts.delete(:hostnames)
12
+ ::User.as :admin do
13
+ state = host.import_facts(facts)
14
+ output[:state] = state
15
+ output[:facts] = facts
16
+ end
17
+ try_set_subnet!(host)
18
+ host.smart_proxy_ids << input[:proxy_id]
19
+ output[:host_id] = host.id
20
+ output[:hostname] = host.name
21
+ scan = ::ForemanProbing::Scan.find(input[:scan_id])
22
+ host.organization_id ||= scan.organization_ids.first
23
+ host.location_id ||= scan.location_ids.first
24
+ host.scans << scan
25
+ host.save!
26
+ end
27
+ rescue ::Foreman::Exception => e
28
+ # This error is what is thrown by Host#ImportHostAndFacts when
29
+ # the Host is in the build state. This can be refactored once
30
+ # issue #3959 is fixed.
31
+ raise e unless e.code == 'ERF51-9911'
32
+ end
33
+
34
+ private
35
+
36
+ def try_match_interface_names(host, facts)
37
+ names = host.interfaces.where(:mac => facts.fetch(:addresses, {}).fetch(:hwaddr, {}).keys).map(&:identifier)
38
+ if names.count != facts[:addresses][:ipv4].keys.count
39
+ names = facts[:addresses][:ipv4].keys.count.times.map { |i| "unknown#{i}" }
40
+ end
41
+ names.each_with_index do |name, i|
42
+ [:ipv4, :ipv6, :hwaddr].each do |kind|
43
+ addr = facts[:addresses].fetch(kind, {}).keys[i]
44
+ facts[:addresses].fetch(kind, {}).fetch(addr, {})[:identifier] = name
45
+ end
46
+ end
47
+ facts
48
+ end
49
+
50
+ def try_set_subnet!(host)
51
+ return unless host.subnet.nil? # We don't want to redefine already set subnet
52
+ subnet = if input[:subnet_id]
53
+ Subnet.find(input[:subnet_id])
54
+ else
55
+ Subnet.all.find { |subnet| subnet.ipaddr.include? host.ip } # Try to find a defined subnet
56
+ end
57
+ if subnet
58
+ host.location_id = subnet.location_ids.first
59
+ host.organization_id = subnet.organization_ids.first
60
+ host.subnet = subnet
61
+ end
62
+ end
63
+
64
+ def determine_host(facts)
65
+ macs = facts[:addresses].fetch(:hwaddr, {}).keys
66
+ unless macs.empty?
67
+ ifaces = ::Nic::Managed.where(:mac => macs)
68
+ return ifaces.first.host unless ifaces.empty?
69
+ end
70
+ Host::Managed.import_host(determine_hostname(facts).dup)
71
+ end
72
+
73
+ def determine_hostname(facts)
74
+ # Try to use first of its hostnames, fallback to some of its addresses
75
+ if !facts[:hostnames].empty?
76
+ facts[:hostnames].first[:name]
77
+ else
78
+ name = facts[:addresses].map { |_kind, values| values.keys }.flatten.first
79
+ raise 'Cannot determine host name' if name.nil?
80
+ name
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,26 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class PerformScan < Actions::EntryAction
4
+ include ::Actions::Helpers::WithContinuousOutput
5
+ include ::Actions::Helpers::WithDelegatedAction
6
+
7
+ # middleware.do_not_use Dynflow::Middleware::Common::Transaction
8
+ middleware.use Actions::Middleware::KeepCurrentUser
9
+
10
+ def plan(scan, ports, options = {})
11
+ options[:subnet_discovery] = true if scan.targeting.is_a? ::ForemanProbing::Targeting::SubnetDiscovery
12
+ scanned = plan_delegated_action(scan.smart_proxy, 'ForemanProbingCore::Actions::UseProbe',
13
+ :targets => scan.targeting.targets,
14
+ :scan_type => scan.scan_type,
15
+ :ports => ports,
16
+ :options => options)
17
+ plan_action(CreateSubnets, :proxy_id => scan.smart_proxy_id, :scan => scanned.output) if scan.targeting.is_a? ::ForemanProbing::Targeting::SubnetDiscovery
18
+ plan_action(::Actions::ForemanProbing::ProcessScan,
19
+ :scan_id => scan.id,
20
+ :scan => scanned.output,
21
+ :proxy_id => scan.smart_proxy_id,
22
+ :options => options)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class ProcessHost < Actions::EntryAction
4
+ middleware.use Actions::Middleware::KeepCurrentUser
5
+ def plan(input)
6
+ sequence do
7
+ parsed_scan = plan_action(::Actions::ForemanProbing::ImportHostFacts,
8
+ :scan_id => input[:scan_id],
9
+ :facts => input[:facts],
10
+ :proxy_id => input[:proxy_id],
11
+ :options => input[:options])
12
+ plan_action(::Actions::ForemanProbing::UpdateProbingFacet, :parsed_scan => parsed_scan.output)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class ProcessScan < Actions::ActionWithSubPlans
4
+
5
+ def plan(*args)
6
+ plan_self(*args)
7
+ end
8
+
9
+ def create_sub_plans
10
+ input[:scan][:proxy_output][:facts].map do |report|
11
+ trigger(::Actions::ForemanProbing::ProcessHost,
12
+ :scan_id => input[:scan_id],
13
+ :facts => report,
14
+ :proxy_id => input[:proxy_id],
15
+ :options => input[:options] || {})
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class ScanHost < Actions::EntryAction
4
+ middleware.use Actions::Middleware::KeepCurrentUser
5
+ include ::Actions::Helpers::WithDelegatedAction
6
+
7
+ middleware.do_not_use Dynflow::Middleware::Common::Transaction
8
+
9
+ def plan(host, probes, proxy_selector = ::ForemanProbingProxySelector.new, port_overrides = {})
10
+ action_subject(host, :probes => probes.map(&:to_s))
11
+
12
+ hostname = find_ip_or_hostname(host)
13
+ proxy = proxy_selector.determine_proxy(host)
14
+ plan_delegated_action(proxy, ForemanProbingCore::Actions::UseProbe, hostname, probes, port_overrides)
15
+ plan_self
16
+ end
17
+
18
+ private
19
+
20
+ def find_ip_or_hostname(host)
21
+ %w(execution primary provision).each do |flag|
22
+ interface = host.send(flag + '_interface')
23
+ return interface.ip if interface && interface.ip.present?
24
+ end
25
+
26
+ host.interfaces.each do |interface|
27
+ return interface.ip unless interface.ip.blank?
28
+ end
29
+
30
+ return host.fqdn
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ module Actions
2
+ module ForemanProbing
3
+ class UpdateProbingFacet < Actions::EntryAction
4
+
5
+ middleware.use Actions::Middleware::KeepCurrentUser
6
+
7
+ def run
8
+ data = input[:parsed_scan]
9
+ if data.key? :host_id
10
+ host = Host.find(data[:host_id])
11
+ host.probing_facet ||= ::ForemanProbing::ProbingFacet.new
12
+ host.probing_facet.status = data['facts'].fetch('status', {}).fetch('state', 'down')
13
+
14
+ %w(tcp udp).each do |protocol|
15
+ update_ports protocol, host.probing_facet
16
+ end
17
+
18
+ host.probing_facet.save!
19
+ end
20
+ end
21
+
22
+ def rescue_strategy
23
+ Dynflow::Action::Rescue::Skip
24
+ end
25
+
26
+ private
27
+
28
+ def update_ports(protocol, facet)
29
+ protocol_base = input[:parsed_scan]['facts'].fetch(:ports, {}).fetch(protocol, {})
30
+ up, down = protocol_base.partition { |number, value| value['state'] == 'open' }
31
+ .map { |part| part.map(&:first).map(&:to_i) }
32
+ known = facet.scanned_ports.where(:protocol => protocol).pluck(:number)
33
+ to_create = up - known
34
+ to_update = known & up
35
+ to_remove = known & down
36
+ facet.scanned_ports.where(:protocol => protocol, :number => to_remove).delete_all
37
+ facet.scanned_ports.where(:protocol => protocol, :number => to_update).each(&:touch)
38
+ to_create.each do |number|
39
+ facet.scanned_ports << ::ForemanProbing::Port.new(:protocol => protocol, :number => number, :state => protocol_base[number.to_s]['state'])
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ module ForemanProbing
2
+ module ForemanTasksTaskExtensions
3
+ def self.prepended(base)
4
+ base.instance_eval do
5
+ has_one :scan, :dependent => :nullify, :foreign_key => 'task_id', :class_name => '::ForemanProbing::ScanHost'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module ForemanProbing
2
+ module HostExtensions
3
+ def self.prepended(base)
4
+ base.instance_eval do
5
+ has_many :scan_hosts, :class_name => '::ForemanProbing::ScanHost', :foreign_key => :host_id
6
+ has_many :scans, :through => :scan_hosts, :class_name => '::ForemanProbing::Scan'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module ForemanProbing
2
+ module SubnetExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # execute callbacks
7
+ has_many :probing_proxies, :dependent => :destroy
8
+ end
9
+
10
+ # create or overwrite instance methods...
11
+ def probe!(probe)
12
+
13
+ end
14
+
15
+ module ClassMethods
16
+ # create or overwrite class methods...
17
+ def class_method_name
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module ForemanProbing
2
+ class FactName < ::FactName
3
+ # Define the class that fact names that come from Ansible should have
4
+ # It allows us to filter facts by origin, and also to display the origin
5
+ # in the fact values table (/fact_values)
6
+ def origin
7
+ 'foreman_probing/Network Scan'
8
+ end
9
+
10
+ end
11
+ end