hammer_cli_foreman_ansible 0.3.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hammer_cli_foreman_ansible/ansible.rb +6 -0
- data/lib/hammer_cli_foreman_ansible/ansible_inventory.rb +52 -0
- data/lib/hammer_cli_foreman_ansible/associated_ansible_role.rb +85 -0
- data/lib/hammer_cli_foreman_ansible/command_extensions/inventory.rb +120 -0
- data/lib/hammer_cli_foreman_ansible/command_extensions/job_template.rb +19 -0
- data/lib/hammer_cli_foreman_ansible/command_extensions.rb +2 -0
- data/lib/hammer_cli_foreman_ansible/host.rb +43 -1
- data/lib/hammer_cli_foreman_ansible/hostgroup.rb +42 -1
- data/lib/hammer_cli_foreman_ansible/version.rb +1 -1
- data/lib/hammer_cli_foreman_ansible.rb +4 -0
- data/test/data/3.3/foreman_api.json +1 -0
- data/test/functional/host_test.rb +200 -0
- data/test/functional/hostgroup_test.rb +200 -0
- data/test/functional/test_helper.rb +11 -0
- data/test/test_helper.rb +16 -2
- metadata +33 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2378fa0dc9b16ad52e6fb9c3d368c23b2d8db6d4ba64bfc31e273b1ba1b2e21
|
4
|
+
data.tar.gz: 3d702aa4392493735abd50d5acf2f382e9f5c42809d2d7b13558df951fb5e87c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09b412c43b4a0050ee41d2e7e2657c1928c81fe1d84ead4323c70e254e92e31ac980c904b2c0d5f4e06f7970ab6269b60fb3d4ce39329607ee3328f0d25b6f56'
|
7
|
+
data.tar.gz: d8e97a5f33a6fa060f8f3b8c29886c8827eb5bca2170290dbce026c80bfbef2ae53878c96b0269a9e3863df7985189ecefa8b98419c8bfc81bab2323fcd10ffb
|
@@ -16,6 +16,12 @@ module HammerCLIForemanAnsible
|
|
16
16
|
'hammer_cli_foreman_ansible/ansible_variables'
|
17
17
|
)
|
18
18
|
|
19
|
+
lazy_subcommand(
|
20
|
+
'inventory',
|
21
|
+
_('Ansible Inventory'),
|
22
|
+
'HammerCLIForemanAnsible::AnsibleInventoryCommand',
|
23
|
+
'hammer_cli_foreman_ansible/ansible_inventory'
|
24
|
+
)
|
19
25
|
autoload_subcommands
|
20
26
|
end
|
21
27
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HammerCLIForemanAnsible
|
4
|
+
class AnsibleInventoryCommand < HammerCLIForeman::Command
|
5
|
+
resource :ansible_inventories
|
6
|
+
|
7
|
+
class HostsCommand < HammerCLIForeman::Command
|
8
|
+
action :hosts
|
9
|
+
command_name 'hosts'
|
10
|
+
option '--as-json', :flag, _('Full response as json')
|
11
|
+
|
12
|
+
def print_data(data)
|
13
|
+
return super unless option_as_json?
|
14
|
+
|
15
|
+
puts JSON.pretty_generate(data)
|
16
|
+
end
|
17
|
+
build_options
|
18
|
+
|
19
|
+
extend_with(HammerCLIForemanAnsible::CommandExtensions::Inventory.new)
|
20
|
+
end
|
21
|
+
|
22
|
+
class HostgroupsCommand < HammerCLIForeman::Command
|
23
|
+
action :hostgroups
|
24
|
+
command_name 'hostgroups'
|
25
|
+
option '--as-json', :flag, _('Full response as json')
|
26
|
+
|
27
|
+
def print_data(data)
|
28
|
+
return super unless option_as_json?
|
29
|
+
|
30
|
+
puts JSON.pretty_generate(data)
|
31
|
+
end
|
32
|
+
build_options
|
33
|
+
|
34
|
+
extend_with(HammerCLIForemanAnsible::CommandExtensions::Inventory.new)
|
35
|
+
end
|
36
|
+
|
37
|
+
class ScheduleCommand < HammerCLIForeman::Command
|
38
|
+
action :schedule
|
39
|
+
command_name 'schedule'
|
40
|
+
|
41
|
+
output do
|
42
|
+
field :job_id, _('Job Id')
|
43
|
+
field :data_url, _('Data URL')
|
44
|
+
end
|
45
|
+
|
46
|
+
build_options
|
47
|
+
end
|
48
|
+
|
49
|
+
autoload_subcommands
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module HammerCLIForemanAnsible
|
2
|
+
module AssociatedAnsibleRole
|
3
|
+
# This method fetches all associated resource (ansible roles) ids
|
4
|
+
# to send them back via :update endpoint of the main resource (host/hostgroup)
|
5
|
+
# We use this array of ids to add or remove direct associations
|
6
|
+
# This method is used to 'reset' associated ids via HammerCLIForeman::[Add|Remove]AssociatedCommand
|
7
|
+
def get_current_ids
|
8
|
+
roles = HammerCLIForeman.record_to_common_format(resource.call(association_name(true), { id: get_identifier }))
|
9
|
+
return ids_to_keep_for_removal(roles) if self.class.command_names.include?('remove')
|
10
|
+
|
11
|
+
ids_to_keep_for_addition(roles)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Since there are no option_sources available to deal with such cases (AssociatedCommand)
|
15
|
+
# Make sure we get id of the provided role before we try to get already associated ones
|
16
|
+
def get_new_ids
|
17
|
+
associated_identifiers = get_associated_identifiers
|
18
|
+
@associated_identifier = get_associated_identifier
|
19
|
+
|
20
|
+
ids = get_current_ids.map(&:to_s)
|
21
|
+
|
22
|
+
required_ids = associated_identifiers.nil? ? [] : associated_identifiers.map(&:to_s)
|
23
|
+
required_ids << @associated_identifier.to_s unless @associated_identifier.nil?
|
24
|
+
|
25
|
+
ids = if self.class.command_names.include?('remove')
|
26
|
+
ids.delete_if { |id| required_ids.include? id }
|
27
|
+
else
|
28
|
+
ids + required_ids
|
29
|
+
end
|
30
|
+
ids.uniq
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Treat inherited as we do in UI:
|
36
|
+
# - Don't allow to directly associate them (the role is not available to select)
|
37
|
+
# - If the same role was assigned AND inherited then treat it as it was just assigned (keep it)
|
38
|
+
# - If the role is indirectly associated only then don't associate it directly unless --force provided
|
39
|
+
def ids_to_keep_for_addition(roles)
|
40
|
+
roles.map do |role|
|
41
|
+
# Keep directly associated roles
|
42
|
+
next role['id'] if role['directly_assigned']
|
43
|
+
|
44
|
+
# Host groups can have not inherited and not directly assigned roles in the response.
|
45
|
+
# This means those roles are from hosts, skip them.
|
46
|
+
# Hosts cannot have such case.
|
47
|
+
|
48
|
+
# Pre-check to force stop the command if we're trying to add an already inherited role
|
49
|
+
# (in case we don't have it directly associated as well)
|
50
|
+
if @associated_identifier == role['id'] && role['inherited']
|
51
|
+
next role['id'] if option_force?
|
52
|
+
|
53
|
+
msg = _(
|
54
|
+
'Ansible role %{name} is already inherited from a host group. Please use %{option} for direct association.'
|
55
|
+
) % { name: role['name'], option: '--force' }
|
56
|
+
raise ArgumentError, msg
|
57
|
+
end
|
58
|
+
|
59
|
+
# We skip not directly assigned and inherited
|
60
|
+
# Also skip not inherited for host groups
|
61
|
+
nil
|
62
|
+
end.compact
|
63
|
+
end
|
64
|
+
|
65
|
+
# Treat inherited as we do in UI:
|
66
|
+
# - Don't allow to remove them (the role is not available to select)
|
67
|
+
# - If the same role was assigned AND inherited then treat it as it was just assigned (keep or remove it)
|
68
|
+
# - If the role was inherited only then don't remove it
|
69
|
+
def ids_to_keep_for_removal(roles)
|
70
|
+
roles.map do |role|
|
71
|
+
# Keep or remove later directly associated roles
|
72
|
+
next role['id'] if role['directly_assigned']
|
73
|
+
|
74
|
+
# Pre-check to force stop the command if we're trying to remove not directly assigned role
|
75
|
+
if role['id'] == @associated_identifier
|
76
|
+
raise ArgumentError, _('Ansible role %s is not assigned directly and cannot be removed.') % role['name']
|
77
|
+
end
|
78
|
+
|
79
|
+
# We skip not directly assigned and inherited
|
80
|
+
# Also skip not inherited for host groups
|
81
|
+
nil
|
82
|
+
end.compact
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HammerCLIForemanAnsible
|
4
|
+
module CommandExtensions
|
5
|
+
class Inventory < HammerCLI::CommandExtensions
|
6
|
+
output do |definition|
|
7
|
+
definition.append do
|
8
|
+
from 'foreman' do
|
9
|
+
field :hostname, _('Name')
|
10
|
+
field :fqdn, _('FQDN')
|
11
|
+
field :hostgroup, _('Host Group')
|
12
|
+
field :location, _('Location')
|
13
|
+
field :organization, _('Organization')
|
14
|
+
field :domainname, _('Domain')
|
15
|
+
field :foreman_domain_description, _('Foreman domain description')
|
16
|
+
field :owner_name, _('Owner name')
|
17
|
+
field :owner_email, _('Owner email')
|
18
|
+
field :ssh_authorized_keys, _('SSH authorized keys')
|
19
|
+
field :root_pw, _('Root password')
|
20
|
+
field :foreman_config_groups, _('Foreman config groups')
|
21
|
+
field :puppetmaster, _('Puppet master')
|
22
|
+
field :foreman_env, _('Puppet environment')
|
23
|
+
|
24
|
+
collection :foreman_subnets, _('Subnets') do
|
25
|
+
field :name, _('Name')
|
26
|
+
field :network, _('Network')
|
27
|
+
field :mask, _('Mask')
|
28
|
+
field :gateway, _('Gateway')
|
29
|
+
field :dns_primary, _('Primary DNS')
|
30
|
+
field :dns_secondary, _('Secondary DNS')
|
31
|
+
field :from, _('From')
|
32
|
+
field :to, _('To')
|
33
|
+
field :boot_mode, _('Boot mode')
|
34
|
+
field :ipam, _('IPAM')
|
35
|
+
field :vlanid, _('VLAN ID')
|
36
|
+
field :mtu, _('MTU')
|
37
|
+
field :nic_delay, _('NIC delay')
|
38
|
+
field :network_type, _('Network type')
|
39
|
+
field :description, _('Description')
|
40
|
+
end
|
41
|
+
|
42
|
+
collection :foreman_interfaces, _('Network interfaces') do
|
43
|
+
field :name, _('Interface Name')
|
44
|
+
field :identifier, _('Identifier')
|
45
|
+
field :attrs, _('Attributes'),Fields::Field, hide_blank: true
|
46
|
+
field :mac, _('MAC address')
|
47
|
+
field :ip, _('IPv4 address'), Fields::Field, hide_blank: true
|
48
|
+
field :ip6, _('IPv6 address'), Fields::Field, hide_blank: true
|
49
|
+
field :virtual, _('Virtual'), Fields::Boolean
|
50
|
+
field :link, _('Link'), Fields::Boolean
|
51
|
+
field :managed, _('Managed'), Fields::Boolean
|
52
|
+
field :primary, _('Primary'), Fields::Boolean
|
53
|
+
field :provision, _('Provision'), Fields::Boolean
|
54
|
+
field :subnet6, _('IPv6 subnet')
|
55
|
+
field :tag, _('Tag')
|
56
|
+
field :attached_to, _('Attached to')
|
57
|
+
field :type, _('Type')
|
58
|
+
field :attached_devices, _('Attached devices')
|
59
|
+
from 'subnet' do
|
60
|
+
label _('Subnet'), hide_blank: true do
|
61
|
+
field :name, _('Name')
|
62
|
+
field :network, _('Network')
|
63
|
+
field :mask, _('Mask')
|
64
|
+
field :gateway, _('Gateway')
|
65
|
+
field :dns_primary, _('Primary DNS')
|
66
|
+
field :dns_secondary, _('Secondary DNS')
|
67
|
+
field :from, _('From')
|
68
|
+
field :to, _('To')
|
69
|
+
field :boot_mode, _('Boot mode')
|
70
|
+
field :ipam, _('IPAM')
|
71
|
+
field :vlanid, _('VLAN ID')
|
72
|
+
field :mtu, _('MTU')
|
73
|
+
field :nic_delay, _('NIC delay')
|
74
|
+
field :network_type, _('Network type')
|
75
|
+
field :description, _('Description')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
collection :foreman_users, _('Foreman Users'), numbered: false do
|
81
|
+
field :firstname, _('First name')
|
82
|
+
field :lastname, _('Last name')
|
83
|
+
field :mail, _('Email')
|
84
|
+
field :description, _('Description')
|
85
|
+
field :fullname, _('Full name')
|
86
|
+
field :ssh_authorized_keys, _('SSH authorized keys')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
collection :foreman_ansible_roles, _('Foreman Ansible Roles'), numbered: false do
|
91
|
+
field :name, _('Role')
|
92
|
+
end
|
93
|
+
|
94
|
+
field :ansible_roles_check_mode, _('Ansible role check mode'), Fields::Boolean
|
95
|
+
field :host_packages, _('Host packages')
|
96
|
+
field :host_registration_insights, _('Host registration insights'), Fields::Boolean
|
97
|
+
field :host_registration_remote_execution, _('Host registration remote execution'), Fields::Boolean
|
98
|
+
field :remote_execution_ssh_keys, _('Remote execution ssh keys')
|
99
|
+
field :remote_execution_ssh_user, _('Remote execution ssh user')
|
100
|
+
field :remote_execution_effective_user_method, _('Remote execution effective user method')
|
101
|
+
field :remote_execution_connect_by_ip, _('Remote execution connect by IP'), Fields::Boolean
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
before_print do |data, cmd_obj|
|
106
|
+
unless cmd_obj.option_as_json?
|
107
|
+
new_data = data['all']['hosts'].each_with_object([]) do |hostname, arr|
|
108
|
+
data['_meta']['hostvars'][hostname]['foreman']['foreman_users'] = data['_meta']['hostvars'][hostname]['foreman']['foreman_users']&.map { |u| u[1] }
|
109
|
+
data['_meta']['hostvars'][hostname]['foreman_ansible_roles'] = data['_meta']['hostvars'][hostname]['foreman_ansible_roles']&.map { |r| { 'name' => r } }
|
110
|
+
arr << {
|
111
|
+
'hostname' => hostname
|
112
|
+
}.merge(data['_meta']['hostvars'][hostname])
|
113
|
+
end
|
114
|
+
data.clear
|
115
|
+
data['results'] = new_data
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HammerCLIForemanAnsible
|
4
|
+
module CommandExtensions
|
5
|
+
class JobTemplate < HammerCLI::CommandExtensions
|
6
|
+
before_print do |data|
|
7
|
+
unless data['provider_type'] == 'Ansible'
|
8
|
+
data.delete('ansible_callback_enabled')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
output do |definition|
|
13
|
+
definition.insert(:before, :description) do
|
14
|
+
field :ansible_callback_enabled, _('Ansible Callback Enabled'), Fields::Boolean
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -11,7 +11,10 @@ module HammerCLIForemanAnsible
|
|
11
11
|
class ListCommand < HammerCLIForeman::ListCommand
|
12
12
|
action :ansible_roles
|
13
13
|
|
14
|
-
output
|
14
|
+
output(HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition) do
|
15
|
+
field :inherited, _('Inherited'), Fields::Boolean
|
16
|
+
field :directly_assigned, _('Directly assigned'), Fields::Boolean
|
17
|
+
end
|
15
18
|
|
16
19
|
build_options
|
17
20
|
end
|
@@ -35,6 +38,45 @@ module HammerCLIForemanAnsible
|
|
35
38
|
|
36
39
|
build_options
|
37
40
|
end
|
41
|
+
|
42
|
+
class AddAnsibleRoleCommand < HammerCLIForeman::AddAssociatedCommand
|
43
|
+
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole
|
44
|
+
|
45
|
+
command_name 'add'
|
46
|
+
associated_resource :ansible_roles
|
47
|
+
desc _('Associate an Ansible role')
|
48
|
+
|
49
|
+
option '--force', :flag, _('Associate the Ansible role even if it already is associated indirectly')
|
50
|
+
|
51
|
+
success_message _('Ansible role has been associated.')
|
52
|
+
failure_message _('Could not associate the Ansible role')
|
53
|
+
|
54
|
+
validate_options do
|
55
|
+
any(:option_name, :option_id).required
|
56
|
+
any(:option_ansible_role_name, :option_ansible_role_id).required
|
57
|
+
end
|
58
|
+
|
59
|
+
build_options
|
60
|
+
end
|
61
|
+
|
62
|
+
class RemoveAnsibleRoleCommand < HammerCLIForeman::RemoveAssociatedCommand
|
63
|
+
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole
|
64
|
+
|
65
|
+
command_name 'remove'
|
66
|
+
associated_resource :ansible_roles
|
67
|
+
desc _('Disassociate an Ansible role')
|
68
|
+
|
69
|
+
success_message _('Ansible role has been disassociated.')
|
70
|
+
failure_message _('Could not disassociate the Ansible role')
|
71
|
+
|
72
|
+
validate_options do
|
73
|
+
any(:option_name, :option_id).required
|
74
|
+
any(:option_ansible_role_name, :option_ansible_role_id).required
|
75
|
+
end
|
76
|
+
|
77
|
+
build_options
|
78
|
+
end
|
79
|
+
|
38
80
|
autoload_subcommands
|
39
81
|
end
|
40
82
|
end
|
@@ -11,7 +11,10 @@ module HammerCLIForemanAnsible
|
|
11
11
|
class ListCommand < HammerCLIForeman::ListCommand
|
12
12
|
action :ansible_roles
|
13
13
|
|
14
|
-
output
|
14
|
+
output(HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition) do
|
15
|
+
field :inherited, _('Inherited'), Fields::Boolean
|
16
|
+
field :directly_assigned, _('Directly assigned'), Fields::Boolean
|
17
|
+
end
|
15
18
|
|
16
19
|
build_options
|
17
20
|
end
|
@@ -36,6 +39,44 @@ module HammerCLIForemanAnsible
|
|
36
39
|
build_options
|
37
40
|
end
|
38
41
|
|
42
|
+
class AddAnsibleRoleCommand < HammerCLIForeman::AddAssociatedCommand
|
43
|
+
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole
|
44
|
+
|
45
|
+
command_name 'add'
|
46
|
+
associated_resource :ansible_roles
|
47
|
+
desc _('Associate an Ansible role')
|
48
|
+
|
49
|
+
option '--force', :flag, _('Associate the Ansible role even if it already is associated indirectly')
|
50
|
+
|
51
|
+
success_message _('Ansible role has been associated.')
|
52
|
+
failure_message _('Could not associate the Ansible role')
|
53
|
+
|
54
|
+
validate_options do
|
55
|
+
any(:option_name, :option_title, :option_id).required
|
56
|
+
any(:option_ansible_role_name, :option_ansible_role_id).required
|
57
|
+
end
|
58
|
+
|
59
|
+
build_options
|
60
|
+
end
|
61
|
+
|
62
|
+
class RemoveAnsibleRoleCommand < HammerCLIForeman::RemoveAssociatedCommand
|
63
|
+
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole
|
64
|
+
|
65
|
+
command_name 'remove'
|
66
|
+
associated_resource :ansible_roles
|
67
|
+
desc _('Disassociate an Ansible role')
|
68
|
+
|
69
|
+
success_message _('Ansible role has been disassociated.')
|
70
|
+
failure_message _('Could not disassociate the Ansible role')
|
71
|
+
|
72
|
+
validate_options do
|
73
|
+
any(:option_name, :option_title, :option_id).required
|
74
|
+
any(:option_ansible_role_name, :option_ansible_role_id).required
|
75
|
+
end
|
76
|
+
|
77
|
+
build_options
|
78
|
+
end
|
79
|
+
|
39
80
|
autoload_subcommands
|
40
81
|
end
|
41
82
|
end
|
@@ -3,16 +3,20 @@ module HammerCLIForemanAnsible
|
|
3
3
|
require 'hammer_cli_foreman'
|
4
4
|
require 'hammer_cli_foreman/host'
|
5
5
|
require 'hammer_cli_foreman/hostgroup'
|
6
|
+
require 'hammer_cli_foreman_remote_execution'
|
6
7
|
|
7
8
|
require 'hammer_cli_foreman_ansible/version'
|
8
9
|
require 'hammer_cli_foreman_ansible/i18n'
|
9
10
|
require 'hammer_cli_foreman_ansible/ansible'
|
10
11
|
require 'hammer_cli_foreman_ansible/ansible_roles'
|
12
|
+
require 'hammer_cli_foreman_ansible/associated_ansible_role'
|
11
13
|
require 'hammer_cli_foreman_ansible/host'
|
12
14
|
require 'hammer_cli_foreman_ansible/hostgroup'
|
13
15
|
|
14
16
|
require 'hammer_cli_foreman_ansible/command_extensions'
|
15
17
|
|
18
|
+
HammerCLIForemanRemoteExecution::JobTemplate::InfoCommand.extend_with(HammerCLIForemanAnsible::CommandExtensions::JobTemplate.new)
|
19
|
+
|
16
20
|
HammerCLI::MainCommand.lazy_subcommand(
|
17
21
|
'ansible',
|
18
22
|
'Manage foreman ansible',
|