Skip to content

Commit fa257b8

Browse files
A-Beckcurt-matthews
authored andcommitted
Refactor Ansible Tower integration for inventory mgmt
add ansibletowerbase.rb Move common methods to a base class to avoid duplications modify add_host_to_inventory.rb Take advantage of new base class alter the methods to determine what ip address to use to take into account the aquired_ip_address variable modify remove_host_from_inventory.rb Take advantage of new base class
1 parent 3053956 commit fa257b8

File tree

7 files changed

+259
-207
lines changed

7 files changed

+259
-207
lines changed

Automate/RedHatConsulting_Utilities/AutomationManagement/AnsibleTower/Operations/Methods.class/__methods__/add_host_to_inventory.rb

+59-108
Original file line numberDiff line numberDiff line change
@@ -5,138 +5,89 @@
55
#
66
# Description: Add a host from an Ansible Inventory via API
77

8-
require 'rest_client'
9-
require 'json'
10-
118
module AutomationManagement
129
module AnsibleTower
1310
module Operations
1411
module Methods
15-
class AddHostToInventory
16-
include RedHatConsulting_Utilities::StdLib::Core
12+
class AddHostToInventory < Integration::AnsibleTower::AnsibleTowerBase
1713

18-
TOWER_CONFIGURATION_URI = 'Integration/AnsibleTower/Configuration/default'.freeze
14+
include RedHatConsulting_Utilities::StdLib::Core
1915

2016
def initialize(handle = $evm)
21-
@handle = handle
17+
super(handle)
2218
@DEBUG = false
23-
@tower_config = @handle.instantiate(TOWER_CONFIGURATION_URI)
24-
@handle.log(:info, "Resolved Ansible Tower Configuration URI: #{@tower_config.name}") if @DEBUG
25-
end
26-
27-
def check_configuration
28-
error("Ansible Tower Config not found at #{TOWER_CONFIGURATION_URI}") if @tower_config.blank?
29-
error("Ansible Tower URL not set") if @tower_config['tower_url'].blank?
30-
error("Ansible Tower Username not set") if @tower_config['tower_username'].blank?
31-
error("Ansible Tower Password not set") if @tower_config['tower_password'].blank?
32-
end
33-
34-
def tower_request_url(api_path)
35-
api_version = @tower_config['tower_api_version']
36-
tower_url = @tower_config['tower_url']
37-
# build the URL for the REST request
38-
url = "#{tower_url}/api/#{api_version}/#{api_path}"
39-
# Tower expects the api path to end with a "/" so guarantee that it is there
40-
# Searches and filters don't like trailing / so exclude if includes =
41-
url << '/' unless url.end_with?('/') || url.include?('=')
42-
@handle.log(:info, "Call Tower API URL: <#{url}>") if @DEBUG
43-
return url
4419
end
45-
46-
def tower_request(action, api_path, payload=nil)
47-
# build the REST request
48-
params = {
49-
:method => action,
50-
:url => tower_request_url(api_path),
51-
:user => @tower_config['tower_username'],
52-
:password => @tower_config['tower_password'],
53-
:verify_ssl => @tower_config['tower_verify_ssl'],
54-
:timeout => @tower_config['tower_api_timeout']
55-
}
56-
params[:payload] = payload unless payload.nil?
57-
params[:headers] = {:content_type => 'application/json' } unless payload.nil?
58-
@handle.log(:info, "Tower request payload: #{payload.inspect}") if (@DEBUG and !payload.nil?)
59-
60-
# call the Ansible Tower REST service
61-
begin
62-
response = RestClient::Request.new(params).execute
63-
rescue => e
64-
error("Error making Tower request: #{e.response}")
65-
end
66-
67-
# Parse Tower Response
68-
response_json = {}
69-
# treat all 2xx responses as acceptable
70-
if response.code.to_s =~ /2\d\d/
71-
response_json = JSON.parse(response) unless response.body.blank?
72-
else
73-
error("Error calling Ansible Tower REST API. Response Code: <#{response.code}> Response: <#{response.inspect}>")
20+
21+
# Get the ip address to set as the 'ansible_host' host variable
22+
# if an IP address cannot be found, return nil.
23+
# More info about the 'ansible_host' var:
24+
# - https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#list-of-behavioral-inventory-parameters
25+
#
26+
# @param vm object
27+
def determine_host_ip_address(vm)
28+
if vm.ipaddresses.blank?
29+
log(:info, "Unable to determine VM IP address for Ansible Tower Inventory - no IP Addresses associated with VM.")
30+
return nil
7431
end
75-
return response_json
32+
acquired_ip_address = get_param(:acquired_ip_address)
33+
log(:info, "Aquired IP Address => #{acquired_ip_address}" )
34+
ip_address = vm.ipaddresses.include?(acquired_ip_address) ? acquired_ip_address : vm.ipaddresses.first
35+
log(:info, "Discovered IP Address for Ansible Tower Inventory => #{ip_address}" )
36+
return ip_address
7637
end
7738

