| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| package config |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "sort" |
| "strings" |
| "unicode" |
| ) |
| |
| // FAKE is used for fake CLI only options like filter= |
| const FAKE = "fake" |
| |
| // APIArg are the args passable to an API |
| type APIArg struct { |
| Name string |
| Type string |
| Related []string |
| Description string |
| Required bool |
| Length int |
| } |
| |
| // API describes a CloudStack API |
| type API struct { |
| Name string |
| Verb string |
| Noun string |
| Args []*APIArg |
| RequiredArgs []string |
| Related []string |
| Async bool |
| Description string |
| ResponseKeys []string |
| } |
| |
| var apiCache map[string]*API |
| var apiVerbMap map[string][]*API |
| |
| // GetAPIVerbMap returns API cache by verb |
| func (c *Config) GetAPIVerbMap() map[string][]*API { |
| if apiVerbMap != nil { |
| return apiVerbMap |
| } |
| apiSplitMap := make(map[string][]*API) |
| for api := range apiCache { |
| verb := apiCache[api].Verb |
| apiSplitMap[verb] = append(apiSplitMap[verb], apiCache[api]) |
| } |
| return apiSplitMap |
| } |
| |
| // GetCache returns API cache by full API name |
| func (c *Config) GetCache() map[string]*API { |
| if apiCache == nil { |
| // read from disk? |
| return make(map[string]*API) |
| } |
| return apiCache |
| } |
| |
| // LoadCache loads cache using the default cache file |
| func LoadCache(c *Config) interface{} { |
| cacheFile := c.CacheFile() |
| Debug("Trying to read API cache from:", cacheFile) |
| cache, err := ioutil.ReadFile(cacheFile) |
| if err != nil { |
| if c.HasShell { |
| fmt.Fprintf(os.Stderr, "Loaded in-built API cache. Failed to read API cache, please run 'sync'.\n") |
| } |
| cache = []byte(preCache) |
| } |
| var data map[string]interface{} |
| _ = json.Unmarshal(cache, &data) |
| return c.UpdateCache(data) |
| } |
| |
| // SaveCache saves received auto-discovery data to cache file |
| func (c *Config) SaveCache(response map[string]interface{}) { |
| output, _ := json.Marshal(response) |
| ioutil.WriteFile(c.CacheFile(), output, 0600) |
| } |
| |
| // UpdateCache uses auto-discovery data to update internal API cache |
| func (c *Config) UpdateCache(response map[string]interface{}) interface{} { |
| apiCache = make(map[string]*API) |
| apiVerbMap = nil |
| |
| count := response["count"] |
| apiList := response["api"].([]interface{}) |
| |
| for _, node := range apiList { |
| api, valid := node.(map[string]interface{}) |
| if !valid { |
| fmt.Println("Error, moving on...") |
| continue |
| } |
| apiName := api["name"].(string) |
| isAsync := api["isasync"].(bool) |
| description := api["description"].(string) |
| |
| idx := 0 |
| for _, chr := range apiName { |
| if unicode.IsLower(chr) { |
| idx++ |
| } else { |
| break |
| } |
| } |
| verb := apiName[:idx] |
| noun := strings.ToLower(apiName[idx:]) |
| |
| var apiArgs []*APIArg |
| for _, argNode := range api["params"].([]interface{}) { |
| apiArg, _ := argNode.(map[string]interface{}) |
| related := []string{} |
| if apiArg["related"] != nil { |
| related = strings.Split(apiArg["related"].(string), ",") |
| sort.Strings(related) |
| } |
| apiArgs = append(apiArgs, &APIArg{ |
| Name: apiArg["name"].(string) + "=", |
| Type: apiArg["type"].(string), |
| Required: apiArg["required"].(bool), |
| Related: related, |
| Description: apiArg["description"].(string), |
| }) |
| } |
| |
| // Add filter arg |
| apiArgs = append(apiArgs, &APIArg{ |
| Name: "filter=", |
| Type: FAKE, |
| Description: "cloudmonkey specific response key filtering", |
| }) |
| |
| sort.Slice(apiArgs, func(i, j int) bool { |
| return apiArgs[i].Name < apiArgs[j].Name |
| }) |
| |
| var responseKeys []string |
| for _, respNode := range api["response"].([]interface{}) { |
| if resp, ok := respNode.(map[string]interface{}); ok { |
| if resp == nil || resp["name"] == nil { |
| continue |
| } |
| responseKeys = append(responseKeys, fmt.Sprintf("%v,", resp["name"])) |
| } |
| } |
| sort.Strings(responseKeys) |
| |
| var requiredArgs []string |
| for _, arg := range apiArgs { |
| if arg.Required { |
| requiredArgs = append(requiredArgs, arg.Name) |
| } |
| } |
| |
| apiCache[strings.ToLower(apiName)] = &API{ |
| Name: apiName, |
| Verb: verb, |
| Noun: noun, |
| Args: apiArgs, |
| RequiredArgs: requiredArgs, |
| Async: isAsync, |
| Description: description, |
| ResponseKeys: responseKeys, |
| } |
| } |
| return count |
| } |