blob: da841cd7745c9c09a53c939fcf0a49a13728d775 [file] [log] [blame]
Matteo Scandolo8df63df2019-09-12 10:34:32 -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 */
17
18package commands
19
20import (
21 "context"
Matteo Scandolo10f965c2019-09-24 10:40:46 -070022 "fmt"
Anand S Katti09541352020-01-29 15:54:01 +053023 "os"
24 "strconv"
25 "strings"
26
Matteo Scandolo8df63df2019-09-12 10:34:32 -070027 "github.com/jessevdk/go-flags"
Anand S Katti09541352020-01-29 15:54:01 +053028 "github.com/olekukonko/tablewriter"
Matteo Scandolo8df63df2019-09-12 10:34:32 -070029 pb "github.com/opencord/bbsim/api/bbsim"
30 "github.com/opencord/bbsim/internal/bbsimctl/config"
31 "github.com/opencord/cordctl/pkg/format"
32 log "github.com/sirupsen/logrus"
33 "google.golang.org/grpc"
Matteo Scandolo8df63df2019-09-12 10:34:32 -070034)
35
36const (
Matteo Scandolo4a036262020-08-17 15:56:13 -070037 DEFAULT_ONU_DEVICE_HEADER_FORMAT = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .PortNo }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}"
38 DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .PortNo }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .Services }}"
Matteo Scandolo8df63df2019-09-12 10:34:32 -070039)
40
Matteo Scandolo10f965c2019-09-24 10:40:46 -070041type OnuSnString string
Arjun E K57a7fcb2020-01-30 06:44:45 +000042type IgmpSubAction string
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000043type GroupAddress string
Arjun E K57a7fcb2020-01-30 06:44:45 +000044
45const IgmpJoinKey string = "join"
46const IgmpLeaveKey string = "leave"
Arjun E Kdd443f02020-02-07 15:24:01 +000047const IgmpJoinKeyV3 string = "joinv3"
Arjun E K57a7fcb2020-01-30 06:44:45 +000048
Matteo Scandolo618a6582020-09-09 12:21:29 -070049var IgmpAllowedActions = []string{IgmpJoinKey, IgmpLeaveKey, IgmpJoinKeyV3}
50
Matteo Scandolo4a036262020-08-17 15:56:13 -070051type ONUList struct {
52 Verbose bool `short:"v" long:"verbose" description:"Print all the informations we have about ONUs"`
53}
Matteo Scandolod2ca2c72019-10-04 16:50:22 -070054
55type ONUGet struct {
Matteo Scandolo4a036262020-08-17 15:56:13 -070056 Verbose bool `short:"v" long:"verbose" description:"Print all the informations we have about ONUs"`
57 Args struct {
58 OnuSn OnuSnString
59 } `positional-args:"yes" required:"yes"`
60}
61
62type ONUServices struct {
Matteo Scandolod2ca2c72019-10-04 16:50:22 -070063 Args struct {
64 OnuSn OnuSnString
65 } `positional-args:"yes" required:"yes"`
66}
67
Matteo Scandolo10f965c2019-09-24 10:40:46 -070068type ONUShutDown struct {
69 Args struct {
70 OnuSn OnuSnString
71 } `positional-args:"yes" required:"yes"`
Matteo Scandolo8df63df2019-09-12 10:34:32 -070072}
73
Matteo Scandolo10f965c2019-09-24 10:40:46 -070074type ONUPowerOn struct {
75 Args struct {
76 OnuSn OnuSnString
77 } `positional-args:"yes" required:"yes"`
78}
79
Matteo Scandoloe383d5d2019-10-25 14:47:27 -070080type ONUEapolRestart struct {
81 Args struct {
82 OnuSn OnuSnString
83 } `positional-args:"yes" required:"yes"`
84}
85
86type ONUDhcpRestart struct {
87 Args struct {
88 OnuSn OnuSnString
89 } `positional-args:"yes" required:"yes"`
90}
91
Arjun E K57a7fcb2020-01-30 06:44:45 +000092type ONUIgmp struct {
93 Args struct {
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000094 OnuSn OnuSnString
95 SubAction IgmpSubAction
96 GroupAddress GroupAddress
Arjun E K57a7fcb2020-01-30 06:44:45 +000097 } `positional-args:"yes" required:"yes"`
98}
99
Pragya Arya8bdb4532020-03-02 17:08:09 +0530100type ONUTrafficSchedulers struct {
101 Args struct {
102 OnuSn OnuSnString
103 } `positional-args:"yes" required:"yes"`
104}
105
106type ONUFlows struct {
107 Args struct {
108 OnuSn OnuSnString
109 } `positional-args:"yes" required:"yes"`
110}
111
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700112type ONUOptions struct {
Anand S Katti09541352020-01-29 15:54:01 +0530113 List ONUList `command:"list"`
114 Get ONUGet `command:"get"`
Matteo Scandolo4a036262020-08-17 15:56:13 -0700115 Services ONUServices `command:"services"`
Anand S Katti09541352020-01-29 15:54:01 +0530116 ShutDown ONUShutDown `command:"shutdown"`
117 PowerOn ONUPowerOn `command:"poweron"`
118 RestartEapol ONUEapolRestart `command:"auth_restart"`
119 RestartDchp ONUDhcpRestart `command:"dhcp_restart"`
120 Igmp ONUIgmp `command:"igmp"`
121 TrafficSchedulers ONUTrafficSchedulers `command:"traffic_schedulers"`
Anand S Katti86552f92020-03-03 21:56:32 +0530122 Alarms AlarmOptions `command:"alarms"`
Pragya Arya8bdb4532020-03-02 17:08:09 +0530123 Flows ONUFlows `command:"flows"`
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700124}
125
126func RegisterONUCommands(parser *flags.Parser) {
Shrey Baid688b4242020-07-10 20:40:10 +0530127 _, _ = parser.AddCommand("onu", "ONU Commands", "Commands to query and manipulate ONU devices", &ONUOptions{})
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700128}
129
130func connect() (pb.BBSimClient, *grpc.ClientConn) {
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700131 conn, err := grpc.Dial(config.GlobalConfig.Server, grpc.WithInsecure())
132
133 if err != nil {
Matteo Scandolo2bf742a2019-10-01 11:33:34 -0700134 log.Fatalf("did not connect: %v", err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700135 return nil, conn
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700136 }
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700137 return pb.NewBBSimClient(conn), conn
138}
139
140func getONUs() *pb.ONUs {
141
142 client, conn := connect()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700143 defer conn.Close()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700144
145 // Contact the server and print out its response.
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700146 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
147 defer cancel()
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700148
149 onus, err := client.GetONUs(ctx, &pb.Empty{})
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700150 if err != nil {
Matteo Scandolo2bf742a2019-10-01 11:33:34 -0700151 log.Fatalf("could not get OLT: %v", err)
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700152 return nil
153 }
154 return onus
155}
156
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700157func (options *ONUList) Execute(args []string) error {
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700158 onus := getONUs()
159
160 // print out
Matteo Scandolo4a036262020-08-17 15:56:13 -0700161 var tableFormat format.Format
162 if options.Verbose {
163 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
164 } else {
165 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
166 }
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700167 if err := tableFormat.Execute(os.Stdout, true, onus.Items); err != nil {
168 log.Fatalf("Error while formatting ONUs table: %s", err)
169 }
170
171 return nil
172}
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700173
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700174func (options *ONUGet) Execute(args []string) error {
175 client, conn := connect()
176 defer conn.Close()
177
178 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
179 defer cancel()
180 req := pb.ONURequest{
181 SerialNumber: string(options.Args.OnuSn),
182 }
183 res, err := client.GetONU(ctx, &req)
184
185 if err != nil {
Matteo Scandolo4a036262020-08-17 15:56:13 -0700186 log.Fatalf("Cannot not get ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700187 return err
188 }
189
Matteo Scandolo4a036262020-08-17 15:56:13 -0700190 var tableFormat format.Format
191 if options.Verbose {
192 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
193 } else {
194 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
195 }
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700196 if err := tableFormat.Execute(os.Stdout, true, []*pb.ONU{res}); err != nil {
197 log.Fatalf("Error while formatting ONUs table: %s", err)
198 }
199
200 return nil
201}
202
Matteo Scandolo4a036262020-08-17 15:56:13 -0700203func (options *ONUServices) Execute(args []string) error {
204
205 client, conn := connect()
206 defer conn.Close()
207
208 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
209 defer cancel()
210 req := pb.ONURequest{
211 SerialNumber: string(options.Args.OnuSn),
212 }
213 res, err := client.GetOnuServices(ctx, &req)
214
215 if err != nil {
216 log.Fatalf("Cannot not get services for ONU %s: %v", options.Args.OnuSn, err)
217 return err
218 }
219
220 tableFormat := format.Format(DEFAULT_SERVICE_HEADER_FORMAT)
221 if err := tableFormat.Execute(os.Stdout, true, res.Items); err != nil {
222 log.Fatalf("Error while formatting Services table: %s", err)
223 }
224
225 return nil
226}
227
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700228func (options *ONUShutDown) Execute(args []string) error {
229
230 client, conn := connect()
231 defer conn.Close()
232
233 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
234 defer cancel()
235 req := pb.ONURequest{
236 SerialNumber: string(options.Args.OnuSn),
237 }
238 res, err := client.ShutdownONU(ctx, &req)
239
240 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700241 log.Fatalf("Cannot shutdown ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700242 return err
243 }
244
245 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
246
247 return nil
248}
249
250func (options *ONUPowerOn) Execute(args []string) error {
251 client, conn := connect()
252 defer conn.Close()
253
254 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
255 defer cancel()
256 req := pb.ONURequest{
257 SerialNumber: string(options.Args.OnuSn),
258 }
259 res, err := client.PoweronONU(ctx, &req)
260
261 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700262 log.Fatalf("Cannot power on ONU %s: %v", options.Args.OnuSn, err)
263 return err
264 }
265
266 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
267
268 return nil
269}
270
271func (options *ONUEapolRestart) Execute(args []string) error {
272 client, conn := connect()
273 defer conn.Close()
274
275 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
276 defer cancel()
277 req := pb.ONURequest{
278 SerialNumber: string(options.Args.OnuSn),
279 }
280 res, err := client.RestartEapol(ctx, &req)
281
282 if err != nil {
283 log.Fatalf("Cannot restart EAPOL for ONU %s: %v", options.Args.OnuSn, err)
284 return err
285 }
286
287 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
288
289 return nil
290}
291
292func (options *ONUDhcpRestart) Execute(args []string) error {
293 client, conn := connect()
294 defer conn.Close()
295
296 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
297 defer cancel()
298 req := pb.ONURequest{
299 SerialNumber: string(options.Args.OnuSn),
300 }
301 res, err := client.RestartDhcp(ctx, &req)
302
303 if err != nil {
304 log.Fatalf("Cannot restart DHCP for ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700305 return err
306 }
307
308 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
309
310 return nil
311}
312
Arjun E K57a7fcb2020-01-30 06:44:45 +0000313func (options *ONUIgmp) Execute(args []string) error {
314 client, conn := connect()
315 defer conn.Close()
316
317 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
318 defer cancel()
319
320 req := pb.ONURequest{
321 SerialNumber: string(options.Args.OnuSn),
322 }
323
324 var subActionVal pb.SubActionTypes
325 if string(options.Args.SubAction) == IgmpJoinKey {
326 subActionVal = pb.SubActionTypes_JOIN
327 } else if string(options.Args.SubAction) == IgmpLeaveKey {
328 subActionVal = pb.SubActionTypes_LEAVE
Anand S Katti09541352020-01-29 15:54:01 +0530329 } else if string(options.Args.SubAction) == IgmpJoinKeyV3 {
330 subActionVal = pb.SubActionTypes_JOINV3
331 }
Arjun E K57a7fcb2020-01-30 06:44:45 +0000332
333 igmpReq := pb.IgmpRequest{
334 OnuReq: &req,
335 SubActionVal: subActionVal,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000336 GroupAddress: string(options.Args.GroupAddress),
Arjun E K57a7fcb2020-01-30 06:44:45 +0000337 }
338 res, err := client.GetONU(ctx, igmpReq.OnuReq)
339 if err != nil {
340 log.WithFields(log.Fields{
341 "SerialNumber": options.Args.OnuSn,
342 }).Errorf("Cannot not get details on ONU error: %v", err)
343 }
344 log.WithFields(log.Fields{
345 "SerialNumber": igmpReq.OnuReq.SerialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000346 }).Debugf("ONU has identified : %s", res)
Arjun E K57a7fcb2020-01-30 06:44:45 +0000347
348 igmpRes, igmpErr := client.ChangeIgmpState(ctx, &igmpReq)
349 if igmpErr != nil {
350 log.WithFields(log.Fields{
351 "SubAction": options.Args.SubAction,
352 }).Errorf("Could not process Action: error: %v", igmpErr)
353 } else {
354 log.WithFields(log.Fields{
355 "SubAction": options.Args.SubAction,
356 }).Debugf("igmp state has been changed with response: %s",
357 igmpRes.Message)
358 }
359
360 return nil
361}
362
Pragya Arya8bdb4532020-03-02 17:08:09 +0530363func (options *ONUFlows) Execute(args []string) error {
364 client, conn := connect()
365 defer conn.Close()
366
367 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
368 defer cancel()
369 req := pb.ONURequest{
370 SerialNumber: string(options.Args.OnuSn),
371 }
372 res, err := client.GetFlows(ctx, &req)
373 if err != nil {
374 log.Errorf("Cannot get flows for ONU %s: %v", options.Args.OnuSn, err)
375 return err
376 }
377
378 if res.Flows == nil {
379 fmt.Println(fmt.Sprintf("ONU %s has no flows", options.Args.OnuSn))
380 return nil
381 }
382
383 flowHeader := []string{
384 "access_intf_id",
385 "onu_id",
386 "uni_id",
387 "flow_id",
388 "flow_type",
389 "eth_type",
390 "alloc_id",
391 "network_intf_id",
392 "gemport_id",
393 "classifier",
394 "action",
395 "priority",
396 "cookie",
397 "port_no",
398 }
399
400 tableFlow := tablewriter.NewWriter(os.Stdout)
401 tableFlow.SetRowLine(true)
402 fmt.Fprintf(os.Stdout, "ONU Flows:\n")
403 tableFlow.SetHeader(flowHeader)
404
405 for _, flow := range res.Flows {
406 flowInfo := []string{}
407 flowInfo = append(flowInfo,
408 strconv.Itoa(int(flow.AccessIntfId)),
409 strconv.Itoa(int(flow.OnuId)),
410 strconv.Itoa(int(flow.UniId)),
411 strconv.Itoa(int(flow.FlowId)),
412 flow.FlowType,
413 fmt.Sprintf("%x", flow.Classifier.EthType),
414 strconv.Itoa(int(flow.AllocId)),
415 strconv.Itoa(int(flow.NetworkIntfId)),
416 strconv.Itoa(int(flow.GemportId)),
417 flow.Classifier.String(),
418 flow.Action.String(),
419 strconv.Itoa(int(flow.Priority)),
420 strconv.Itoa(int(flow.Cookie)),
421 strconv.Itoa(int(flow.PortNo)),
422 )
423 tableFlow.Append(flowInfo)
424 }
425 tableFlow.Render()
426 tableFlow.SetNewLine("")
427 return nil
428}
429
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700430func (onuSn *OnuSnString) Complete(match string) []flags.Completion {
431 client, conn := connect()
432 defer conn.Close()
433
434 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
435 defer cancel()
436
437 onus, err := client.GetONUs(ctx, &pb.Empty{})
438 if err != nil {
Matteo Scandolo86e8ce62019-10-11 12:03:10 -0700439 log.Fatalf("could not get ONUs: %v", err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700440 return nil
441 }
442
443 list := make([]flags.Completion, 0)
444 for _, k := range onus.Items {
445 if strings.HasPrefix(k.SerialNumber, match) {
446 list = append(list, flags.Completion{Item: k.SerialNumber})
447 }
448 }
449
450 return list
451}
Anand S Katti09541352020-01-29 15:54:01 +0530452
Matteo Scandolo618a6582020-09-09 12:21:29 -0700453func (onuSn *IgmpSubAction) Complete(match string) []flags.Completion {
454 list := make([]flags.Completion, 0)
455 for _, k := range IgmpAllowedActions {
456 if strings.HasPrefix(k, match) {
457 list = append(list, flags.Completion{Item: k})
458 }
459 }
460 return list
461}
462
Anand S Katti09541352020-01-29 15:54:01 +0530463func (options *ONUTrafficSchedulers) Execute(args []string) error {
464 client, conn := connect()
465 defer conn.Close()
466
467 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
468 defer cancel()
469 req := pb.ONURequest{
470 SerialNumber: string(options.Args.OnuSn),
471 }
472 res, err := client.GetOnuTrafficSchedulers(ctx, &req)
473 if err != nil {
474 log.Fatalf("Cannot get traffic schedulers for ONU %s: %v", options.Args.OnuSn, err)
475 return err
476 }
477
478 if res.TraffSchedulers == nil {
479 log.Fatalf("Cannot get traffic schedulers for ONU: %s (unavailable)", options.Args.OnuSn)
480 return nil
481 }
482
483 SchedulerHeader := []string{"Direction",
484 "AllocId",
485 "Scheduler.Direction",
486 "Scheduler.AdditionalBw",
487 "Scheduler.Priority",
488 "Scheduler.Weight",
489 "Scheduler.SchedPolicy",
490 }
491
492 ShapingInfoHeader := []string{"InferredAdditionBwIndication",
493 "Cbs",
494 "Cir",
495 "Gir",
496 "Pbs",
497 "Pir",
498 }
499
500 SchedulerVals := []string{}
501 ShapingInfoVals := []string{}
502 for _, v := range res.TraffSchedulers.TrafficScheds {
503 SchedulerVals = append(SchedulerVals,
504 v.GetDirection().String(),
505 strconv.Itoa(int(v.GetAllocId())),
506 v.Scheduler.GetDirection().String(),
507 v.Scheduler.GetAdditionalBw().String(),
508 strconv.Itoa(int(v.Scheduler.GetPriority())),
509 strconv.Itoa(int(v.Scheduler.GetWeight())),
510 v.GetScheduler().GetSchedPolicy().String(),
511 )
512
513 ShapingInfoVals = append(ShapingInfoVals,
514 v.TrafficShapingInfo.GetAddBwInd().String(),
515 strconv.Itoa(int(v.TrafficShapingInfo.GetCbs())),
516 strconv.Itoa(int(v.TrafficShapingInfo.GetCir())),
517 strconv.Itoa(int(v.TrafficShapingInfo.GetGir())),
518 strconv.Itoa(int(v.TrafficShapingInfo.GetPbs())),
519 strconv.Itoa(int(v.TrafficShapingInfo.GetPir())),
520 )
521 }
522
523 fmt.Fprintf(os.Stdout, "OnuId: %d \n", int(res.TraffSchedulers.OnuId))
524 fmt.Fprintf(os.Stdout, "IntfId: %d \n", int(res.TraffSchedulers.IntfId))
525 fmt.Fprintf(os.Stdout, "UniId: %d \n", int(res.TraffSchedulers.UniId))
526 fmt.Fprintf(os.Stdout, "OnuPortNo: %d \n", int(res.TraffSchedulers.PortNo))
527
528 tableSched := tablewriter.NewWriter(os.Stdout)
529 tableSched.SetRowLine(true)
530 fmt.Fprintf(os.Stdout, "Traffic Schedulers Info:\n")
531 tableSched.SetHeader(SchedulerHeader)
532 tableSched.Append(SchedulerVals)
533 tableSched.Render()
534 tableSched.SetNewLine("")
535
536 tableShap := tablewriter.NewWriter(os.Stdout)
537 tableShap.SetRowLine(true)
538 fmt.Fprintf(os.Stdout, "Traffic Shaping Info:\n")
539 tableShap.SetHeader(ShapingInfoHeader)
540 tableShap.Append(ShapingInfoVals)
541 tableShap.Render()
542
543 return nil
544}