7839
def main
79-
80-
@handle.log(:info, "Starting Ansible Tower REST API call to add a host to the inventory")
81-
40+
@handle.log(:info, "Starting Routine to add a host to an Ansible Tower Inventory [ #{@tower_inventory_name} ]") if @DEBUG
8241
dump_root() if @DEBUG
83-
84-
check_configuration
85-
86-
# Get Ansible Tower Inventory ID from Inventory Name
87-
inventory_name = @tower_config['tower_inventory_name']
88-
error('Ansible Tower Inventory not defined. Update configuration at #{@tower_config.name}') if inventory_name.blank?
89-
@handle.log(:info, "inventory_name: #{inventory_name}") if @DEBUG
90-
api_path = "inventories?name=#{CGI.escape(inventory_name)}"
91-
inventory_result = tower_request(:get, api_path)
92-
inventory_id = inventory_result['results'].first['id'] rescue nil
93-
error("Unable to determine Tower inventory_id from inventory name: [ #{inventory_name} ]") if inventory_id.blank?
94-
@handle.log(:info, "inventory_id: #{inventory_id}") if @DEBUG
95-
96-
# Get VM ip address and hostname
9742
vm,options = get_vm_and_options()
98-
error('Unable to find VM') if vm.blank?
99-
# determine vm hostname, first try to get hostname entry, else use vm name
100-
vm_hostname = vm.hostnames.first unless vm.hostnames.blank?
101-
vm_hostname ||= vm.name
102-
@handle.log(:info, "VM Hostname determined for Ansible Tower Inventory: #{vm_hostname}") if @DEBUG
103-
error('Unable to determine vm_name') if vm_hostname.blank?
104-
error('No IP addresses associated with VM') if vm.ipaddresses.blank?
105-
vm_ip_address = vm.ipaddresses.first
106-
@handle.log(:info, "Host IP address to be added to Tower Inventory: #{vm_ip_address}") if @DEBUG
107-
43+
vm_inventory_hostname = inventory_hostname(vm)
44+
10845
# Check if VM already exists in inventory
109-
api_path = "inventories/#{inventory_id}/hosts/?name=#{vm_hostname}"
110-
host_result = tower_request(:get, api_path)
111-
host_present_in_inventory = host_result['count'] > 0
112-
host_id = host_result['results'].first['id'] if host_present_in_inventory
113-
@handle.log(:info, "Host already present in Tower inventory: Host ID = #{host_id}") if ( @DEBUG and host_present_in_inventory )
114-
@handle.log(:info, "Host not yet present in Tower inventory") if ( @DEBUG and !host_present_in_inventory )
115-
46+
begin
47+
host_id = tower_host_id(vm)
48+
rescue
49+
log(:error, "Unable to determine if host is in Ansible Tower Inventory [ #{@tower_inventory_name} ]")
50+
error("Error making Ansible Tower API Call. #{e.to_s}")
51+
end
52+
11653
# Add the host to Ansible Tower Inventory
117-
api_path = host_present_in_inventory ? "hosts/#{host_id}" : "hosts"
118-
host_management_action = host_present_in_inventory ? :patch : :post
119-
54+
api_path = host_id.nil? ? "hosts" : "hosts/#{host_id}"
55+
host_management_action = host_id.nil? ? :post : :patch
56+
57+
# If the ipaddress cannot be determined, set ansible_host to
58+
# the inventory_hostname
59+
# This is default connection behavior if ansible_host is not set
60+
vm_ip_address = determine_host_ip_address(vm)
12061
host_variables = {
121-
:ansible_host => vm_ip_address
122-
}.to_json
62+
:ansible_host => vm_ip_address || vm_inventory_hostname,
63+
}.to_json
12364

