blob: 4a38d5bf6d8edad2ddb3cc8b68d3fc7ddbf2b3a7 [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"
23 "google.golang.org/grpc"
24 "gopkg.in/yaml.v2"
Scott Baker14c8f182019-05-22 18:05:29 -070025 "io"
Scott Baker2c0ebda2019-05-06 16:55:47 -070026 "io/ioutil"
27 "log"
28 "os"
29 "strings"
30 "time"
31)
32
33type OutputType uint8
34
35const (
36 OUTPUT_TABLE OutputType = iota
37 OUTPUT_JSON
38 OUTPUT_YAML
39)
40
Scott Baker14c8f182019-05-22 18:05:29 -070041// Make it easy to override output stream for testing
42var OutputStream io.Writer = os.Stdout
43
Scott Baker2c0ebda2019-05-06 16:55:47 -070044var CharReplacer = strings.NewReplacer("\\t", "\t", "\\n", "\n")
45
46type GrpcConfigSpec struct {
47 Timeout time.Duration `yaml:"timeout"`
48}
49
50type TlsConfigSpec struct {
51 UseTls bool `yaml:"useTls"`
52 CACert string `yaml:"caCert"`
53 Cert string `yaml:"cert"`
54 Key string `yaml:"key"`
55 Verify string `yaml:"verify"`
56}
57
58type GlobalConfigSpec struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070059 Server string `yaml:"server"`
60 Username string `yaml:"username"`
61 Password string `yaml:"password"`
Scott Baker14c8f182019-05-22 18:05:29 -070062 Protoset string `yaml:"protoset"`
Scott Baker6cf525a2019-05-09 12:25:08 -070063 Tls TlsConfigSpec `yaml:"tls"`
64 Grpc GrpcConfigSpec
Scott Baker2c0ebda2019-05-06 16:55:47 -070065}
66
67var GlobalConfig = GlobalConfigSpec{
Scott Baker6cf525a2019-05-09 12:25:08 -070068 Server: "localhost",
Scott Baker2c0ebda2019-05-06 16:55:47 -070069 Tls: TlsConfigSpec{
70 UseTls: false,
71 },
72 Grpc: GrpcConfigSpec{
73 Timeout: time.Second * 10,
74 },
75}
76
77var GlobalOptions struct {
Scott Baker6cf525a2019-05-09 12:25:08 -070078 Config string `short:"c" long:"config" env:"CORDCONFIG" value-name:"FILE" default:"" description:"Location of client config file"`
79 Server string `short:"s" long:"server" default:"" value-name:"SERVER:PORT" description:"IP/Host and port of XOS"`
80 Username string `short:"u" long:"username" value-name:"USERNAME" default:"" description:"Username to authenticate with XOS"`
81 Password string `short:"p" long:"password" value-name:"PASSWORD" default:"" description:"Password to authenticate with XOS"`
Scott Baker14c8f182019-05-22 18:05:29 -070082 Protoset string `long:"protoset" value-name:"FILENAME" description:"Load protobuf definitions from protoset instead of reflection api"`
Scott Baker6cf525a2019-05-09 12:25:08 -070083 Debug bool `short:"d" long:"debug" description:"Enable debug mode"`
84 UseTLS bool `long:"tls" description:"Use TLS"`
85 CACert string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
86 Cert string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
87 Key string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
88 Verify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
Scott Baker5281d002019-05-16 10:45:26 -070089 Yes bool `short:"y" long:"yes" description:"answer yes to any confirmation prompts"`
Scott Baker2c0ebda2019-05-06 16:55:47 -070090}
91
92type OutputOptions struct {
93 Format string `long:"format" value-name:"FORMAT" default:"" description:"Format to use to output structured data"`
94 Quiet bool `short:"q" long:"quiet" description:"Output only the IDs of the objects"`
95 OutputAs string `short:"o" long:"outputas" default:"table" choice:"table" choice:"json" choice:"yaml" description:"Type of output to generate"`
96}
97
98func toOutputType(in string) OutputType {
99 switch in {
100 case "table":
101 fallthrough
102 default:
103 return OUTPUT_TABLE
104 case "json":
105 return OUTPUT_JSON
106 case "yaml":
107 return OUTPUT_YAML
108 }
109}
110
111type CommandResult struct {
112 Format format.Format
113 OutputAs OutputType
114 Data interface{}
115}
116
117type config struct {
Scott Baker6cf525a2019-05-09 12:25:08 -0700118 Server string `yaml:"server"`
Scott Baker2c0ebda2019-05-06 16:55:47 -0700119}
120
Scott Baker63ce82e2019-05-15 09:01:42 -0700121func ProcessGlobalOptions() {
Scott Baker2c0ebda2019-05-06 16:55:47 -0700122 if len(GlobalOptions.Config) == 0 {
Scott Baker63ce82e2019-05-15 09:01:42 -0700123 home, err := os.UserHomeDir()
124 if err != nil {
125 log.Printf("Unable to discover the users home directory: %s\n", err)
126 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700127 GlobalOptions.Config = fmt.Sprintf("%s/.cord/config", home)
128 }
129
130 info, err := os.Stat(GlobalOptions.Config)
131 if err == nil && !info.IsDir() {
132 configFile, err := ioutil.ReadFile(GlobalOptions.Config)
133 if err != nil {
134 log.Printf("configFile.Get err #%v ", err)
135 }
136 err = yaml.Unmarshal(configFile, &GlobalConfig)
137 if err != nil {
138 log.Fatalf("Unmarshal: %v", err)
139 }
140 }
141
Scott Baker14c8f182019-05-22 18:05:29 -0700142 // Override from environment
143 // in particualr, for passing env vars via `go test`
144 env_server, present := os.LookupEnv("CORDCTL_SERVER")
145 if present {
146 GlobalConfig.Server = env_server
147 }
148 env_username, present := os.LookupEnv("CORDCTL_USERNAME")
149 if present {
150 GlobalConfig.Username = env_username
151 }
152 env_password, present := os.LookupEnv("CORDCTL_PASSWORD")
153 if present {
154 GlobalConfig.Password = env_password
155 }
156 env_protoset, present := os.LookupEnv("CORDCTL_PROTOSET")
157 if present {
158 GlobalConfig.Protoset = env_protoset
159 }
160
Scott Baker2c0ebda2019-05-06 16:55:47 -0700161 // Override from command line
162 if GlobalOptions.Server != "" {
163 GlobalConfig.Server = GlobalOptions.Server
164 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700165 if GlobalOptions.Username != "" {
166 GlobalConfig.Username = GlobalOptions.Username
167 }
168 if GlobalOptions.Password != "" {
169 GlobalConfig.Password = GlobalOptions.Password
170 }
Scott Baker14c8f182019-05-22 18:05:29 -0700171 if GlobalOptions.Protoset != "" {
172 GlobalConfig.Protoset = GlobalOptions.Protoset
173 }
Scott Baker2c0ebda2019-05-06 16:55:47 -0700174
Scott Baker5281d002019-05-16 10:45:26 -0700175 // Generate error messages for required settings
Scott Baker63ce82e2019-05-15 09:01:42 -0700176 if GlobalConfig.Server == "" {
177 log.Fatal("Server is not set. Please update config file or use the -s option")
178 }
179 if GlobalConfig.Username == "" {
180 log.Fatal("Username is not set. Please update config file or use the -u option")
181 }
182 if GlobalConfig.Password == "" {
183 log.Fatal("Password is not set. Please update config file or use the -p option")
184 }
185}
186
187func NewConnection() (*grpc.ClientConn, error) {
188 ProcessGlobalOptions()
Scott Baker2c0ebda2019-05-06 16:55:47 -0700189 return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure())
190}
191
192func GenerateOutput(result *CommandResult) {
193 if result != nil && result.Data != nil {
194 if result.OutputAs == OUTPUT_TABLE {
195 tableFormat := format.Format(result.Format)
Scott Baker14c8f182019-05-22 18:05:29 -0700196 tableFormat.Execute(OutputStream, true, result.Data)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700197 } else if result.OutputAs == OUTPUT_JSON {
198 asJson, err := json.Marshal(&result.Data)
199 if err != nil {
200 panic(err)
201 }
Scott Baker14c8f182019-05-22 18:05:29 -0700202 fmt.Fprintf(OutputStream, "%s", asJson)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700203 } else if result.OutputAs == OUTPUT_YAML {
204 asYaml, err := yaml.Marshal(&result.Data)
205 if err != nil {
206 panic(err)
207 }
Scott Baker14c8f182019-05-22 18:05:29 -0700208 fmt.Fprintf(OutputStream, "%s", asYaml)
Scott Baker2c0ebda2019-05-06 16:55:47 -0700209 }
210 }
211}
Scott Baker5281d002019-05-16 10:45:26 -0700212
213// Applies common output options to format and generate output
214func FormatAndGenerateOutput(options *OutputOptions, default_format string, quiet_format string, data interface{}) {
215 outputFormat := CharReplacer.Replace(options.Format)
216 if outputFormat == "" {
217 outputFormat = default_format
218 }
Scott Baker175cb402019-05-17 16:13:06 -0700219 if options.Quiet {
Scott Baker5281d002019-05-16 10:45:26 -0700220 outputFormat = quiet_format
221 }
222
223 result := CommandResult{
224 Format: format.Format(outputFormat),
225 OutputAs: toOutputType(options.OutputAs),
226 Data: data,
227 }
228
229 GenerateOutput(&result)
230}