Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
awilliams committed Mar 18, 2014
1 parent b5b4834 commit d18767d
Show file tree
Hide file tree
Showing 5 changed files with 407 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ _cgo_export.*
_testmain.go

*.exe

*.ini
134 changes: 134 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)

const (
API_URL = "https://api.linode.com/"
)

type ErrorJson struct {
Errors []struct {
Code int `json:"ERRORCODE"`
Message string `json:"ERRORMESSAGE"`
} `json:"ERRORARRAY,omitempty"`
}

type queryParams map[string]string

type apiAction struct {
method string
params queryParams
}

func (self *apiAction) Set(key, value string) {
self.params[key] = value
}

func (self apiAction) values() queryParams {
self.params["api_action"] = self.method
return self.params
}

type apiRequest struct {
apiKey string
baseUrl *url.URL
actions []*apiAction
}

func NewApiRequest(apiKey string) (*apiRequest, error) {
apiUrl, err := url.Parse(API_URL)
if err != nil {
return nil, err
}
var actions []*apiAction
return &apiRequest{apiKey: apiKey, baseUrl: apiUrl, actions: actions}, nil
}

func (self *apiRequest) AddAction(method string) *apiAction {
action := &apiAction{method: method, params: make(queryParams)}
self.actions = append(self.actions, action)
return action
}

func (self apiRequest) URL() string {
params := make(url.Values)
params.Set("api_key", self.apiKey)

if len(self.actions) == 1 {
for key, value := range self.actions[0].values() {
params.Set(key, value)
}
} else if len(self.actions) > 1 {
params.Set("api_action", "batch")
var requestArray []queryParams
for _, action := range self.actions {
requestArray = append(requestArray, action.values())
}
b, err := json.Marshal(requestArray)
if err != nil {
log.Fatal(err)
}
params.Set("api_requestArray", string(b))
}
self.baseUrl.RawQuery = params.Encode()
return self.baseUrl.String()
}

func (self apiRequest) GetJson(data interface{}) error {
resp, err := http.Get(self.URL())
if err != nil {
return err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

decoder := json.NewDecoder(bytes.NewReader(body))
err = decoder.Decode(data)
if err != nil {
linodeErr := checkForLinodeError(bytes.NewReader(body))
if linodeErr != nil {
return linodeErr
}
return err
}
return nil
}

func checkForLinodeError(body *bytes.Reader) error {
data := new(ErrorJson)
decoder := json.NewDecoder(body)
err := decoder.Decode(&data)
if err != nil {
// this is not actually an error
return nil
}
if len(data.Errors) > 0 {
var buf bytes.Buffer
buf.WriteString("Api Error!\n")
for _, e := range data.Errors {
buf.WriteString(fmt.Sprintf("[Code: %d] %s\n", e.Code, e.Message))
}
return fmt.Errorf(buf.String())
}
return nil
}

func (self *apiRequest) GoString() string {
s, err := url.QueryUnescape(self.URL())
if err != nil {
return ""
}
return s
}
125 changes: 125 additions & 0 deletions api/linode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package api

import (
"strconv"
//"fmt"
//"bytes"
)

type Linode struct {
Id int `json:"LINODEID"`
Status int `json:"STATUS"`
Label string `json:"LABEL"`
DisplayGroup string `json:"LPM_DISPLAYGROUP"`
Ram int `json:"TOTALRAM"`
Ips []LinodeIp
}

func (self Linode) PublicIp() string {
var ip string
for _, linodeIp := range self.Ips {
if linodeIp.Public == 1 {
ip = linodeIp.Ip
break
}
}
return ip
}

func (self Linode) PrivateIp() string {
var ip string
for _, linodeIp := range self.Ips {
if linodeIp.Public == 0 {
ip = linodeIp.Ip
break
}
}
return ip
}

type Linodes map[int]*Linode

func (self Linodes) FilterByDisplayGroup(group string) Linodes {
for id, linode := range self {
if linode.Status != 1 || (linode.DisplayGroup != "" && linode.DisplayGroup != group) {
delete(self, id)
}
}
return self
}

func (self Linodes) FilterByStatus(status int) Linodes {
for id, linode := range self {
if linode.Status != status {
delete(self, id)
}
}
return self
}

func LinodeList(apiKey string) (Linodes, error) {
method := "linode.list"
apiRequest, err := NewApiRequest(apiKey)
if err != nil {
return nil, err
}
apiRequest.AddAction(method)

var data struct {
Linodes []Linode `json:"DATA,omitempty"`
}
err = apiRequest.GetJson(&data)
if err != nil {
return nil, err
}

linodes := make(Linodes)
for _, linode := range data.Linodes {
//linode.Ips = []LinodeIp{}
l := linode
linodes[linode.Id] = &l
}

return linodes, nil
}

type LinodeIp struct {
LinodeId int `json:"LINODEID"`
Ip string `json:"IPADDRESS"`
Public int `json:"ISPUBLIC"`
}

func LinodeListWithIps(apiKey string) (Linodes, error) {
linodes, err := LinodeList(apiKey)
if err != nil {
return nil, err
}

method := "linode.ip.list"
apiRequest, err := NewApiRequest(apiKey)
if err != nil {
return nil, err
}
for _, linode := range linodes {
action := apiRequest.AddAction(method)
action.Set("LinodeID", strconv.Itoa(linode.Id))
}

var data []struct {
LinodeIps []LinodeIp `json:"DATA"`
}
err = apiRequest.GetJson(&data)
if err != nil {
return nil, err
}

for _, ipList := range data {
for _, linodeIp := range ipList.LinodeIps {
if linode, ok := linodes[linodeIp.LinodeId]; ok {
linode.Ips = append(linode.Ips, linodeIp)
}
}
}

return linodes, nil
}
35 changes: 35 additions & 0 deletions inventory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"encoding/json"
"github.com/awilliams/linode-inventory/api"
)

type HostMeta map[string]string

type Inventory struct {
Meta map[string]map[string]HostMeta `json:"_meta"`
Hosts []string `json:"hosts"`
}

func (self Inventory) toJson() ([]byte, error) {
return json.MarshalIndent(self, " ", " ")
}

func makeInventory(linodes api.Linodes) Inventory {
meta := make(map[string]map[string]HostMeta)
hostvars := make(map[string]HostMeta)
meta["hostvars"] = hostvars

inventory := Inventory{Hosts: []string{}, Meta: meta}
for _, linode := range linodes {
inventory.Hosts = append(inventory.Hosts, linode.Label)
hostmeta := make(HostMeta)
hostmeta["ansible_ssh_host"] = linode.PublicIp()
hostmeta["host_label"] = linode.Label
hostmeta["host_display_group"] = linode.DisplayGroup
hostmeta["host_private_ip"] = linode.PrivateIp()
hostvars[linode.Label] = hostmeta
}
return inventory
}
Loading

0 comments on commit d18767d

Please sign in to comment.