12465
payload = {
125-
:name => vm_hostname,
126-
:inventory => inventory_id,
66+
:name => vm_inventory_hostname,
67+
:inventory => @tower_inventory_id,
12768
:enabled => true,
12869
:variables => host_variables
129-
}.to_json
130-
131-
tower_request(host_management_action, api_path, payload)
70+
}.to_json
71+
72+
begin
73+
tower_request(host_management_action, api_path, payload)
74+
rescue
75+
log(:error, "Unable to add host [ #{vm_inventory_hostname} ] to Ansible Tower inventory [ @tower_inventory_name ]")
76+
error("Error making Ansible Tower API Call. #{e.to_s}")
77+
end
13278

133-
# Verify if the name is in the inventory now.
134-
api_path = "inventories/#{inventory_id}/hosts?name=#{vm_hostname}"
135-
host_added_result = tower_request(:get, api_path)
136-
if host_added_result['count'] == 0
137-
error("Failed to add #{vm_hostname} to Ansible Inventory [ #{inventory_name} ].")
79+
# Verify if the name is in the inventory
80+
begin
81+
host_present_in_inventory = vm_in_inventory?(vm)
82+
rescue => e
83+
log(:error, "Unable to determine if host [ #{vm_inventory_hostname} ] is in Ansible Tower Inventory [ #{@tower_inventory_name} ]")
84+
error("Error making Ansible Tower API Call. #{e.to_s}")
85+
end
86+
87+
if !host_present_in_inventory
88+
error("Failed to add #{vm_inventory_hostname} to Ansible Inventory [ #{@tower_inventory_name} ].")
13889
end
139-
@handle.log(:info, "VM #{vm_hostname} with IP address #{vm_ip_address} successfully added to Ansible Tower inventory [ #{inventory_name} ]")
90+
@handle.log(:info, "VM #{vm_inventory_hostname} with IP address #{vm_ip_address} successfully added to Ansible Tower inventory [ #{@tower_inventory_name} ]")
14091
exit MIQ_OK
14192
end
14293

Automate/RedHatConsulting_Utilities/AutomationManagement/AnsibleTower/Operations/Methods.class/__methods__/add_host_to_inventory.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ object:
1111
location: inline
1212
embedded_methods:
1313
- "/StdLib/Core/Core"
14+
- "/Integration/AnsibleTower/AnsibleTowerBase/AnsibleTowerBase"
1415
options: {}
1516
inputs: []

Automate/RedHatConsulting_Utilities/AutomationManagement/AnsibleTower/Operations/Methods.class/__methods__/remove_host_from_inventory.rb

+39-99
Original file line numberDiff line numberDiff line change
@@ -5,125 +5,65 @@
55
#
66
# Description: Remove a host from an Ansible Inventory via API
77

8-
require 'rest_client'
9-
require 'json'
10-
118
module AutomationManagement
129
module AnsibleTower
1310
module Operations
1411
module Methods
15-
class RemoveHostFromInventory
16-
include RedHatConsulting_Utilities::StdLib::Core
17-
18-
TOWER_CONFIGURATION_URI = 'Integration/AnsibleTower/Configuration/default'.freeze
12+
class RemoveHostFromInventory < Integration::AnsibleTower::AnsibleTowerBase
1913

