blob: 33bd5efa980399a10671c8b605c07b85b44b985f [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 Scandolocedde462021-03-09 17:37:16 -080036 DEFAULT_ONU_DEVICE_HEADER_FORMAT = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .PortNo }}\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{{ .PortNo }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .ImageSoftwareExpectedSections }}\t{{ .ImageSoftwareReceivedSections }}\t{{ .ActiveImageEntityId }}\t{{ .CommittedImageEntityId }}\t{{ .Services }}"
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
61type ONUServices struct {
Matteo Scandolod2ca2c72019-10-04 16:50:22 -070062 Args struct {
63 OnuSn OnuSnString
64 } `positional-args:"yes" required:"yes"`
65}
66
Matteo Scandolo10f965c2019-09-24 10:40:46 -070067type ONUShutDown struct {
68 Args struct {
69 OnuSn OnuSnString
70 } `positional-args:"yes" required:"yes"`
Matteo Scandolo8df63df2019-09-12 10:34:32 -070071}
72
Matteo Scandolo10f965c2019-09-24 10:40:46 -070073type ONUPowerOn struct {
74 Args struct {
75 OnuSn OnuSnString
76 } `positional-args:"yes" required:"yes"`
77}
78
Matteo Scandoloe383d5d2019-10-25 14:47:27 -070079type ONUEapolRestart struct {
80 Args struct {
81 OnuSn OnuSnString
82 } `positional-args:"yes" required:"yes"`
83}
84
85type ONUDhcpRestart struct {
86 Args struct {
87 OnuSn OnuSnString
88 } `positional-args:"yes" required:"yes"`
89}
90
Arjun E K57a7fcb2020-01-30 06:44:45 +000091type ONUIgmp struct {
92 Args struct {
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000093 OnuSn OnuSnString
94 SubAction IgmpSubAction
95 GroupAddress GroupAddress
Arjun E K57a7fcb2020-01-30 06:44:45 +000096 } `positional-args:"yes" required:"yes"`
97}
98
Pragya Arya8bdb4532020-03-02 17:08:09 +053099type ONUTrafficSchedulers struct {
100 Args struct {
101 OnuSn OnuSnString
102 } `positional-args:"yes" required:"yes"`
103}
104
105type ONUFlows struct {
106 Args struct {
107 OnuSn OnuSnString
108 } `positional-args:"yes" required:"yes"`
109}
110
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700111type ONUOptions struct {
Anand S Katti09541352020-01-29 15:54:01 +0530112 List ONUList `command:"list"`
113 Get ONUGet `command:"get"`
Matteo Scandolo4a036262020-08-17 15:56:13 -0700114 Services ONUServices `command:"services"`
Anand S Katti09541352020-01-29 15:54:01 +0530115 ShutDown ONUShutDown `command:"shutdown"`
116 PowerOn ONUPowerOn `command:"poweron"`
117 RestartEapol ONUEapolRestart `command:"auth_restart"`
118 RestartDchp ONUDhcpRestart `command:"dhcp_restart"`
119 Igmp ONUIgmp `command:"igmp"`
120 TrafficSchedulers ONUTrafficSchedulers `command:"traffic_schedulers"`
Anand S Katti86552f92020-03-03 21:56:32 +0530121 Alarms AlarmOptions `command:"alarms"`
Pragya Arya8bdb4532020-03-02 17:08:09 +0530122 Flows ONUFlows `command:"flows"`
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700123}
124
125func RegisterONUCommands(parser *flags.Parser) {
Shrey Baid688b4242020-07-10 20:40:10 +0530126 _, _ = parser.AddCommand("onu", "ONU Commands", "Commands to query and manipulate ONU devices", &ONUOptions{})
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700127}
128
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700129func getONUs() *pb.ONUs {
130
131 client, conn := connect()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700132 defer conn.Close()
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700133
134 // Contact the server and print out its response.
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700135 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
136 defer cancel()
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700137
138 onus, err := client.GetONUs(ctx, &pb.Empty{})
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700139 if err != nil {
Matteo Scandolo2bf742a2019-10-01 11:33:34 -0700140 log.Fatalf("could not get OLT: %v", err)
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700141 return nil
142 }
143 return onus
144}
145
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700146func (options *ONUList) Execute(args []string) error {
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700147 onus := getONUs()
148
149 // print out
Matteo Scandolo4a036262020-08-17 15:56:13 -0700150 var tableFormat format.Format
151 if options.Verbose {
152 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
153 } else {
154 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
155 }
Matteo Scandolo8df63df2019-09-12 10:34:32 -0700156 if err := tableFormat.Execute(os.Stdout, true, onus.Items); err != nil {
157 log.Fatalf("Error while formatting ONUs table: %s", err)
158 }
159
160 return nil
161}
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700162
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700163func (options *ONUGet) Execute(args []string) error {
164 client, conn := connect()
165 defer conn.Close()
166
167 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
168 defer cancel()
169 req := pb.ONURequest{
170 SerialNumber: string(options.Args.OnuSn),
171 }
172 res, err := client.GetONU(ctx, &req)
173
174 if err != nil {
Matteo Scandolo4a036262020-08-17 15:56:13 -0700175 log.Fatalf("Cannot not get ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700176 return err
177 }
178
Matteo Scandolo4a036262020-08-17 15:56:13 -0700179 var tableFormat format.Format
180 if options.Verbose {
181 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT_WITH_SERVICES)
182 } else {
183 tableFormat = format.Format(DEFAULT_ONU_DEVICE_HEADER_FORMAT)
184 }
Matteo Scandolod2ca2c72019-10-04 16:50:22 -0700185 if err := tableFormat.Execute(os.Stdout, true, []*pb.ONU{res}); err != nil {
186 log.Fatalf("Error while formatting ONUs table: %s", err)
187 }
188
189 return nil
190}
191
Matteo Scandolo4a036262020-08-17 15:56:13 -0700192func (options *ONUServices) Execute(args []string) error {
193
194 client, conn := connect()
195 defer conn.Close()
196
197 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
198 defer cancel()
199 req := pb.ONURequest{
200 SerialNumber: string(options.Args.OnuSn),
201 }
202 res, err := client.GetOnuServices(ctx, &req)
203
204 if err != nil {
205 log.Fatalf("Cannot not get services for ONU %s: %v", options.Args.OnuSn, err)
206 return err
207 }
208
209 tableFormat := format.Format(DEFAULT_SERVICE_HEADER_FORMAT)
210 if err := tableFormat.Execute(os.Stdout, true, res.Items); err != nil {
211 log.Fatalf("Error while formatting Services table: %s", err)
212 }
213
214 return nil
215}
216
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700217func (options *ONUShutDown) Execute(args []string) error {
218
219 client, conn := connect()
220 defer conn.Close()
221
222 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
223 defer cancel()
224 req := pb.ONURequest{
225 SerialNumber: string(options.Args.OnuSn),
226 }
227 res, err := client.ShutdownONU(ctx, &req)
228
229 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700230 log.Fatalf("Cannot shutdown ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700231 return err
232 }
233
234 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
235
236 return nil
237}
238
239func (options *ONUPowerOn) Execute(args []string) error {
240 client, conn := connect()
241 defer conn.Close()
242
243 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
244 defer cancel()
245 req := pb.ONURequest{
246 SerialNumber: string(options.Args.OnuSn),
247 }
248 res, err := client.PoweronONU(ctx, &req)
249
250 if err != nil {
Matteo Scandoloe383d5d2019-10-25 14:47:27 -0700251 log.Fatalf("Cannot power on ONU %s: %v", options.Args.OnuSn, err)
252 return err
253 }
254
255 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
256
257 return nil
258}
259
260func (options *ONUEapolRestart) Execute(args []string) error {
261 client, conn := connect()
262 defer conn.Close()
263
264 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
265 defer cancel()
266 req := pb.ONURequest{
267 SerialNumber: string(options.Args.OnuSn),
268 }
269 res, err := client.RestartEapol(ctx, &req)
270
271 if err != nil {
272 log.Fatalf("Cannot restart EAPOL for ONU %s: %v", options.Args.OnuSn, err)
273 return err
274 }
275
276 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
277
278 return nil
279}
280
281func (options *ONUDhcpRestart) Execute(args []string) error {
282 client, conn := connect()
283 defer conn.Close()
284
285 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
286 defer cancel()
287 req := pb.ONURequest{
288 SerialNumber: string(options.Args.OnuSn),
289 }
290 res, err := client.RestartDhcp(ctx, &req)
291
292 if err != nil {
293 log.Fatalf("Cannot restart DHCP for ONU %s: %v", options.Args.OnuSn, err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700294 return err
295 }
296
297 fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
298
299 return nil
300}
301
Arjun E K57a7fcb2020-01-30 06:44:45 +0000302func (options *ONUIgmp) Execute(args []string) error {
303 client, conn := connect()
304 defer conn.Close()
305
306 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
307 defer cancel()
308
309 req := pb.ONURequest{
310 SerialNumber: string(options.Args.OnuSn),
311 }
312
313 var subActionVal pb.SubActionTypes
314 if string(options.Args.SubAction) == IgmpJoinKey {
315 subActionVal = pb.SubActionTypes_JOIN
316 } else if string(options.Args.SubAction) == IgmpLeaveKey {
317 subActionVal = pb.SubActionTypes_LEAVE
Anand S Katti09541352020-01-29 15:54:01 +0530318 } else if string(options.Args.SubAction) == IgmpJoinKeyV3 {
319 subActionVal = pb.SubActionTypes_JOINV3
320 }
Arjun E K57a7fcb2020-01-30 06:44:45 +0000321
322 igmpReq := pb.IgmpRequest{
323 OnuReq: &req,
324 SubActionVal: subActionVal,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000325 GroupAddress: string(options.Args.GroupAddress),
Arjun E K57a7fcb2020-01-30 06:44:45 +0000326 }
327 res, err := client.GetONU(ctx, igmpReq.OnuReq)
328 if err != nil {
329 log.WithFields(log.Fields{
330 "SerialNumber": options.Args.OnuSn,
331 }).Errorf("Cannot not get details on ONU error: %v", err)
332 }
333 log.WithFields(log.Fields{
334 "SerialNumber": igmpReq.OnuReq.SerialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000335 }).Debugf("ONU has identified : %s", res)
Arjun E K57a7fcb2020-01-30 06:44:45 +0000336
337 igmpRes, igmpErr := client.ChangeIgmpState(ctx, &igmpReq)
338 if igmpErr != nil {
339 log.WithFields(log.Fields{
340 "SubAction": options.Args.SubAction,
341 }).Errorf("Could not process Action: error: %v", igmpErr)
342 } else {
343 log.WithFields(log.Fields{
344 "SubAction": options.Args.SubAction,
345 }).Debugf("igmp state has been changed with response: %s",
346 igmpRes.Message)
347 }
348
349 return nil
350}
351
Pragya Arya8bdb4532020-03-02 17:08:09 +0530352func (options *ONUFlows) Execute(args []string) error {
353 client, conn := connect()
354 defer conn.Close()
355
356 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
357 defer cancel()
358 req := pb.ONURequest{
359 SerialNumber: string(options.Args.OnuSn),
360 }
361 res, err := client.GetFlows(ctx, &req)
362 if err != nil {
363 log.Errorf("Cannot get flows for ONU %s: %v", options.Args.OnuSn, err)
364 return err
365 }
366
367 if res.Flows == nil {
368 fmt.Println(fmt.Sprintf("ONU %s has no flows", options.Args.OnuSn))
369 return nil
370 }
371
372 flowHeader := []string{
373 "access_intf_id",
374 "onu_id",
375 "uni_id",
376 "flow_id",
377 "flow_type",
378 "eth_type",
379 "alloc_id",
380 "network_intf_id",
381 "gemport_id",
382 "classifier",
383 "action",
384 "priority",
385 "cookie",
386 "port_no",
387 }
388
389 tableFlow := tablewriter.NewWriter(os.Stdout)
390 tableFlow.SetRowLine(true)
391 fmt.Fprintf(os.Stdout, "ONU Flows:\n")
392 tableFlow.SetHeader(flowHeader)
393
394 for _, flow := range res.Flows {
395 flowInfo := []string{}
396 flowInfo = append(flowInfo,
397 strconv.Itoa(int(flow.AccessIntfId)),
398 strconv.Itoa(int(flow.OnuId)),
399 strconv.Itoa(int(flow.UniId)),
400 strconv.Itoa(int(flow.FlowId)),
401 flow.FlowType,
402 fmt.Sprintf("%x", flow.Classifier.EthType),
403 strconv.Itoa(int(flow.AllocId)),
404 strconv.Itoa(int(flow.NetworkIntfId)),
405 strconv.Itoa(int(flow.GemportId)),
406 flow.Classifier.String(),
407 flow.Action.String(),
408 strconv.Itoa(int(flow.Priority)),
409 strconv.Itoa(int(flow.Cookie)),
410 strconv.Itoa(int(flow.PortNo)),
411 )
412 tableFlow.Append(flowInfo)
413 }
414 tableFlow.Render()
415 tableFlow.SetNewLine("")
416 return nil
417}
418
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700419func (onuSn *OnuSnString) Complete(match string) []flags.Completion {
420 client, conn := connect()
421 defer conn.Close()
422
423 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
424 defer cancel()
425
426 onus, err := client.GetONUs(ctx, &pb.Empty{})
427 if err != nil {
Matteo Scandolo86e8ce62019-10-11 12:03:10 -0700428 log.Fatalf("could not get ONUs: %v", err)
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700429 return nil
430 }
431
432 list := make([]flags.Completion, 0)
433 for _, k := range onus.Items {
434 if strings.HasPrefix(k.SerialNumber, match) {
435 list = append(list, flags.Completion{Item: k.SerialNumber})
436 }
437 }
438
439 return list
440}
Anand S Katti09541352020-01-29 15:54:01 +0530441
Matteo Scandolo618a6582020-09-09 12:21:29 -0700442func (onuSn *IgmpSubAction) Complete(match string) []flags.Completion {
443 list := make([]flags.Completion, 0)
444 for _, k := range IgmpAllowedActions {
445 if strings.HasPrefix(k, match) {
446 list = append(list, flags.Completion{Item: k})
447 }
448 }
449 return list
450}
451
Anand S Katti09541352020-01-29 15:54:01 +0530452func (options *ONUTrafficSchedulers) Execute(args []string) error {
453 client, conn := connect()
454 defer conn.Close()
455
456 ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
457 defer cancel()
458 req := pb.ONURequest{
459 SerialNumber: string(options.Args.OnuSn),
460 }
461 res, err := client.GetOnuTrafficSchedulers(ctx, &req)
462 if err != nil {
463 log.Fatalf("Cannot get traffic schedulers for ONU %s: %v", options.Args.OnuSn, err)
464 return err
465 }
466
467 if res.TraffSchedulers == nil {
468 log.Fatalf("Cannot get traffic schedulers for ONU: %s (unavailable)", options.Args.OnuSn)
469 return nil
470 }
471
472 SchedulerHeader := []string{"Direction",
473 "AllocId",
474 "Scheduler.Direction",
475 "Scheduler.AdditionalBw",
476 "Scheduler.Priority",
477 "Scheduler.Weight",
478 "Scheduler.SchedPolicy",
479 }
480
481 ShapingInfoHeader := []string{"InferredAdditionBwIndication",
482 "Cbs",
483 "Cir",
484 "Gir",
485 "Pbs",
486 "Pir",
487 }
488
489 SchedulerVals := []string{}
490 ShapingInfoVals := []string{}
491 for _, v := range res.TraffSchedulers.TrafficScheds {
492 SchedulerVals = append(SchedulerVals,
493 v.GetDirection().String(),
494 strconv.Itoa(int(v.GetAllocId())),
495 v.Scheduler.GetDirection().String(),
496 v.Scheduler.GetAdditionalBw().String(),
497 strconv.Itoa(int(v.Scheduler.GetPriority())),
498 strconv.Itoa(int(v.Scheduler.GetWeight())),
499 v.GetScheduler().GetSchedPolicy().String(),
500 )
501
502 ShapingInfoVals = append(ShapingInfoVals,
503 v.TrafficShapingInfo.GetAddBwInd().String(),
504 strconv.Itoa(int(v.TrafficShapingInfo.GetCbs())),
505 strconv.Itoa(int(v.TrafficShapingInfo.GetCir())),
506 strconv.Itoa(int(v.TrafficShapingInfo.GetGir())),
507 strconv.Itoa(int(v.TrafficShapingInfo.GetPbs())),
508 strconv.Itoa(int(v.TrafficShapingInfo.GetPir())),
509 )
510 }
511
512 fmt.Fprintf(os.Stdout, "OnuId: %d \n", int(res.TraffSchedulers.OnuId))
513 fmt.Fprintf(os.Stdout, "IntfId: %d \n", int(res.TraffSchedulers.IntfId))
514 fmt.Fprintf(os.Stdout, "UniId: %d \n", int(res.TraffSchedulers.UniId))
515 fmt.Fprintf(os.Stdout, "OnuPortNo: %d \n", int(res.TraffSchedulers.PortNo))
516
517 tableSched := tablewriter.NewWriter(os.Stdout)
518 tableSched.SetRowLine(true)
519 fmt.Fprintf(os.Stdout, "Traffic Schedulers Info:\n")
520 tableSched.SetHeader(SchedulerHeader)
521 tableSched.Append(SchedulerVals)
522 tableSched.Render()
523 tableSched.SetNewLine("")
524
525 tableShap := tablewriter.NewWriter(os.Stdout)
526 tableShap.SetRowLine(true)
527 fmt.Fprintf(os.Stdout, "Traffic Shaping Info:\n")
528 tableShap.SetHeader(ShapingInfoHeader)
529 tableShap.Append(ShapingInfoVals)
530 tableShap.Render()
531
532 return nil
533}