Skip to content

Instantly share code, notes, and snippets.

@zealot128
Created November 8, 2022 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zealot128/046733a248329e31d43fc9d52fd9040c to your computer and use it in GitHub Desktop.
Save zealot128/046733a248329e31d43fc9d52fd9040c to your computer and use it in GitHub Desktop.
Obtain hetzner cloud in ansible compatible inventory (script plugin)
#!/usr/bin/env ruby
require 'json'
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
# gem 'pry'
# gem 'toml'
gem 'typhoeus'
end
api_key = ENV['HCLOUD_TOKEN']
toml_file = File.expand_path("~/.config/hcloud/cli.toml")
if !api_key && File.exist?(toml_file)
config = TOML.load_file(toml_file)
api_key = config['contexts'].find { |i| i['name'] == config['active_context'] }['token']
end
unless api_key
puts "HCLOUD_TOKEN environment variable not set or hcloud config file not found"
exit 1
end
api_key = ENV['HCLOUD_TOKEN']
class ApiClient
def initialize(token)
@token = token
end
def servers(page: 1, per_page: 50)
get "https://api.hetzner.cloud/v1/servers?page=#{page}&per_page=#{per_page}"
end
def floating_ips(id)
get "https://api.hetzner.cloud/v1/floating_ips/#{id}"
end
private
def get(uri)
r = Typhoeus.get(uri, headers: headers)
unless r.success?
puts "Error: #{r.code}"
exit 1
end
JSON.parse(r.body)
end
def headers
{ 'Authorization' => "Bearer #{@token}" }
end
end
class Inventory
def initialize(api_client, public_net_type: 'ipv4')
@hosts = []
@host_vars = {}
@api_client = api_client
@root = { 'hcloud' => { 'hosts' => @hosts }, '_meta' => { 'hostvars' => @host_vars } }
@public_net_type = public_net_type
end
def add(server)
server_name = server['name']
@hosts << server_name
@host_vars[server_name] = map_host_vars(server)
add_to_datacenter(server)
add_to_labels(server)
end
def to_json(*_args)
JSON.pretty_generate(@root)
end
private
def map_host_vars(server)
ip = server['public_net'][@public_net_type]['ip']
# In case op IPv6, set the IP to the first address of the assigned range.
if @public_net_type == "ipv6"
raise
else
ansible_host = ip
end
public_net = server['public_net']
floating_ips_ids = public_net['floating_ips']
public_net['floating_ips'] = { 'ipv4': [], 'ipv6': [] }
floating_ips_ids.each do |id|
r = @api_client.floating_ips(id)
floating_ip = r['floating_ip']
ip_type = floating_ip['type']
ip = floating_ip['ip']
public_net['floating_ips'][ip_type] << ip
end
{
'ansible_host': ansible_host,
'hcloud_public_net': public_net,
'hcloud_server_type': server['server_type'],
'hcloud_image': server.dig('image', 'name') || '',
'hcloud_datacenter': server['datacenter']['name'],
'hcloud_labels': server['labels'],
}
end
def add_to_datacenter(server)
dc = server['datacenter']['name'].gsub("-", "_")
@root[dc] ||= { 'hosts' => [] }
@root[dc]['hosts'] << server['name']
end
def add_to_labels(server)
server['labels'].each do |label, value|
vlabel = "#{label}_#{value}"
@root[vlabel] ||= { 'hosts' => [] }
@root[vlabel]['hosts'] << server['name']
end
end
end
api_client = ApiClient.new(api_key)
inventory = Inventory.new(api_client)
page = 1
loop do
res = api_client.servers(page: page)
res['servers'].each do |server|
inventory.add(server)
end
if res['meta']['pagination']['next_page'] == nil
break
else
page += 1
end
end
puts inventory.to_json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment