我从cloudstack API listVirtualMachines获取数据,并尝试创建一个为某些主机提供dynamic ansible inventory的网络服务。
在我的第一次尝试中,目前我通过执行连接请求获取所有数据,然后在循环中解析所有输出:
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)
}
我想要的输出需要这种JSON格式:
{
"_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"
]
}
}
我的第一次尝试是创建一个巨大的地图:
inventory := map[string]map[string]map[string]string{}
但这只涉及_meta
键的结构:
{
"_meta": {
"hostvars": {
"x.x.x.x": {
"key": "value"
},
"x.x.x.y": {
"key": "value"
}
}
}
}
后来我带来了一个结构:
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)
}
此方法的问题在于返回的JSON正在添加键_meta
和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"
]
}
...
}
}
我希望只有密钥_meta
并且在同一级别(内联)上为每个主机名分配每个密钥,例如:
{
"_meta": {...},
"server1": {
"hosts": []
},
"group1": {
"hosts": []
},
"sandbox": {
"children": []
}
}
我猜这可能可以通过map[string]interface{}
来解决,这可能允许动态JSON对象具有动态密钥名称/结构,但是不确切知道,因此,任何帮助都将受到赞赏。
答案 0 :(得分:0)
你最好的选择可能是让Ansible实施json.Marshaler来“平铺”JSON文档。
package main
import (
"encoding/json"
"fmt"
)
func main() {
a := &Ansible{
Metadata: Hostvars{
Hosts: map[string]map[string]string{
"172.16.0.3": map[string]string{
"displayname": "server 1",
},
},
},
Hosts: map[string]Group{
"group1": Group{Hosts: []string{"172.16.0.3", "172.16.0.4", "172.16.0.5"}},
"sandbox": Group{Children: []string{"server1", "server2", "server3"}},
"server1": Group{Hosts: []string{"172.16.0.3"}},
},
}
b, err := json.MarshalIndent(a, "", " ")
fmt.Println(string(b), err)
}
type Ansible struct {
Metadata Hostvars
Hosts map[string]Group
}
type Hostvars struct {
Hosts map[string]map[string]string `json:"hostvars"`
}
// I don't *think* Ansible supports anything else in host groups, so it makes
// sense to define it as a struct.
type Group struct {
Hosts []string `json:"hosts,omitempty"`
Children []string `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler
func (a *Ansible) MarshalJSON() ([]byte, error) {
// Define a map that we will encode at the end of the function.
doc := make(map[string]interface{})
// Add all the groups.
for k, v := range a.Hosts {
doc[k] = v
}
// Add the _meta key.
doc["_meta"] = a.Metadata
return json.Marshal(doc)
}