blob: f929f39e679b177ec4c43988d47733af8a598759 [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"
Matteo Scandolo8df63df2019-09-12 10:34:32 -070033)
34
35const (
Matteo Scandolo8a574812021-05-20 15:18:53 -070036 DEFAULT_ONU_DEVICE_HEADER_FORMAT = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .ImageSoftwareExpectedSections }}\t{{ .ImageSoftwareReceivedSections }}\t{{ .ActiveImageEntityId }}\t{{ .CommittedImageEntityId }}"
37 DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .ImageSoftwareExpectedSections }}\t{{ .ImageSoftwareReceivedSections }}\t{{ .ActiveImageEntityId }}\t{{ .CommittedImageEntityId }}\t{{ .Unis }}"
Matteo Scandolo8df63df2019-09-12 10:34:32 -070038)
39
Matteo Scandolo10f965c2019-09-24 10:40:46 -070040type OnuSnString string
Arjun E K57a7fcb2020-01-30 06:44:45 +000041type IgmpSubAction string
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000042type GroupAddress string
Arjun E K57a7fcb2020-01-30 06:44:45 +000043
44const IgmpJoinKey string = "join"
45const IgmpLeaveKey string = "leave"
Arjun E Kdd443f02020-02-07 15:24:01 +000046const IgmpJoinKeyV3 string = "joinv3"
Arjun E K57a7fcb2020-01-30 06:44:45 +000047
Matteo Scandolo618a6582020-09-09 12:21:29 -070048var IgmpAllowedActions = []string{IgmpJoinKey, IgmpLeaveKey, IgmpJoinKeyV3}
49
Matteo Scandolo4a036262020-08-17 15:56:13 -070050type ONUList struct {
51 Verbose bool `short:"v" long:"verbose" description:"Print all the informations we have about ONUs"`
52}
Matteo Scandolod2ca2c72019-10-04 16:50:22 -070053
54type ONUGet struct {
Matteo Scandolo4a036262020-08-17 15:56:13 -070055 Verbose bool `short:"v" long:"verbose" description:"Print all the informations we have about ONUs"`
56 Args struct {
57 OnuSn OnuSnString
58 } `positional-args:"yes" required:"yes"`
59}
60
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070061type ONUUnis struct {
Matteo Scandolo8a574812021-05-20 15:18:53 -070062 Verbose bool `short:"v" long:"verbose" description:"Print all the informations we have about UNIs"`
63 Args struct {
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070064 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
Nitin Subramanianb0a333a2021-07-08 15:01:41 -0700112type ONUServices struct {
113 Args struct {
114 OnuSn OnuSnString
115 } `positional-args:"yes"`
116}
117
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700118type ONUOptions struct {
Anand S Katti09541352020-01-29 15:54:01 +0530119 List ONUList `command:"list"`
120 Get ONUGet `command:"get"`
121 ShutDown ONUShutDown `command:"shutdown"`
122 PowerOn ONUPowerOn `command:"poweron"`
123 RestartEapol ONUEapolRestart `command:"auth_restart"`
124 RestartDchp ONUDhcpRestart `command:"dhcp_restart"`
125 Igmp ONUIgmp `command:"igmp"`
126 TrafficSchedulers ONUTrafficSchedulers `command:"traffic_schedulers"`
Anand S Katti86552f92020-03-03 21:56:32 +0530127 Alarms AlarmOptions `command:"alarms"`
Pragya Arya8bdb4532020-03-02 17:08:09 +0530128 Flows ONUFlows `command:"flows"`
Nitin Subramanianb0a333a2021-07-08 15:01:41 -0700129 Services ONUServices `command:"services"`
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700130}
131
132func RegisterONUCommands(parser *flags.Parser) {
Shrey Baid688b4242020-07-10 20:40:10 +0530133 _, _ = parser.AddCommand("onu", "ONU Commands", "Commands to query and manipulate ONU devices", &ONUOptions{})
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700134}
135
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700136func getONUs() *pb.ONUs {
137
138 client, conn := connect()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700139 defer conn.Close()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700140
141 // Contact the server and print out its response.
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700142 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
143 defer cancel()
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700144
145 onus, err := client.GetONUs(ctx, &pb.Empty{})
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700146 if err != nil {
Matteo Scandolo2bf742a2019-10-01 11:33:34 -0700147 log.Fatalf("could not get OLT: %v", err)
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700148 return nil
149 }
150 return onus
151}
152
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700153func (options *ONUList) Execute(args []string) error {
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700154 onus := getONUs()
155
156 // print out
Matteo Scandolo4a036262020-08-17 15:56:13 -0700157 var tableFormat format.Format
158 if options.Verbose {
159 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
160 } else {
161 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
162 }
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700163 if err := tableFormat.Execute(os.Stdout, true, onus.Items); err != nil {
164 log.Fatalf("Error while formatting ONUs table: %s", err)
165 }
166
167 return nil
168}
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700169
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700170func (options *ONUGet) Execute(args []string) error {
171 client, conn := connect()
172 defer conn.Close()
173
174 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
175 defer cancel()
176 req := pb.ONURequest{
177 SerialNumber: string(options.Args.OnuSn),
178 }
179 res, err := client.GetONU(ctx, &req)
180
181 if err != nil {
Matteo Scandolo4a036262020-08-17 15:56:13 -0700182 log.Fatalf("Cannot not get ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700183 return err
184 }
185
Matteo Scandolo4a036262020-08-17 15:56:13 -0700186 var tableFormat format.Format
187 if options.Verbose {
188 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
189 } else {
190 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
191 }
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700192 if err := tableFormat.Execute(os.Stdout, true, []*pb.ONU{res}); err != nil {
193 log.Fatalf("Error while formatting ONUs table: %s", err)
194 }
195
196 return nil
197}
198
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700199func (options *ONUShutDown) Execute(args []string) error {
200
201 client, conn := connect()
202 defer conn.Close()
203
204 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
205 defer cancel()
206 req := pb.ONURequest{
207 SerialNumber: string(options.Args.OnuSn),
208 }
209 res, err := client.ShutdownONU(ctx, &req)
210
211 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700212 log.Fatalf("Cannot shutdown ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700213 return err
214 }
215
216 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
217
218 return nil
219}
220
221func (options *ONUPowerOn) Execute(args []string) error {
222 client, conn := connect()
223 defer conn.Close()
224
225 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
226 defer cancel()
227 req := pb.ONURequest{
228 SerialNumber: string(options.Args.OnuSn),
229 }
230 res, err := client.PoweronONU(ctx, &req)
231
232 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700233 log.Fatalf("Cannot power on ONU %s: %v", options.Args.OnuSn, err)
234 return err
235 }
236
237 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
238
239 return nil
240}
241
242func (options *ONUEapolRestart) Execute(args []string) error {
243 client, conn := connect()
244 defer conn.Close()
245
246 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
247 defer cancel()
Nitin Subramanian150f1bb2021-08-02 12:04:05 -0700248 req := pb.UNIRequest{
249 OnuSerialNumber: string(options.Args.OnuSn),
250 UniID: "",
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700251 }
252 res, err := client.RestartEapol(ctx, &req)
253
254 if err != nil {
255 log.Fatalf("Cannot restart EAPOL for ONU %s: %v", options.Args.OnuSn, err)
256 return err
257 }
258
259 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
260
261 return nil
262}
263
264func (options *ONUDhcpRestart) Execute(args []string) error {
265 client, conn := connect()
266 defer conn.Close()
267
268 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
269 defer cancel()
Nitin Subramanian150f1bb2021-08-02 12:04:05 -0700270 req := pb.UNIRequest{
271 OnuSerialNumber: string(options.Args.OnuSn),
272 UniID: "",
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700273 }
274 res, err := client.RestartDhcp(ctx, &req)
275
276 if err != nil {
277 log.Fatalf("Cannot restart DHCP for ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700278 return err
279 }
280
281 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
282
283 return nil
284}
285
Arjun E K57a7fcb2020-01-30 06:44:45 +0000286func (options *ONUIgmp) Execute(args []string) error {
287 client, conn := connect()
288 defer conn.Close()
289
290 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
291 defer cancel()
292
293 req := pb.ONURequest{
294 SerialNumber: string(options.Args.OnuSn),
295 }
296
297 var subActionVal pb.SubActionTypes
298 if string(options.Args.SubAction) == IgmpJoinKey {
299 subActionVal = pb.SubActionTypes_JOIN
300 } else if string(options.Args.SubAction) == IgmpLeaveKey {
301 subActionVal = pb.SubActionTypes_LEAVE
Anand S Katti09541352020-01-29 15:54:01 +0530302 } else if string(options.Args.SubAction) == IgmpJoinKeyV3 {
303 subActionVal = pb.SubActionTypes_JOINV3
304 }
Arjun E K57a7fcb2020-01-30 06:44:45 +0000305
306 igmpReq := pb.IgmpRequest{
307 OnuReq: &req,
308 SubActionVal: subActionVal,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000309 GroupAddress: string(options.Args.GroupAddress),
Arjun E K57a7fcb2020-01-30 06:44:45 +0000310 }
311 res, err := client.GetONU(ctx, igmpReq.OnuReq)
312 if err != nil {
313 log.WithFields(log.Fields{
314 "SerialNumber": options.Args.OnuSn,
315 }).Errorf("Cannot not get details on ONU error: %v", err)
316 }
317 log.WithFields(log.Fields{
318 "SerialNumber": igmpReq.OnuReq.SerialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000319 }).Debugf("ONU has identified : %s", res)
Arjun E K57a7fcb2020-01-30 06:44:45 +0000320
321 igmpRes, igmpErr := client.ChangeIgmpState(ctx, &igmpReq)
322 if igmpErr != nil {
323 log.WithFields(log.Fields{
324 "SubAction": options.Args.SubAction,
325 }).Errorf("Could not process Action: error: %v", igmpErr)
326 } else {
327 log.WithFields(log.Fields{
328 "SubAction": options.Args.SubAction,
329 }).Debugf("igmp state has been changed with response: %s",
330 igmpRes.Message)
331 }
332
333 return nil
334}
335
Pragya Arya8bdb4532020-03-02 17:08:09 +0530336func (options *ONUFlows) Execute(args []string) error {
337 client, conn := connect()
338 defer conn.Close()
339
340 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
341 defer cancel()
342 req := pb.ONURequest{
343 SerialNumber: string(options.Args.OnuSn),
344 }
345 res, err := client.GetFlows(ctx, &req)
346 if err != nil {
347 log.Errorf("Cannot get flows for ONU %s: %v", options.Args.OnuSn, err)
348 return err
349 }
350
351 if res.Flows == nil {
352 fmt.Println(fmt.Sprintf("ONU %s has no flows", options.Args.OnuSn))
353 return nil
354 }
355
356 flowHeader := []string{
357 "access_intf_id",
358 "onu_id",
359 "uni_id",
360 "flow_id",
361 "flow_type",
362 "eth_type",
363 "alloc_id",
364 "network_intf_id",
365 "gemport_id",
366 "classifier",
367 "action",
368 "priority",
369 "cookie",
370 "port_no",
371 }
372
373 tableFlow := tablewriter.NewWriter(os.Stdout)
374 tableFlow.SetRowLine(true)
375 fmt.Fprintf(os.Stdout, "ONU Flows:\n")
376 tableFlow.SetHeader(flowHeader)
377
378 for _, flow := range res.Flows {
379 flowInfo := []string{}
380 flowInfo = append(flowInfo,
381 strconv.Itoa(int(flow.AccessIntfId)),
382 strconv.Itoa(int(flow.OnuId)),
383 strconv.Itoa(int(flow.UniId)),
384 strconv.Itoa(int(flow.FlowId)),
385 flow.FlowType,
386 fmt.Sprintf("%x", flow.Classifier.EthType),
387 strconv.Itoa(int(flow.AllocId)),
388 strconv.Itoa(int(flow.NetworkIntfId)),
389 strconv.Itoa(int(flow.GemportId)),
390 flow.Classifier.String(),
391 flow.Action.String(),
392 strconv.Itoa(int(flow.Priority)),
393 strconv.Itoa(int(flow.Cookie)),
394 strconv.Itoa(int(flow.PortNo)),
395 )
396 tableFlow.Append(flowInfo)
397 }
398 tableFlow.Render()
399 tableFlow.SetNewLine("")
400 return nil
401}
402
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700403func (onuSn *OnuSnString) Complete(match string) []flags.Completion {
404 client, conn := connect()
405 defer conn.Close()
406
407 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
408 defer cancel()
409
410 onus, err := client.GetONUs(ctx, &pb.Empty{})
411 if err != nil {
Matteo Scandolo86e8ce62019-10-11 12:03:10 -0700412 log.Fatalf("could not get ONUs: %v", err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700413 return nil
414 }
415
416 list := make([]flags.Completion, 0)
417 for _, k := range onus.Items {
418 if strings.HasPrefix(k.SerialNumber, match) {
419 list = append(list, flags.Completion{Item: k.SerialNumber})
420 }
421 }
422
423 return list
424}
Anand S Katti09541352020-01-29 15:54:01 +0530425
Matteo Scandolo618a6582020-09-09 12:21:29 -0700426func (onuSn *IgmpSubAction) Complete(match string) []flags.Completion {
427 list := make([]flags.Completion, 0)
428 for _, k := range IgmpAllowedActions {
429 if strings.HasPrefix(k, match) {
430 list = append(list, flags.Completion{Item: k})
431 }
432 }
433 return list
434}
435
Anand S Katti09541352020-01-29 15:54:01 +0530436func (options *ONUTrafficSchedulers) Execute(args []string) error {
437 client, conn := connect()
438 defer conn.Close()
439
440 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
441 defer cancel()
442 req := pb.ONURequest{
443 SerialNumber: string(options.Args.OnuSn),
444 }
445 res, err := client.GetOnuTrafficSchedulers(ctx, &req)
446 if err != nil {
447 log.Fatalf("Cannot get traffic schedulers for ONU %s: %v", options.Args.OnuSn, err)
448 return err
449 }
450
451 if res.TraffSchedulers == nil {
452 log.Fatalf("Cannot get traffic schedulers for ONU: %s (unavailable)", options.Args.OnuSn)
453 return nil
454 }
455
456 SchedulerHeader := []string{"Direction",
457 "AllocId",
458 "Scheduler.Direction",
459 "Scheduler.AdditionalBw",
460 "Scheduler.Priority",
461 "Scheduler.Weight",
462 "Scheduler.SchedPolicy",
463 }
464
465 ShapingInfoHeader := []string{"InferredAdditionBwIndication",
466 "Cbs",
467 "Cir",
468 "Gir",
469 "Pbs",
470 "Pir",
471 }
472
473 SchedulerVals := []string{}
474 ShapingInfoVals := []string{}
475 for _, v := range res.TraffSchedulers.TrafficScheds {
476 SchedulerVals = append(SchedulerVals,
477 v.GetDirection().String(),
478 strconv.Itoa(int(v.GetAllocId())),
479 v.Scheduler.GetDirection().String(),
480 v.Scheduler.GetAdditionalBw().String(),
481 strconv.Itoa(int(v.Scheduler.GetPriority())),
482 strconv.Itoa(int(v.Scheduler.GetWeight())),
483 v.GetScheduler().GetSchedPolicy().String(),
484 )
485
486 ShapingInfoVals = append(ShapingInfoVals,
487 v.TrafficShapingInfo.GetAddBwInd().String(),
488 strconv.Itoa(int(v.TrafficShapingInfo.GetCbs())),
489 strconv.Itoa(int(v.TrafficShapingInfo.GetCir())),
490 strconv.Itoa(int(v.TrafficShapingInfo.GetGir())),
491 strconv.Itoa(int(v.TrafficShapingInfo.GetPbs())),
492 strconv.Itoa(int(v.TrafficShapingInfo.GetPir())),
493 )
494 }
495
496 fmt.Fprintf(os.Stdout, "OnuId: %d \n", int(res.TraffSchedulers.OnuId))
497 fmt.Fprintf(os.Stdout, "IntfId: %d \n", int(res.TraffSchedulers.IntfId))
498 fmt.Fprintf(os.Stdout, "UniId: %d \n", int(res.TraffSchedulers.UniId))
499 fmt.Fprintf(os.Stdout, "OnuPortNo: %d \n", int(res.TraffSchedulers.PortNo))
500
501 tableSched := tablewriter.NewWriter(os.Stdout)
502 tableSched.SetRowLine(true)
503 fmt.Fprintf(os.Stdout, "Traffic Schedulers Info:\n")
504 tableSched.SetHeader(SchedulerHeader)
505 tableSched.Append(SchedulerVals)
506 tableSched.Render()
507 tableSched.SetNewLine("")
508
509 tableShap := tablewriter.NewWriter(os.Stdout)
510 tableShap.SetRowLine(true)
511 fmt.Fprintf(os.Stdout, "Traffic Shaping Info:\n")
512 tableShap.SetHeader(ShapingInfoHeader)
513 tableShap.Append(ShapingInfoVals)
514 tableShap.Render()
515
516 return nil
517}
Nitin Subramanianb0a333a2021-07-08 15:01:41 -0700518
519func (options *ONUServices) Execute(args []string) error {
520 services, err := getServices(string(options.Args.OnuSn), "")
521
522 if err != nil {
523 log.Errorf("Cannot get services for ONU %s: %v", options.Args.OnuSn, err)
524 return err
525 }
526
527 // print out
528 tableFormat := format.Format(DEFAULT_SERVICE_HEADER_FORMAT)
529 if err := tableFormat.Execute(os.Stdout, true, services.Items); err != nil {
530 log.Fatalf("Error while formatting ONUs table: %s", err)
531 }
532
533 return nil
534}