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 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看