I am obtaining data from the cloudstack API listVirtualMachines, and trying to create a web service that will provide a dynamic ansible inventory for certain hosts.
In my first tries, currently I am fetching all the data by doing a connection request and then within a loop parse all the output:
vms, _ := cs.Request(&cs.ListVirtualMachines{})
// use for the metadata _meta['hostvars']['IP']['key'] = val
hostvars := make(map[string]map[string]string)
// used per each host ['name']['hosts'] = [list]
hosts := make(map[string]map[string][]string)
for _, vm := range vms(*cs.ListVirtualMachinesResponse).VirtualMachine {
ip := vm.Nic[0].IPAddress.String()
if ip == "" {
continue
}
hostvars[ip] = map[string]string{
vm.DisplayName: vm.DisplayName,
"ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
}
if hosts[vm.DisplayName] == nil {
hosts[vm.DisplayName] = map[string][]string{}
}
hosts[vm.DisplayName]["hosts"] = append(hosts[vm.DisplayName]["hosts"], ip)
}
My desired output needs this JSON format:
{
"_meta": {
"hostvars": {
"172.16.0.3": {
"ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
"displayname": "server 1"
},
"172.16.0.4": {
"ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
"displayname": "server 2"
},
"172.16.0.5": {
"ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
"displayname": "server 3"
}
}
},
"group1": {
"hosts": [
"172.16.0.3",
"172.16.0.4",
"172.16.0.5"
]
},
"sandbox": {
"children": [
"server1",
"server2",
"server3"
]
},
"server1": {
"hosts": [
"172.16.0.3"
]
},
"server2": {
"hosts": [
"172.16.0.4"
]
},
"server3": {
"hosts": [
"172.16.0.5"
]
}
}
My first try was to create a huge map:
inventory := map[string]map[string]map[string]string{}
But that was covering only the structure of the _meta
key :
{
"_meta": {
"hostvars": {
"x.x.x.x": {
"key": "value"
},
"x.x.x.y": {
"key": "value"
}
}
}
}
Later I came with a struct:
type Ansible struct {
Metadata Hostvars `json:"_meta"`
Hosts map[string]map[string][]string `json:",inline"` // want to remove the Hosts key
}
type Hostvars struct {
Hosts map[string]map[string]string `json:"hostvars"`
}
inventory := &Ansible{
Hostvars{hostvars},
hosts,
}
if err := json.NewEncoder(os.Stdout).Encode(inventory); err != nil {
log.Println(err)
}
The problem with this approach is that the returned JSON is adding both keys _meta
and hosts
:
{
"_meta": {
"hostvars": {
"x.x.x.x": {
"key": "value"
},
"x.x.x.y": {
"key": "value"
}
}
},
"hosts": { <--- want to remove this
"server1": {
"hosts": [
"172.16.0.3"
]
}
...
}
}
I would like to just have the key _meta
and at the same level (inline) have each key for every hostname, something like:
{
"_meta": {...},
"server1": {
"hosts": []
},
"group1": {
"hosts": []
},
"sandbox": {
"children": []
}
}
I have a guess this probably could be solved with a map[string]interface{}
maybe that could allow a dynamic JSON object with dynamic key names/structure, don't know exactly, therefore, any help would be appreciated.