2014
def initialize(handle = $evm)
21-
@handle = handle
15+
super(handle)
2216
@DEBUG = false
23-
@tower_config = @handle.instantiate(TOWER_CONFIGURATION_URI)
24-
@handle.log(:info, "Resolved Ansible Tower Configuration URI: #{@tower_config.name}") if @DEBUG
25-
end
26-
27-
def check_configuration
28-
error("Ansible Tower Config not found at #{TOWER_CONFIGURATION_URI}") if @tower_config.blank?
29-
error("Ansible Tower URL not set") if @tower_config['tower_url'].blank?
30-
error("Ansible Tower Username not set") if @tower_config['tower_username'].blank?
31-
error("Ansible Tower Password not set") if @tower_config['tower_password'].blank?
32-
end
33-
34-
def tower_request_url(api_path)
35-
api_version = @tower_config['tower_api_version']
36-
tower_url = @tower_config['tower_url']
37-
# build the URL for the REST request
38-
url = "#{tower_url}/api/#{api_version}/#{api_path}"
39-
# Tower expects the api path to end with a "/" so guarantee that it is there
40-
# Searches and filters don't like trailing / so exclude if includes =
41-
url << '/' unless url.end_with?('/') || url.include?('=')
42-
@handle.log(:info, "Call Tower API URL: <#{url}>") if @DEBUG
43-
return url
44-
end
45-
46-
def tower_request(action, api_path, payload=nil)
47-
# build the REST request
48-
params = {
49-
:method => action,
50-
:url => tower_request_url(api_path),
51-
:user => @tower_config['tower_username'],
52-
:password => @tower_config['tower_password'],
53-
:verify_ssl => @tower_config['tower_verify_ssl'],
54-
:timeout => @tower_config['tower_api_timeout']
55-
}
56-
params[:payload] = payload unless payload.nil?
57-
params[:headers] = {:content_type => 'application/json' } unless payload.nil?
58-
@handle.log(:info, "Tower request payload: #{payload.inspect}") if (@DEBUG and !payload.nil?)
59-
60-
# call the Ansible Tower REST service
61-
begin
62-
response = RestClient::Request.new(params).execute
63-
rescue => e
64-
error("Error making Tower request: #{e.response}")
65-
end
66-
67-
# Parse Tower Response
68-
response_json = {}
69-
# treat all 2xx responses as acceptable
70-
if response.code.to_s =~ /2\d\d/
71-
response_json = JSON.parse(response) unless response.body.blank?
72-
else
73-
error("Error calling Ansible Tower REST API. Response Code: <#{response.code}> Response: <#{response.inspect}>")
74-
end
75-
return response_json
7617
end
77-
78-
def main
7918

80-
@handle.log(:info, "Starting Ansible Tower Routine to remove host from inventory")
81-
19+
def main
20+
@handle.log(:info, "Starting Ansible Tower Routine to remove host from Ansible Tower inventory [ #{@tower_inventory_name} ]")
8221
dump_root() if @DEBUG
22+
vm,options = get_vm_and_options()
23+
vm_inventory_hostname = inventory_hostname(vm)
8324

84-
check_configuration
25+
# Check if VM already exists in inventory
26+
begin
27+
host_id = tower_host_id(vm)
28+
rescue
29+
log(:error, "Unable to determine if host [ #{vm_inventory_hostname} ] is in Ansible Tower Inventory [ #{@tower_inventory_name} ]")
30+
error("Error making Ansible Tower API Call. #{e.to_s}")
31+
end
8532

