foreman_probing 0.0.1

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.
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