dslf46995 2018-04-11 19:26
浏览 116
已采纳

如何创建自定义json结构(可库存)

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.

  • 写回答

1条回答 默认 最新

  • doupingdiao3546 2018-04-11 20:48
    关注

    You best bet is probably to let Ansible implement json.Marshaler to "flatten" the JSON document on-the-fly.

    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)
    }
    

    Try it on the playground: https://play.golang.org/p/OKQHAG3vqIG

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 准备学习小程序搭建,谁能手把手的教我啊?
  • ¥15 关于#嵌入式硬件#的问题:树莓派第一天重装配置python和opencv后第二天打开就成这样,瞎捣鼓搞出来文件夹还是没把原来的界面调回来
  • ¥20 Arduino 循迹小车程序电路出错故障求解
  • ¥20 Arduino 循迹小车程序电路出错故障求解
  • ¥100 AT89C52单片机C语言调试之后再回答
  • ¥15 AT89C52单片机C语言串口助手发送数据包返回值
  • ¥15 C++数组中找第二小的数字程序纠错
  • ¥15 wannier复现图像时berry曲率极值点与高对称点严重偏移
  • ¥15 利用决策森林为什么会出现这样·的问题(关键词-情感分析)
  • ¥15 DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI[/untitled30_war_e