86-
# Get Ansible Tower Inventory ID from Inventory Name
87-
inventory_name = @tower_config['tower_inventory_name']
88-
error('Ansible Tower Inventory not defined. Update configuration at #{@tower_config.name}') if inventory_name.blank?
89-
@handle.log(:info, "inventory_name: #{inventory_name}") if @DEBUG
90-
api_path = "inventories?name=#{CGI.escape(inventory_name)}"
91-
inventory_result = tower_request(:get, api_path)
92-
inventory_id = inventory_result['results'].first['id'] rescue nil
93-
error("Unable to determine Tower inventory_id from inventory name: #{inventory_name}") if inventory_id.blank?
94-
@handle.log(:info, "inventory_id: #{inventory_id}") if @DEBUG
95-
96-
# Get VM hostname
97-
vm,options = get_vm_and_options()
98-
error('Unable to find VM') if vm.blank?
99-
# determine vm hostname, first try to get hostname entry, else use vm name
100-
vm_hostname = vm.hostnames.first unless vm.hostnames.blank?
101-
vm_hostname ||= vm.name
102-
@handle.log(:info, "VM Hostname determined for Ansible Tower Inventory: #{vm_hostname}") if @DEBUG
103-
error('Unable to determine VM Hostname') if vm_hostname.blank?
104-
105-
# Check That VM already exists in inventory
106-
api_path = "inventories/#{inventory_id}/hosts/?name=#{vm_hostname}"
107-
host_result = tower_request(:get, api_path)
108-
host_present_in_inventory = host_result['count'] > 0
109-
if !host_present_in_inventory
110-
@handle.log(:info, "VM #{vm_hostname} does not exist in Ansible Tower Inventory [ #{inventory_name} ], done.")
33+
if host_id.nil?
34+
@handle.log(:info, "VM [ #{vm_inventory_hostname} ] does not exist in Ansible Tower Inventory [ #{@tower_inventory_name} ], done.")
11135
exit MIQ_OK
11236
end
11337

11438
# Remove the host from the Ansible Tower Inventory
115-
host_id = host_result['results'].first['id']
116-
api_path = "hosts/#{host_id}"
117-
tower_request(:delete, api_path)
39+
api_path = "inventories/#{@tower_inventory_id}/hosts/"
40+
payload = {
41+
:id => host_id,
42+
:disassociate => true
43+
}.to_json
44+
45+
begin
46+
tower_request(:post, api_path, payload)
47+
rescue
48+
log(:error, "Unable to remove host [ #{vm_inventory_hostname} ] from Ansible Tower Inventory [ #{@tower_inventory_name} ]")
49+
error("Error making Ansible Tower API Call. #{e.to_s}")
50+
end
11851

11952
# Verify that the host has been remove from the inventory
120-
api_path = "inventories/#{inventory_id}/hosts?name=#{vm_hostname}"
121-
host_removed_result = tower_request(:get, api_path)
122-
if host_removed_result['count'] > 0
123-
error("Failed to remove #{vm_hostname} to Ansible Inventory [ #{inventory_name} ].")
53+
begin
54+
host_present_in_inventory = vm_in_inventory?(vm)
55+
rescue => e
56+
log(:error, "Unable to determine if host [ #{vm_inventory_hostname} ] is in Ansible Tower Inventory [ #{@tower_inventory_name} ]")
57+
error("Error making Ansible Tower API Call. #{e.to_s}")
12458
end
125-
@handle.log(:info, "VM #{vm_hostname} successfully removed from Ansible Tower inventory [ #{inventory_name} ]")
59+
60+
if host_present_in_inventory
61+
error("Failed to remove #{vm_inventory_hostname} to Ansible Inventory [ #{@tower_inventory_name} ].")
62+
end
63+
64+
@handle.log(:info, "VM #{vm_inventory_hostname} successfully removed from Ansible Tower inventory [ #{@tower_inventory_name} ]")
12665
exit MIQ_OK
66+
12767
end
12868

12969
end

Automate/RedHatConsulting_Utilities/AutomationManagement/AnsibleTower/Operations/Methods.class/__methods__/remove_host_from_inventory.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ object:
1111
location: inline
1212
embedded_methods:
1313
- "/StdLib/Core/Core"
14+
- "/Integration/AnsibleTower/AnsibleTowerBase/AnsibleTowerBase"
1415
options: {}
1516
inputs: []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
object_type: class
3+
version: 1.0
4+
object:
5+
attributes:
6+
description:
7+
display_name:
8+
name: AnsibleTowerBase
9+
type:
10+
inherits:
11+
visibility:
12+
owner:
13+
schema: []

0 commit comments

Comments
 (0)