|
5 | 5 | #
|
6 | 6 | # Description: Add a host from an Ansible Inventory via API
|
7 | 7 |
|
8 |
| -require 'rest_client' |
9 |
| -require 'json' |
10 |
| - |
11 | 8 | module AutomationManagement
|
12 | 9 | module AnsibleTower
|
13 | 10 | module Operations
|
14 | 11 | module Methods
|
15 |
| - class AddHostToInventory |
16 |
| - include RedHatConsulting_Utilities::StdLib::Core |
| 12 | + class AddHostToInventory < Integration::AnsibleTower::AnsibleTowerBase |
17 | 13 |
|
18 |
| - TOWER_CONFIGURATION_URI = 'Integration/AnsibleTower/Configuration/default'.freeze |
| 14 | + include RedHatConsulting_Utilities::StdLib::Core |
19 | 15 |
|
20 | 16 | def initialize(handle = $evm)
|
21 |
| - @handle = handle |
| 17 | + super(handle) |
22 | 18 | @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 | 19 | 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 |
74 | 31 | 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 |
76 | 37 | end
|
77 | 38 |
|
78 | 39 | 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 |
82 | 41 | 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 |
97 | 42 | 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 | + |
108 | 45 | # 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 | + |
116 | 53 | # 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) |
120 | 61 | host_variables = {
|
121 |
| - :ansible_host => vm_ip_address |
122 |
| - }.to_json |
| 62 | + :ansible_host => vm_ip_address || vm_inventory_hostname, |
| 63 | + }.to_json |
123 | 64 |
|
124 | 65 | payload = {
|
125 |
| - :name => vm_hostname, |
126 |
| - :inventory => inventory_id, |
| 66 | + :name => vm_inventory_hostname, |
| 67 | + :inventory => @tower_inventory_id, |
127 | 68 | :enabled => true,
|
128 | 69 | :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 |
132 | 78 |
|
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} ].") |
138 | 89 | 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} ]") |
140 | 91 | exit MIQ_OK
|
141 | 92 | end
|
142 | 93 |
|
|
0 commit comments