blob: 91d44a9698c5aa99759b1829ba8cb38a14ae2f29 [file] [log] [blame]
Scott Baker2c0ebda2019-05-06 16:55:47 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package commands
18
19import (
20 "encoding/json"
21 "fmt"
22 "github.com/opencord/cordctl/format"
Scott Bakera00418a2019-06-03 16:15:28 -070023 "github.com/opencord/cordctl/order"
Scott Baker2c0ebda2019-05-06 16:55:47 -070024 "google.golang.org/grpc"
25 "gopkg.in/yaml.v2"
Scott Baker14c8f182019-05-22 18:05:29 -070026 "io"
Scott Baker2c0ebda2019-05-06 16:55:47 -070027 "io/ioutil"
28 "log"
29 "os"
30 "strings"
31 "time"
32)
33
34type OutputType uint8
35
36const (
37 OUTPUT_TABLE OutputType = iota
38 OUTPUT_JSON
39 OUTPUT_YAML
Scott Baker867aa302019-06-19 13:18:45 -070040
41 CORE_VERSION_CONSTRAINT = ">= 3, < 4" // Support XOS major version 3
Scott Baker2c0ebda2019-05-06 16:55:47 -070042)
43
Scott Baker14c8f182019-05-22 18:05:29 -070044// Make it easy to override output stream for testing
45var OutputStream io.Writer = os.Stdout
46
Scott Baker2c0ebda2019-05-06 16:55:47 -070047var CharReplacer = strings.NewReplacer("\\t", "\t", "\\n", "\n")
48
49type GrpcConfigSpec struct {
50 Timeout time.Duration `yaml:"timeout"`
51}
52
53type TlsConfigSpec struct {
54 UseTls bool `yaml:"useTls"`
55 CACert string `yaml:"caCert"`
56 Cert string `yaml:"cert"`
57 Key string `yaml:"key"`
58 Verify string `yaml:"verify"`
59}
60
61type GlobalConfigSpec struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070062 Server string `yaml:"server"`
63 Username string `yaml:"username"`
64 Password string `yaml:"password"`
Scott Baker14c8f182019-05-22 18:05:29 -070065 Protoset string `yaml:"protoset"`
Scott Baker6cf525a2019-05-09 12:25:08 -070066 Tls TlsConfigSpec `yaml:"tls"`
67 Grpc GrpcConfigSpec
Scott Baker2c0ebda2019-05-06 16:55:47 -070068}
69
70var GlobalConfig = GlobalConfigSpec{
Scott Baker6cf525a2019-05-09 12:25:08 -070071 Server: "localhost",
Scott Baker2c0ebda2019-05-06 16:55:47 -070072 Tls: TlsConfigSpec{
73 UseTls: false,
74 },
75 Grpc: GrpcConfigSpec{
76 Timeout: time.Second * 10,
77 },
78}
79
80var GlobalOptions struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070081 Config string `short:"c" long:"config" env:"CORDCONFIG" value-name:"FILE" default:"" description:"Location of client config file"`
82 Server string `short:"s" long:"server" default:"" value-name:"SERVER:PORT" description:"IP/Host and port of XOS"`
83 Username string `short:"u" long:"username" value-name:"USERNAME" default:"" description:"Username to authenticate with XOS"`
84 Password string `short:"p" long:"password" value-name:"PASSWORD" default:"" description:"Password to authenticate with XOS"`
Scott Baker14c8f182019-05-22 18:05:29 -070085 Protoset string `long:"protoset" value-name:"FILENAME" description:"Load protobuf definitions from protoset instead of reflection api"`
Scott Baker6cf525a2019-05-09 12:25:08 -070086 Debug bool `short:"d" long:"debug" description:"Enable debug mode"`
87 UseTLS bool `long:"tls" description:"Use TLS"`
88 CACert string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
89 Cert string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
90 Key string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
91 Verify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
Scott Baker5281d002019-05-16 10:45:26 -070092 Yes bool `short:"y" long:"yes" description:"answer yes to any confirmation prompts"`
Scott Baker2c0ebda2019-05-06 16:55:47 -070093}
94
95type OutputOptions struct {
96 Format string `long:"format" value-name:"FORMAT" default:"" description:"Format to use to output structured data"`
97 Quiet bool `short:"q" long:"quiet" description:"Output only the IDs of the objects"`
98 OutputAs string `short:"o" long:"outputas" default:"table" choice:"table" choice:"json" choice:"yaml" description:"Type of output to generate"`
99}
100
Scott Bakera00418a2019-06-03 16:15:28 -0700101type ListOutputOptions struct {
102 OutputOptions
103 OrderBy string `short:"r" long:"orderby" default:"" description:"Specify the sort order of the results"`
104}
105
Scott Baker2c0ebda2019-05-06 16:55:47 -0700106func toOutputType(in string) OutputType {
107 switch in {
108 case "table":
109 fallthrough
110 default:
111 return OUTPUT_TABLE
112 case "json":
113 return OUTPUT_JSON
114 case "yaml":
115 return OUTPUT_YAML
116 }
117}
118
119type CommandResult struct {
120 Format format.Format
Scott Bakera00418a2019-06-03 16:15:28 -0700121 OrderBy string
Scott Baker2c0ebda2019-05-06 16:55:47 -0700122 OutputAs OutputType
123 Data interface{}
124}
125
126type config struct {
Scott Baker6cf525a2019-05-09 12:25:08 -0700127 Server string `yaml:"server"`
Scott Baker2c0ebda2019-05-06 16:55:47 -0700128}
129
Scott Baker63ce82e2019-05-15 09:01:42 -0700130func ProcessGlobalOptions() {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700131 if len(GlobalOptions.Config) == 0 {
Scott Baker63ce82e2019-05-15 09:01:42 -0700132 home, err := os.UserHomeDir()
133 if err != nil {
134 log.Printf("Unable to discover the users home directory: %s\n", err)
135 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700136 GlobalOptions.Config = fmt.Sprintf("%s/.cord/config", home)
137 }
138
139 info, err := os.Stat(GlobalOptions.Config)
140 if err == nil && !info.IsDir() {
141 configFile, err := ioutil.ReadFile(GlobalOptions.Config)
142 if err != nil {
143 log.Printf("configFile.Get err #%v ", err)
144 }
145 err = yaml.Unmarshal(configFile, &GlobalConfig)
146 if err != nil {
147 log.Fatalf("Unmarshal: %v", err)
148 }
149 }
150
Scott Baker14c8f182019-05-22 18:05:29 -0700151 // Override from environment
152 // in particualr, for passing env vars via `go test`
153 env_server, present := os.LookupEnv("CORDCTL_SERVER")
154 if present {
155 GlobalConfig.Server = env_server
156 }
157 env_username, present := os.LookupEnv("CORDCTL_USERNAME")
158 if present {
159 GlobalConfig.Username = env_username
160 }
161 env_password, present := os.LookupEnv("CORDCTL_PASSWORD")
162 if present {
163 GlobalConfig.Password = env_password
164 }
165 env_protoset, present := os.LookupEnv("CORDCTL_PROTOSET")
166 if present {
167 GlobalConfig.Protoset = env_protoset
168 }
169
Scott Baker2c0ebda2019-05-06 16:55:47 -0700170 // Override from command line
171 if GlobalOptions.Server != "" {
172 GlobalConfig.Server = GlobalOptions.Server
173 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700174 if GlobalOptions.Username != "" {
175 GlobalConfig.Username = GlobalOptions.Username
176 }
177 if GlobalOptions.Password != "" {
178 GlobalConfig.Password = GlobalOptions.Password
179 }
Scott Baker14c8f182019-05-22 18:05:29 -0700180 if GlobalOptions.Protoset != "" {
181 GlobalConfig.Protoset = GlobalOptions.Protoset
182 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700183
Scott Baker5281d002019-05-16 10:45:26 -0700184 // Generate error messages for required settings
Scott Baker63ce82e2019-05-15 09:01:42 -0700185 if GlobalConfig.Server == "" {
186 log.Fatal("Server is not set. Please update config file or use the -s option")
187 }
188 if GlobalConfig.Username == "" {
189 log.Fatal("Username is not set. Please update config file or use the -u option")
190 }
191 if GlobalConfig.Password == "" {
192 log.Fatal("Password is not set. Please update config file or use the -p option")
193 }
194}
195
196func NewConnection() (*grpc.ClientConn, error) {
197 ProcessGlobalOptions()
Scott Baker2c0ebda2019-05-06 16:55:47 -0700198 return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure())
199}
200
201func GenerateOutput(result *CommandResult) {
202 if result != nil && result.Data != nil {
Scott Bakera00418a2019-06-03 16:15:28 -0700203 data := result.Data
204 if result.OrderBy != "" {
205 s, err := order.Parse(result.OrderBy)
206 if err != nil {
207 panic(err)
208 }
209 data, err = s.Process(data)
210 if err != nil {
211 panic(err)
212 }
213 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700214 if result.OutputAs == OUTPUT_TABLE {
215 tableFormat := format.Format(result.Format)
Scott Bakera00418a2019-06-03 16:15:28 -0700216 tableFormat.Execute(OutputStream, true, data)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700217 } else if result.OutputAs == OUTPUT_JSON {
Scott Bakera00418a2019-06-03 16:15:28 -0700218 asJson, err := json.Marshal(&data)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700219 if err != nil {
220 panic(err)
221 }
Scott Baker14c8f182019-05-22 18:05:29 -0700222 fmt.Fprintf(OutputStream, "%s", asJson)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700223 } else if result.OutputAs == OUTPUT_YAML {
Scott Bakera00418a2019-06-03 16:15:28 -0700224 asYaml, err := yaml.Marshal(&data)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700225 if err != nil {
226 panic(err)
227 }
Scott Baker14c8f182019-05-22 18:05:29 -0700228 fmt.Fprintf(OutputStream, "%s", asYaml)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700229 }
230 }
231}
Scott Baker5281d002019-05-16 10:45:26 -0700232
233// Applies common output options to format and generate output
234func FormatAndGenerateOutput(options *OutputOptions, default_format string, quiet_format string, data interface{}) {
235 outputFormat := CharReplacer.Replace(options.Format)
236 if outputFormat == "" {
237 outputFormat = default_format
238 }
Scott Baker175cb402019-05-17 16:13:06 -0700239 if options.Quiet {
Scott Baker5281d002019-05-16 10:45:26 -0700240 outputFormat = quiet_format
241 }
242
243 result := CommandResult{
244 Format: format.Format(outputFormat),
245 OutputAs: toOutputType(options.OutputAs),
246 Data: data,
247 }
248
249 GenerateOutput(&result)
250}
Scott Bakera00418a2019-06-03 16:15:28 -0700251
252// Applies common output options to format and generate output
253func FormatAndGenerateListOutput(options *ListOutputOptions, default_format string, quiet_format string, data interface{}) {
254 outputFormat := CharReplacer.Replace(options.Format)
255 if outputFormat == "" {
256 outputFormat = default_format
257 }
258 if options.Quiet {
259 outputFormat = quiet_format
260 }
261
262 result := CommandResult{
263 Format: format.Format(outputFormat),
264 OutputAs: toOutputType(options.OutputAs),
265 Data: data,
266 OrderBy: options.OrderBy,
267 }
268
269 GenerateOutput(&result)
270}