blob: 78d1acaa1c15cf00e1af17405d634e503a81306b [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2 * Copyright 2019-present Ciena Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package commands
17
18import (
19 "context"
20 "fmt"
Zack Williamse940c7a2019-08-21 14:25:39 -070021 "github.com/fullstorydev/grpcurl"
22 flags "github.com/jessevdk/go-flags"
23 "github.com/jhump/protoreflect/dynamic"
Scott Baker2b0ad652019-08-21 14:57:07 -070024 "github.com/opencord/voltctl/pkg/format"
25 "github.com/opencord/voltctl/pkg/model"
David Bainbridge12f036f2019-10-15 22:09:04 +000026 "log"
Zack Williamse940c7a2019-08-21 14:25:39 -070027 "strings"
28)
29
30const (
31 DEFAULT_DEVICE_FORMAT = "table{{ .Id }}\t{{.Type}}\t{{.Root}}\t{{.ParentId}}\t{{.SerialNumber}}\t{{.Vlan}}\t{{.AdminState}}\t{{.OperStatus}}\t{{.ConnectStatus}}"
32 DEFAULT_DEVICE_PORTS_FORMAT = "table{{.PortNo}}\t{{.Label}}\t{{.Type}}\t{{.AdminState}}\t{{.OperStatus}}\t{{.DeviceId}}\t{{.Peers}}"
33 DEFAULT_DEVICE_INSPECT_FORMAT = `ID: {{.Id}}
34 TYPE: {{.Type}}
35 ROOT: {{.Root}}
36 PARENTID: {{.ParentId}}
37 SERIALNUMBER: {{.SerialNumber}}
38 VLAN: {{.Vlan}}
39 ADMINSTATE: {{.AdminState}}
40 OPERSTATUS: {{.OperStatus}}
41 CONNECTSTATUS: {{.ConnectStatus}}`
42)
43
44type DeviceList struct {
45 ListOutputOptions
46}
47
48type DeviceCreate struct {
49 DeviceType string `short:"t" long:"devicetype" default:"simulated_olt" description:"Device type"`
50 MACAddress string `short:"m" long:"macaddress" default:"00:0c:e2:31:40:00" description:"MAC Address"`
51 IPAddress string `short:"i" long:"ipaddress" default:"" description:"IP Address"`
52 HostAndPort string `short:"H" long:"hostandport" default:"" description:"Host and port"`
53}
54
55type DeviceId string
56
57type DeviceDelete struct {
58 Args struct {
59 Ids []DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
60 } `positional-args:"yes"`
61}
62
63type DeviceEnable struct {
64 Args struct {
65 Ids []DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
66 } `positional-args:"yes"`
67}
68
69type DeviceDisable struct {
70 Args struct {
71 Ids []DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
72 } `positional-args:"yes"`
73}
74
75type DeviceReboot struct {
76 Args struct {
77 Ids []DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
78 } `positional-args:"yes"`
79}
80
81type DeviceFlowList struct {
82 ListOutputOptions
83 Args struct {
84 Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
85 } `positional-args:"yes"`
86}
87
88type DevicePortList struct {
89 ListOutputOptions
90 Args struct {
91 Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
92 } `positional-args:"yes"`
93}
94
95type DeviceInspect struct {
96 OutputOptionsJson
97 Args struct {
98 Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
99 } `positional-args:"yes"`
100}
101
102type DeviceOpts struct {
103 List DeviceList `command:"list"`
104 Create DeviceCreate `command:"create"`
105 Delete DeviceDelete `command:"delete"`
106 Enable DeviceEnable `command:"enable"`
107 Disable DeviceDisable `command:"disable"`
108 Flows DeviceFlowList `command:"flows"`
109 Ports DevicePortList `command:"ports"`
110 Inspect DeviceInspect `command:"inspect"`
111 Reboot DeviceReboot `command:"reboot"`
112}
113
114var deviceOpts = DeviceOpts{}
115
116func RegisterDeviceCommands(parser *flags.Parser) {
David Bainbridge12f036f2019-10-15 22:09:04 +0000117 if _, err := parser.AddCommand("device", "device commands", "Commands to query and manipulate VOLTHA devices", &deviceOpts); err != nil {
118 log.Fatalf("Unexpected error while attempting to register device commands : %s", err)
119 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700120}
121
122func (i *DeviceId) Complete(match string) []flags.Completion {
123 conn, err := NewConnection()
124 if err != nil {
125 return nil
126 }
127 defer conn.Close()
128
129 descriptor, method, err := GetMethod("device-list")
130 if err != nil {
131 return nil
132 }
133
134 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
135 defer cancel()
136
137 h := &RpcEventHandler{}
138 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
139 if err != nil {
140 return nil
141 }
142
143 if h.Status != nil && h.Status.Err() != nil {
144 return nil
145 }
146
147 d, err := dynamic.AsDynamicMessage(h.Response)
148 if err != nil {
149 return nil
150 }
151
152 items, err := d.TryGetFieldByName("items")
153 if err != nil {
154 return nil
155 }
156
157 list := make([]flags.Completion, 0)
158 for _, item := range items.([]interface{}) {
159 val := item.(*dynamic.Message)
160 id := val.GetFieldByName("id").(string)
161 if strings.HasPrefix(id, match) {
162 list = append(list, flags.Completion{Item: id})
163 }
164 }
165
166 return list
167}
168
169func (options *DeviceList) Execute(args []string) error {
170
171 conn, err := NewConnection()
172 if err != nil {
173 return err
174 }
175 defer conn.Close()
176
177 descriptor, method, err := GetMethod("device-list")
178 if err != nil {
179 return err
180 }
181
182 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
183 defer cancel()
184
185 h := &RpcEventHandler{}
186 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
187 if err != nil {
188 return err
189 }
190
191 if h.Status != nil && h.Status.Err() != nil {
192 return h.Status.Err()
193 }
194
195 d, err := dynamic.AsDynamicMessage(h.Response)
196 if err != nil {
197 return err
198 }
199
200 items, err := d.TryGetFieldByName("items")
201 if err != nil {
202 return err
203 }
204
205 outputFormat := CharReplacer.Replace(options.Format)
206 if outputFormat == "" {
207 outputFormat = DEFAULT_DEVICE_FORMAT
208 }
209 if options.Quiet {
210 outputFormat = "{{.Id}}"
211 }
212
213 data := make([]model.Device, len(items.([]interface{})))
214 for i, item := range items.([]interface{}) {
215 val := item.(*dynamic.Message)
216 data[i].PopulateFrom(val)
217 }
218
219 result := CommandResult{
220 Format: format.Format(outputFormat),
221 Filter: options.Filter,
222 OrderBy: options.OrderBy,
223 OutputAs: toOutputType(options.OutputAs),
224 NameLimit: options.NameLimit,
225 Data: data,
226 }
227
228 GenerateOutput(&result)
229 return nil
230}
231
232func (options *DeviceCreate) Execute(args []string) error {
233
234 dm := make(map[string]interface{})
235 if options.HostAndPort != "" {
236 dm["host_and_port"] = options.HostAndPort
237 } else if options.IPAddress != "" {
238 dm["ipv4_address"] = options.IPAddress
239 } else if options.MACAddress != "" {
240 dm["mac_address"] = strings.ToLower(options.MACAddress)
241 }
242 if options.DeviceType != "" {
243 dm["type"] = options.DeviceType
244 }
245
246 conn, err := NewConnection()
247 if err != nil {
248 return err
249 }
250 defer conn.Close()
251
252 descriptor, method, err := GetMethod("device-create")
253 if err != nil {
254 return err
255 }
256
257 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
258 defer cancel()
259
260 h := &RpcEventHandler{
261 Fields: map[string]map[string]interface{}{"voltha.Device": dm},
262 }
263 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
264 if err != nil {
265 return err
266 } else if h.Status != nil && h.Status.Err() != nil {
267 return h.Status.Err()
268 }
269
270 resp, err := dynamic.AsDynamicMessage(h.Response)
271 if err != nil {
272 return err
273 }
274 fmt.Printf("%s\n", resp.GetFieldByName("id").(string))
275
276 return nil
277}
278
279func (options *DeviceDelete) Execute(args []string) error {
280
281 conn, err := NewConnection()
282 if err != nil {
283 return err
284 }
285 defer conn.Close()
286
287 descriptor, method, err := GetMethod("device-delete")
288 if err != nil {
289 return err
290 }
291
292 for _, i := range options.Args.Ids {
293
294 h := &RpcEventHandler{
295 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
296 }
297 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
298 defer cancel()
299
300 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
301 if err != nil {
302 fmt.Printf("Error while deleting '%s': %s\n", i, err)
303 continue
304 } else if h.Status != nil && h.Status.Err() != nil {
305 fmt.Printf("Error while deleting '%s': %s\n", i, h.Status.Err())
306 continue
307 }
308 fmt.Printf("%s\n", i)
309 }
310
311 return nil
312}
313
314func (options *DeviceEnable) Execute(args []string) error {
315 conn, err := NewConnection()
316 if err != nil {
317 return err
318 }
319 defer conn.Close()
320
321 descriptor, method, err := GetMethod("device-enable")
322 if err != nil {
323 return err
324 }
325
326 for _, i := range options.Args.Ids {
327 h := &RpcEventHandler{
328 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
329 }
330 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
331 defer cancel()
332
333 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
334 if err != nil {
335 fmt.Printf("Error while enabling '%s': %s\n", i, err)
336 continue
337 } else if h.Status != nil && h.Status.Err() != nil {
338 fmt.Printf("Error while enabling '%s': %s\n", i, h.Status.Err())
339 continue
340 }
341 fmt.Printf("%s\n", i)
342 }
343
344 return nil
345}
346
347func (options *DeviceDisable) Execute(args []string) error {
348 conn, err := NewConnection()
349 if err != nil {
350 return err
351 }
352 defer conn.Close()
353
354 descriptor, method, err := GetMethod("device-disable")
355 if err != nil {
356 return err
357 }
358
359 for _, i := range options.Args.Ids {
360 h := &RpcEventHandler{
361 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
362 }
363 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
364 defer cancel()
365
366 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
367 if err != nil {
368 fmt.Printf("Error while disabling '%s': %s\n", i, err)
369 continue
370 } else if h.Status != nil && h.Status.Err() != nil {
371 fmt.Printf("Error while disabling '%s': %s\n", i, h.Status.Err())
372 continue
373 }
374 fmt.Printf("%s\n", i)
375 }
376
377 return nil
378}
379
380func (options *DeviceReboot) Execute(args []string) error {
381 conn, err := NewConnection()
382 if err != nil {
383 return err
384 }
385 defer conn.Close()
386
387 descriptor, method, err := GetMethod("device-reboot")
388 if err != nil {
389 return err
390 }
391
392 for _, i := range options.Args.Ids {
393 h := &RpcEventHandler{
394 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": i}},
395 }
396 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
397 defer cancel()
398
399 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
400 if err != nil {
401 fmt.Printf("Error while rebooting '%s': %s\n", i, err)
402 continue
403 } else if h.Status != nil && h.Status.Err() != nil {
404 fmt.Printf("Error while rebooting '%s': %s\n", i, h.Status.Err())
405 continue
406 }
407 fmt.Printf("%s\n", i)
408 }
409
410 return nil
411}
412
413func (options *DevicePortList) Execute(args []string) error {
414
415 conn, err := NewConnection()
416 if err != nil {
417 return err
418 }
419 defer conn.Close()
420
421 descriptor, method, err := GetMethod("device-ports")
422 if err != nil {
423 return err
424 }
425
426 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
427 defer cancel()
428
429 h := &RpcEventHandler{
430 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
431 }
432 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
433 if err != nil {
434 return err
435 }
436
437 if h.Status != nil && h.Status.Err() != nil {
438 return h.Status.Err()
439 }
440
441 d, err := dynamic.AsDynamicMessage(h.Response)
442 if err != nil {
443 return err
444 }
445
446 items, err := d.TryGetFieldByName("items")
447 if err != nil {
448 return err
449 }
450
451 outputFormat := CharReplacer.Replace(options.Format)
452 if outputFormat == "" {
453 outputFormat = DEFAULT_DEVICE_PORTS_FORMAT
454 }
455 if options.Quiet {
456 outputFormat = "{{.Id}}"
457 }
458
459 data := make([]model.DevicePort, len(items.([]interface{})))
460 for i, item := range items.([]interface{}) {
461 data[i].PopulateFrom(item.(*dynamic.Message))
462 }
463
464 result := CommandResult{
465 Format: format.Format(outputFormat),
466 Filter: options.Filter,
467 OrderBy: options.OrderBy,
468 OutputAs: toOutputType(options.OutputAs),
469 NameLimit: options.NameLimit,
470 Data: data,
471 }
472
473 GenerateOutput(&result)
474 return nil
475}
476
477func (options *DeviceFlowList) Execute(args []string) error {
478 fl := &FlowList{}
479 fl.ListOutputOptions = options.ListOutputOptions
480 fl.Args.Id = string(options.Args.Id)
481 fl.Method = "device-flow-list"
482 return fl.Execute(args)
483}
484
485func (options *DeviceInspect) Execute(args []string) error {
486 if len(args) > 0 {
487 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
488 }
489
490 conn, err := NewConnection()
491 if err != nil {
492 return err
493 }
494 defer conn.Close()
495
496 descriptor, method, err := GetMethod("device-inspect")
497 if err != nil {
498 return err
499 }
500
501 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
502 defer cancel()
503
504 h := &RpcEventHandler{
505 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
506 }
507 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{"Get-Depth: 2"}, h, h.GetParams)
508 if err != nil {
509 return err
510 } else if h.Status != nil && h.Status.Err() != nil {
511 return h.Status.Err()
512 }
513
514 d, err := dynamic.AsDynamicMessage(h.Response)
515 if err != nil {
516 return err
517 }
518
519 device := &model.Device{}
520 device.PopulateFrom(d)
521
522 outputFormat := CharReplacer.Replace(options.Format)
523 if outputFormat == "" {
524 outputFormat = DEFAULT_DEVICE_INSPECT_FORMAT
525 }
526 if options.Quiet {
527 outputFormat = "{{.Id}}"
528 }
529
530 result := CommandResult{
531 Format: format.Format(outputFormat),
532 OutputAs: toOutputType(options.OutputAs),
533 NameLimit: options.NameLimit,
534 Data: device,
535 }
536 GenerateOutput(&result)
537 return nil
538}