foreman_ansible 6.3.3 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
- data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
- data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
- data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
- data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
- data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
- data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
- data/app/graphql/types/ansible_role.rb +9 -0
- data/app/graphql/types/ansible_variable.rb +23 -0
- data/app/graphql/types/ansible_variable_override.rb +9 -0
- data/app/graphql/types/inherited_ansible_role.rb +13 -0
- data/app/graphql/types/overriden_ansible_variable.rb +27 -0
- data/app/helpers/foreman_ansible/ansible_reports_helper.rb +35 -54
- data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
- data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +1 -0
- data/app/models/foreman_ansible/ansible_provider.rb +56 -6
- data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
- data/app/services/foreman_ansible/inventory_creator.rb +1 -1
- data/app/services/foreman_ansible/override_resolver.rb +22 -0
- data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
- data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
- data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
- data/app/views/foreman_ansible/config_reports/_ansible.html.erb +14 -5
- data/app/views/foreman_ansible/job_templates/ansible_roles_-_ansible_default.erb +4 -0
- data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
- data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
- data/lib/foreman_ansible/engine.rb +0 -18
- data/lib/foreman_ansible/register.rb +114 -2
- data/lib/foreman_ansible/version.rb +1 -1
- data/package.json +10 -6
- data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
- data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
- data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
- data/test/unit/ansible_provider_test.rb +3 -6
- data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
- data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
- data/test/unit/helpers/ansible_reports_helper_test.rb +4 -30
- data/test/unit/services/override_resolver_test.rb +34 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +59 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +6 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +20 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
- data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
- data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
- data/webpack/components/AnsibleHostDetail/constants.js +9 -0
- data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
- data/webpack/components/AnsibleHostDetail/index.js +6 -0
- data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsiblePermissionDenied.test.js.snap +2 -0
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AssignedRolesList.test.js.snap +4 -4
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -0
- data/webpack/components/DualList/DualList.scss +3 -0
- data/webpack/components/DualList/ListControls.js +65 -0
- data/webpack/components/DualList/ListHeader.js +16 -0
- data/webpack/components/DualList/ListItem.js +69 -0
- data/webpack/components/DualList/ListPane.js +95 -0
- data/webpack/components/DualList/SelectedStatus.js +21 -0
- data/webpack/components/DualList/index.js +103 -0
- data/webpack/components/ErrorState.js +16 -0
- data/webpack/components/withLoading.js +135 -0
- data/webpack/components/withPagination.js +0 -0
- data/webpack/formHelper.js +131 -0
- data/webpack/globalIdHelper.js +13 -0
- data/webpack/global_index.js +18 -0
- data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
- data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
- data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
- data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
- data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
- data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
- data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
- data/webpack/graphql/queries/ansibleRoles.gql +13 -0
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
- data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
- data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
- data/webpack/graphql/queries/recurringJobs.gql +28 -0
- data/webpack/helpers/pageParamsHelper.js +40 -0
- data/webpack/helpers/paginationHelper.js +9 -0
- data/webpack/permissionsHelper.js +58 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
- data/webpack/routes/HostgroupJobs/index.js +26 -0
- data/webpack/routes/routes.js +10 -0
- data/webpack/testHelper.js +165 -0
- data/webpack/toastHelper.js +4 -0
- metadata +130 -78
- data/app/assets/images/foreman_ansible/Ansible.png +0 -0
- data/app/models/foreman_ansible/fact_name.rb +0 -16
- data/app/models/setting/ansible.rb +0 -106
- data/app/services/foreman_ansible/fact_importer.rb +0 -99
- data/app/services/foreman_ansible/fact_parser.rb +0 -126
- data/app/services/foreman_ansible/fact_sparser.rb +0 -37
- data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
- data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
- data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +0 -51
- data/test/unit/lib/foreman_ansible_core/command_creator_test.rb +0 -64
- data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +0 -110
- data/test/unit/services/fact_importer_test.rb +0 -52
- data/test/unit/services/fact_parser_test.rb +0 -281
- data/test/unit/services/fact_sparser_test.rb +0 -24
- data/test/unit/services/structured_fact_importer_test.rb +0 -30
- data/webpack/__mocks__/foremanReact/common/I18n.js +0 -1
- data/webpack/__mocks__/foremanReact/common/helpers.js +0 -13
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
- data/webpack/__mocks__/foremanReact/components/common/forms/OrderableSelect/helpers.js +0 -5
- data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
- data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
@@ -1,126 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ForemanAnsible
|
4
|
-
# Override methods from Foreman app/services/fact_parser so that facts
|
5
|
-
# representing host properties are understood when they come from Ansible.
|
6
|
-
class FactParser < ::FactParser
|
7
|
-
include OperatingSystemParser
|
8
|
-
attr_reader :facts
|
9
|
-
|
10
|
-
def initialize(facts)
|
11
|
-
@facts = HashWithIndifferentAccess.new(facts[:ansible_facts])
|
12
|
-
end
|
13
|
-
|
14
|
-
# Don't do anything as there's no env in Ansible
|
15
|
-
def environment; end
|
16
|
-
|
17
|
-
def architecture
|
18
|
-
name = facts[:ansible_architecture] || facts[:facter_architecture]
|
19
|
-
Architecture.where(:name => name).first_or_create if name.present?
|
20
|
-
end
|
21
|
-
|
22
|
-
def model
|
23
|
-
name = detect_fact([:ansible_product_name, :facter_virtual,
|
24
|
-
:facter_productname, :facter_model, :model])
|
25
|
-
Model.where(:name => name.strip).first_or_create if name.present?
|
26
|
-
end
|
27
|
-
|
28
|
-
def domain
|
29
|
-
name = detect_fact([:ansible_domain, :facter_domain,
|
30
|
-
:ohai_domain, :domain])
|
31
|
-
Domain.where(:name => name).first_or_create if name.present?
|
32
|
-
end
|
33
|
-
|
34
|
-
def support_interfaces_parsing?
|
35
|
-
true
|
36
|
-
end
|
37
|
-
|
38
|
-
# Move ansible's default interface first in the list of interfaces since
|
39
|
-
# Foreman picks the first one that is usable. If ansible has no
|
40
|
-
# preference otherwise at least sort the list.
|
41
|
-
#
|
42
|
-
# This method overrides app/services/fact_parser.rb on Foreman and returns
|
43
|
-
# an array of interface names, ['eth0', 'wlan1', etc...]
|
44
|
-
def get_interfaces # rubocop:disable Naming/AccessorMethodName
|
45
|
-
pref = facts[:ansible_default_ipv4] &&
|
46
|
-
facts[:ansible_default_ipv4]['interface']
|
47
|
-
if pref.present?
|
48
|
-
(facts[:ansible_interfaces] - [pref]).unshift(pref)
|
49
|
-
else
|
50
|
-
ansible_interfaces
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def get_facts_for_interface(iface_name)
|
55
|
-
interface = iface_name.tr('-', '_') # virbr1-nic -> virbr1_nic
|
56
|
-
interface_facts = facts[:"ansible_#{interface}"]
|
57
|
-
ipaddress = ip_from_interface(interface)
|
58
|
-
ipaddress6 = ipv6_from_interface(interface)
|
59
|
-
macaddress = mac_from_interface(interface)
|
60
|
-
iface_facts = HashWithIndifferentAccess[
|
61
|
-
interface_facts.merge(:ipaddress => ipaddress,
|
62
|
-
:ipaddress6 => ipaddress6,
|
63
|
-
:macaddress => macaddress)
|
64
|
-
]
|
65
|
-
logger.debug { "Ansible interface #{interface} facts: #{iface_facts.inspect}" }
|
66
|
-
iface_facts
|
67
|
-
end
|
68
|
-
|
69
|
-
def ipmi_interface; end
|
70
|
-
|
71
|
-
def boot_timestamp
|
72
|
-
Time.zone.now.to_i - facts['ansible_uptime_seconds'].to_i
|
73
|
-
end
|
74
|
-
|
75
|
-
def virtual
|
76
|
-
facts['ansible_virtualization_role'] == 'guest'
|
77
|
-
end
|
78
|
-
|
79
|
-
def ram
|
80
|
-
facts['ansible_memtotal_mb'].to_i
|
81
|
-
end
|
82
|
-
|
83
|
-
def sockets
|
84
|
-
facts['ansible_processor_count'].to_i
|
85
|
-
end
|
86
|
-
|
87
|
-
def cores
|
88
|
-
facts['ansible_processor_cores'].to_i
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
def ansible_interfaces
|
94
|
-
return [] if facts[:ansible_interfaces].blank?
|
95
|
-
facts[:ansible_interfaces].sort
|
96
|
-
end
|
97
|
-
|
98
|
-
def mac_from_interface(interface)
|
99
|
-
facts[:"ansible_#{interface}"]['perm_macaddress'].presence || facts[:"ansible_#{interface}"]['macaddress']
|
100
|
-
end
|
101
|
-
|
102
|
-
def ip_from_interface(interface)
|
103
|
-
return if facts[:"ansible_#{interface}"]['ipv4'].blank?
|
104
|
-
if facts[:"ansible_#{interface}"]['ipv4'].is_a?(Array)
|
105
|
-
facts[:"ansible_#{interface}"]['ipv4'][0]['address']
|
106
|
-
else
|
107
|
-
facts[:"ansible_#{interface}"]['ipv4']['address']
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def ipv6_from_interface(interface)
|
112
|
-
return if facts[:"ansible_#{interface}"]['ipv6'].blank?
|
113
|
-
|
114
|
-
facts[:"ansible_#{interface}"]['ipv6'].first['address']
|
115
|
-
end
|
116
|
-
|
117
|
-
# Returns first non-empty fact. Needed to check for empty strings.
|
118
|
-
def detect_fact(fact_names)
|
119
|
-
facts[
|
120
|
-
fact_names.detect do |fact_name|
|
121
|
-
facts[fact_name].present?
|
122
|
-
end
|
123
|
-
]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ForemanAnsible
|
4
|
-
# See sparse and unsparse documentation
|
5
|
-
class FactSparser
|
6
|
-
class << self
|
7
|
-
# Sparses facts, so that it converts a facts hash
|
8
|
-
# { operatingsystem : { major: 20, name : 'fedora' }
|
9
|
-
# into
|
10
|
-
# { operatingsystem::major: 20,
|
11
|
-
# operatingsystem::name: 'fedora' }
|
12
|
-
def sparse(hash, options = {})
|
13
|
-
hash.map do |k, v|
|
14
|
-
prefix = options.fetch(:prefix, []) + [k]
|
15
|
-
next sparse(v, options.merge(:prefix => prefix)) if v.is_a? Hash
|
16
|
-
{ prefix.join(options.fetch(:separator, FactName::SEPARATOR)) => v }
|
17
|
-
end.reduce(:merge) || {}
|
18
|
-
end
|
19
|
-
|
20
|
-
# Unsparses facts, so that it converts a hash with facts
|
21
|
-
# { operatingsystem::major: 20,
|
22
|
-
# operatingsystem::name: 'fedora' }
|
23
|
-
# into
|
24
|
-
# { operatingsystem : { major: 20, name: 'fedora' } }
|
25
|
-
def unsparse(facts_hash)
|
26
|
-
ret = {}
|
27
|
-
sparse(facts_hash).each do |full_name, value|
|
28
|
-
current = ret
|
29
|
-
fact_name = full_name.to_s.split(FactName::SEPARATOR)
|
30
|
-
current = (current[fact_name.shift] ||= {}) until fact_name.size <= 1
|
31
|
-
current[fact_name.first] = value
|
32
|
-
end
|
33
|
-
ret
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ForemanAnsible
|
4
|
-
# Methods to parse facts related to the OS
|
5
|
-
module OperatingSystemParser
|
6
|
-
def operatingsystem
|
7
|
-
args = { :name => os_name, :major => os_major, :minor => os_minor }
|
8
|
-
args[:release_name] = os_release_name if os_name == 'Debian' || os_name == 'Ubuntu'
|
9
|
-
return @local_os if local_os(args).present?
|
10
|
-
return @new_os if new_os(args).present?
|
11
|
-
logger.debug do
|
12
|
-
'Ansible facts parser: No OS could be created with '\
|
13
|
-
"os_name='#{os_name}' os_major='#{os_major}' "\
|
14
|
-
"os_minor='#{os_minor}': "\
|
15
|
-
"#{@new_os.errors if @new_os.present?}"
|
16
|
-
end
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def local_os(args)
|
21
|
-
@local_os = Operatingsystem.where(args).first
|
22
|
-
end
|
23
|
-
|
24
|
-
def new_os(args)
|
25
|
-
return @new_os if @new_os.present?
|
26
|
-
@new_os = Operatingsystem.new(args.merge(:description => os_description))
|
27
|
-
@new_os if @new_os.valid? && @new_os.save
|
28
|
-
end
|
29
|
-
|
30
|
-
def debian_os_major_sid
|
31
|
-
case facts[:ansible_distribution_major_version]
|
32
|
-
when /wheezy/i
|
33
|
-
'7'
|
34
|
-
when /jessie/i
|
35
|
-
'8'
|
36
|
-
when /stretch/i
|
37
|
-
'9'
|
38
|
-
when /buster/i
|
39
|
-
'10'
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def os_release_name
|
44
|
-
return '' if os_name != 'Debian' && os_name != 'Ubuntu'
|
45
|
-
facts[:ansible_distribution_release]
|
46
|
-
end
|
47
|
-
|
48
|
-
def os_major
|
49
|
-
if os_name == 'Debian' &&
|
50
|
-
facts[:ansible_distribution_major_version][%r{\/sid}i]
|
51
|
-
debian_os_major_sid
|
52
|
-
else
|
53
|
-
facts[:ansible_distribution_major_version] ||
|
54
|
-
facts[:ansible_lsb] && facts[:ansible_lsb]['major_release'] ||
|
55
|
-
(facts[:version].split('R')[0] if os_name == 'junos')
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def os_release
|
60
|
-
facts[:ansible_distribution_version] ||
|
61
|
-
facts[:ansible_lsb] && facts[:ansible_lsb]['release']
|
62
|
-
end
|
63
|
-
|
64
|
-
def os_minor
|
65
|
-
_, minor = os_release&.split('.', 2) ||
|
66
|
-
(facts[:version].split('R') if os_name == 'junos')
|
67
|
-
# Until Foreman supports os.minor as something that's not a number,
|
68
|
-
# we should remove the extra dots in the version. E.g:
|
69
|
-
# '6.1.7601.65536' becomes '6.1.760165536'
|
70
|
-
if facts[:ansible_os_family] == 'Windows'
|
71
|
-
minor, patch = minor.split('.', 2)
|
72
|
-
patch.tr!('.', '')
|
73
|
-
minor = "#{minor}.#{patch}"
|
74
|
-
end
|
75
|
-
minor || ''
|
76
|
-
end
|
77
|
-
|
78
|
-
def os_name
|
79
|
-
if facts[:ansible_os_family] == 'Windows'
|
80
|
-
facts[:ansible_os_name].tr(" \n\t", '') ||
|
81
|
-
facts[:ansible_distribution].tr(" \n\t", '')
|
82
|
-
else
|
83
|
-
distribution = facts[:ansible_lsb].try(:[], 'id') || facts[:ansible_distribution]
|
84
|
-
|
85
|
-
if distribution == 'RedHat' &&
|
86
|
-
facts[:ansible_lsb].try(:[], 'id') == 'RedHatEnterpriseWorkstation'
|
87
|
-
distribution += '_Workstation'
|
88
|
-
end
|
89
|
-
|
90
|
-
distribution
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def os_description
|
95
|
-
if facts[:ansible_os_family] == 'Windows'
|
96
|
-
facts[:ansible_os_name].strip || facts[:ansible_distribution].strip
|
97
|
-
else
|
98
|
-
facts[:ansible_lsb] && facts[:ansible_lsb]['description']
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ForemanAnsible
|
4
|
-
# On 1.13+ , use the parser for structured facts (like Facter 2) that comes
|
5
|
-
# from core
|
6
|
-
class StructuredFactImporter < ::StructuredFactImporter
|
7
|
-
def fact_name_class
|
8
|
-
ForemanAnsible::FactName
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.authorized_smart_proxy_features
|
12
|
-
'Ansible'
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(host, facts = {})
|
16
|
-
# Try to assign these facts to the correct host as per the facts say
|
17
|
-
# If that host isn't created yet, the host parameter will contain it
|
18
|
-
@host = Host.find_by(:name => facts[:ansible_facts][:ansible_fqdn] ||
|
19
|
-
facts[:ansible_facts][:fqdn]) ||
|
20
|
-
host
|
21
|
-
@facts = normalize(facts[:ansible_facts])
|
22
|
-
@counters = {}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
module ForemanAnsibleCore
|
6
|
-
module Runner
|
7
|
-
class AnsibleRunnerTest < ActiveSupport::TestCase
|
8
|
-
describe AnsibleRunner do
|
9
|
-
it 'parses files without event data' do
|
10
|
-
content = <<~JSON
|
11
|
-
{"uuid": "a29d8592-f805-4d0e-b73d-7a53cc35a92e", "stdout": " [WARNING]: Consider using the yum module rather than running 'yum'. If you", "counter": 8, "end_line": 8, "runner_ident": "e2d9ae11-026a-4f9f-9679-401e4b852ab0", "start_line": 7, "event": "verbose"}
|
12
|
-
JSON
|
13
|
-
|
14
|
-
File.expects(:read).with('fake.json').returns(content)
|
15
|
-
runner = AnsibleRunner.allocate
|
16
|
-
runner.expects(:handle_broadcast_data)
|
17
|
-
assert runner.send(:handle_event_file, 'fake.json')
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '#rebuild_secrets' do
|
22
|
-
let(:inventory) do
|
23
|
-
{ 'all' => { 'hosts' => ['foreman.example.com'] },
|
24
|
-
'_meta' => { 'hostvars' => { 'foreman.example.com' => {} } } }
|
25
|
-
end
|
26
|
-
let(:input) do
|
27
|
-
host_secrets = { 'ansible_password' => 'letmein', 'ansible_become_password' => 'iamroot' }
|
28
|
-
secrets = { 'per-host' => { 'foreman.example.com' => host_secrets } }
|
29
|
-
host_input = { 'input' => { 'action_input' => { 'secrets' => secrets } } }
|
30
|
-
{ 'foreman.example.com' => host_input }
|
31
|
-
end
|
32
|
-
let(:runner) { ForemanAnsibleCore::Runner::AnsibleRunner.allocate }
|
33
|
-
|
34
|
-
test 'uses secrets from inventory' do
|
35
|
-
test_inventory = inventory.merge('ssh_password' => 'sshpass', 'effective_user_password' => 'mypass')
|
36
|
-
rebuilt = runner.send(:rebuild_secrets, test_inventory, input)
|
37
|
-
host_vars = rebuilt.dig('_meta', 'hostvars', 'foreman.example.com')
|
38
|
-
assert_equal 'sshpass', host_vars['ansible_password']
|
39
|
-
assert_equal 'mypass', host_vars['ansible_become_password']
|
40
|
-
end
|
41
|
-
|
42
|
-
test 'host secrets are used when not overriden by inventory secrest' do
|
43
|
-
rebuilt = runner.send(:rebuild_secrets, inventory, input)
|
44
|
-
host_vars = rebuilt.dig('_meta', 'hostvars', 'foreman.example.com')
|
45
|
-
assert_equal 'letmein', host_vars['ansible_password']
|
46
|
-
assert_equal 'iamroot', host_vars['ansible_become_password']
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
class CommandCreatorTest < ActiveSupport::TestCase
|
6
|
-
let(:inventory_file) { 'test_inventory' }
|
7
|
-
let(:playbook_file) { 'test_palybook.yml' }
|
8
|
-
subject do
|
9
|
-
ForemanAnsibleCore::CommandCreator.new(inventory_file, playbook_file, {})
|
10
|
-
end
|
11
|
-
|
12
|
-
test 'returns a command array including the ansible-playbook command' do
|
13
|
-
assert command_parts.include?('ansible-playbook')
|
14
|
-
end
|
15
|
-
|
16
|
-
test 'the last argument is the playbook_file' do
|
17
|
-
assert command_parts.last == playbook_file
|
18
|
-
end
|
19
|
-
|
20
|
-
describe 'environment variables' do
|
21
|
-
let(:environment_variables) { subject.command.first }
|
22
|
-
|
23
|
-
test 'has a JSON_INVENTORY_FILE set' do
|
24
|
-
assert environment_variables['JSON_INVENTORY_FILE']
|
25
|
-
end
|
26
|
-
|
27
|
-
test 'has no ANSIBLE_CALLBACK_WHITELIST set by default' do
|
28
|
-
assert_not environment_variables['ANSIBLE_CALLBACK_WHITELIST']
|
29
|
-
end
|
30
|
-
|
31
|
-
test 'with a REX command it sets ANSIBLE_CALLBACK_WHITELIST to empty' do
|
32
|
-
set_command_options(:remote_execution_command, true)
|
33
|
-
assert environment_variables['ANSIBLE_CALLBACK_WHITELIST']
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe 'command options' do
|
38
|
-
it 'can have verbosity set' do
|
39
|
-
level = '3'
|
40
|
-
level_string = Array.new(level.to_i).map { 'v' }.join
|
41
|
-
set_command_options(:verbosity_level, level)
|
42
|
-
assert command_parts.any? do |part|
|
43
|
-
part == "-#{level_string}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'can have a timeout set' do
|
48
|
-
timeout = '5555'
|
49
|
-
set_command_options(:timeout, timeout)
|
50
|
-
assert command_parts.include?(timeout)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def command_parts
|
57
|
-
subject.command.flatten.map(&:to_s)
|
58
|
-
end
|
59
|
-
|
60
|
-
def set_command_options(option, value)
|
61
|
-
subject.instance_eval("@options[:#{option}] = \"#{value}\"",
|
62
|
-
__FILE__, __LINE__ - 1)
|
63
|
-
end
|
64
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
# Playbook Runner - this class uses foreman_tasks_core
|
6
|
-
# to run playbooks
|
7
|
-
class PlaybookRunnerTest < ActiveSupport::TestCase
|
8
|
-
context 'roles dir' do
|
9
|
-
test 'reads default when none provided' do
|
10
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.stubs(:unknown_hosts).
|
11
|
-
returns([])
|
12
|
-
File.expects(:exist?).with(Dir.home).returns(true)
|
13
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(nil)
|
14
|
-
runner = ForemanAnsibleCore::Runner::Playbook.new(nil, nil, :suspended_action => nil)
|
15
|
-
assert '/etc/ansible', runner.instance_variable_get('@ansible_dir')
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'working_dir' do
|
20
|
-
setup do
|
21
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.stubs(:unknown_hosts).
|
22
|
-
returns([])
|
23
|
-
end
|
24
|
-
|
25
|
-
test 'creates temp one if not provided' do
|
26
|
-
Dir.expects(:mktmpdir)
|
27
|
-
File.expects(:exist?).with(Dir.home).returns(true)
|
28
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(nil)
|
29
|
-
ForemanAnsibleCore::Runner::Playbook.new(nil, nil, :suspended_action => nil)
|
30
|
-
end
|
31
|
-
|
32
|
-
test 'reads it when provided' do
|
33
|
-
settings = { :working_dir => '/foo', :ansible_dir => '/etc/foo' }
|
34
|
-
ForemanAnsibleCore.expects(:settings).returns(settings)
|
35
|
-
File.expects(:exist?).with(settings[:ansible_dir]).returns(true)
|
36
|
-
Dir.expects(:mktmpdir).never
|
37
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(nil)
|
38
|
-
runner = ForemanAnsibleCore::Runner::Playbook.new(nil, nil, :suspended_action => nil)
|
39
|
-
assert '/foo', runner.instance_variable_get('@working_dir')
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'TOFU policy' do # Trust On First Use
|
44
|
-
setup do
|
45
|
-
@inventory = { 'all' => { 'hosts' => ['foreman.example.com'] } }
|
46
|
-
@output = StringIO.new
|
47
|
-
logger = Logger.new(@output)
|
48
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.stubs(:logger).
|
49
|
-
returns(logger)
|
50
|
-
end
|
51
|
-
|
52
|
-
test 'ignores known hosts' do
|
53
|
-
Net::SSH::KnownHosts.expects(:search_for).
|
54
|
-
with('foreman.example.com').returns(['somekey'])
|
55
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.
|
56
|
-
expects(:add_to_known_hosts).never
|
57
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(@inventory)
|
58
|
-
ForemanAnsibleCore::Runner::Playbook.new(@inventory, nil, :suspended_action => nil)
|
59
|
-
end
|
60
|
-
|
61
|
-
test 'adds unknown hosts to known_hosts' do
|
62
|
-
Net::SSH::KnownHosts.expects(:search_for).
|
63
|
-
with('foreman.example.com').returns([])
|
64
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.
|
65
|
-
expects(:add_to_known_hosts).with('foreman.example.com')
|
66
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(@inventory)
|
67
|
-
ForemanAnsibleCore::Runner::Playbook.new(@inventory, nil, :suspended_action => nil)
|
68
|
-
end
|
69
|
-
|
70
|
-
test 'logs error when it cannot add to known_hosts' do
|
71
|
-
Net::SSH::KnownHosts.expects(:search_for).
|
72
|
-
with('foreman.example.com').returns([])
|
73
|
-
Net::SSH::Transport::Session.expects(:new).with('foreman.example.com').
|
74
|
-
raises(Net::Error)
|
75
|
-
ForemanAnsibleCore::Runner::Playbook.any_instance.expects(:rebuild_secrets).returns(@inventory)
|
76
|
-
ForemanAnsibleCore::Runner::Playbook.new(@inventory, nil, :suspended_action => nil)
|
77
|
-
assert_match(
|
78
|
-
/ERROR.*Failed to save host key for foreman.example.com: Net::Error/,
|
79
|
-
@output.string
|
80
|
-
)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
context 'rebuild secrets' do
|
85
|
-
let(:inventory) do
|
86
|
-
{ 'all' => { 'hosts' => ['foreman.example.com'] },
|
87
|
-
'_meta' => { 'hostvars' => { 'foreman.example.com' => {} } } }
|
88
|
-
end
|
89
|
-
let(:secrets) do
|
90
|
-
host_secrets = { 'ansible_password' => 'letmein', 'ansible_become_password' => 'iamroot' }
|
91
|
-
{ 'per-host' => { 'foreman.example.com' => host_secrets } }
|
92
|
-
end
|
93
|
-
let(:runner) { ForemanAnsibleCore::Runner::Playbook.allocate }
|
94
|
-
|
95
|
-
test 'uses secrets from inventory' do
|
96
|
-
test_inventory = inventory.merge('ssh_password' => 'sshpass', 'effective_user_password' => 'mypass')
|
97
|
-
rebuilt = runner.send(:rebuild_secrets, test_inventory, secrets)
|
98
|
-
host_vars = rebuilt.dig('_meta', 'hostvars', 'foreman.example.com')
|
99
|
-
assert_equal 'sshpass', host_vars['ansible_password']
|
100
|
-
assert_equal 'mypass', host_vars['ansible_become_password']
|
101
|
-
end
|
102
|
-
|
103
|
-
test 'host secrets are used when not overriden by inventory secrest' do
|
104
|
-
rebuilt = runner.send(:rebuild_secrets, inventory, secrets)
|
105
|
-
host_vars = rebuilt.dig('_meta', 'hostvars', 'foreman.example.com')
|
106
|
-
assert_equal 'letmein', host_vars['ansible_password']
|
107
|
-
assert_equal 'iamroot', host_vars['ansible_become_password']
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_plugin_helper'
|
4
|
-
|
5
|
-
module ForemanAnsible
|
6
|
-
# Test for the facts importer - only verify that given
|
7
|
-
# a set of facts it's able to import them
|
8
|
-
class FactImporterTest < ActiveSupport::TestCase
|
9
|
-
setup do
|
10
|
-
@host = FactoryBot.build(:host)
|
11
|
-
end
|
12
|
-
|
13
|
-
test 'add new facts adds all fact names in the fixture' do
|
14
|
-
@fact_importer = FactImporter.new(@host, facts_json)
|
15
|
-
facts_to_be_added = FactSparser.sparse(facts_json[:ansible_facts]).keys +
|
16
|
-
FactSparser.unsparse(facts_json[:ansible_facts]).keys
|
17
|
-
@fact_importer.send(:add_new_facts)
|
18
|
-
assert((facts_to_be_added - FactName.all.map(&:name)).empty?)
|
19
|
-
end
|
20
|
-
|
21
|
-
test 'missing_facts returns facts we do not have in the database' do
|
22
|
-
@fact_importer = FactImporter.new(@host, facts_json)
|
23
|
-
@fact_importer.expects(:host_fact_names).
|
24
|
-
returns(['ansible_cmdline'])
|
25
|
-
refute @fact_importer.send(:missing_facts).include?('ansible_cmdline')
|
26
|
-
end
|
27
|
-
|
28
|
-
describe '#add_fact_value' do
|
29
|
-
setup do
|
30
|
-
@fact_importer = FactImporter.new(@host, :ansible_facts => {})
|
31
|
-
end
|
32
|
-
test 'does not add existing facts' do
|
33
|
-
existing_fact = FactoryBot.build(:fact_name)
|
34
|
-
@fact_importer.expects(:missing_facts).returns([])
|
35
|
-
assert_nil @fact_importer.send(:add_fact_value, nil, existing_fact)
|
36
|
-
end
|
37
|
-
|
38
|
-
test 'adds missing facts' do
|
39
|
-
missing_fact = FactoryBot.build(:fact_name)
|
40
|
-
@fact_importer.expects(:missing_facts).returns([missing_fact.name])
|
41
|
-
@fact_importer.counters[:added] = 0
|
42
|
-
assert_difference('@host.fact_values.count', 1) do
|
43
|
-
@fact_importer.send(:add_fact_value, 'missing_value', missing_fact)
|
44
|
-
@host.save
|
45
|
-
# We have to save the host in order to ensure @host.fact_values.count
|
46
|
-
# resolves properly (otherwise) :add_fact_value just won't save the
|
47
|
-
# relation
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|