RICPLT-3008 Subscription register free id list
[ric-plt/submgr.git] / pkg / control / control.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20 package control
21
22 import (
23         "fmt"
24         "gerrit.o-ran-sc.org/r/ric-plt/e2ap/pkg/e2ap"
25         rtmgrclient "gerrit.o-ran-sc.org/r/ric-plt/submgr/pkg/rtmgr_client"
26         rtmgrhandle "gerrit.o-ran-sc.org/r/ric-plt/submgr/pkg/rtmgr_client/handle"
27         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
28         httptransport "github.com/go-openapi/runtime/client"
29         "github.com/go-openapi/strfmt"
30         "github.com/spf13/viper"
31         "sync"
32         "time"
33 )
34
35 //-----------------------------------------------------------------------------
36 //
37 //-----------------------------------------------------------------------------
38
39 var subReqTime time.Duration = 5 * time.Second
40 var subDelReqTime time.Duration = 5 * time.Second
41 var maxSubReqTryCount uint64 = 2    // Initial try + retry
42 var maxSubDelReqTryCount uint64 = 2 // Initial try + retry
43
44 type Control struct {
45         e2ap         *E2ap
46         registry     *Registry
47         tracker      *Tracker
48         timerMap     *TimerMap
49         rmrSendMutex sync.Mutex
50         msgCounter   uint64
51 }
52
53 type RMRMeid struct {
54         PlmnID  string
55         EnbID   string
56         RanName string
57 }
58
59 const (
60         CREATE Action = 0
61         MERGE  Action = 1
62         NONE   Action = 2
63         DELETE Action = 3
64 )
65
66 func init() {
67         xapp.Logger.Info("SUBMGR")
68         viper.AutomaticEnv()
69         viper.SetEnvPrefix("submgr")
70         viper.AllowEmptyEnv(true)
71 }
72
73 func NewControl() *Control {
74
75         transport := httptransport.New(viper.GetString("rtmgr.HostAddr")+":"+viper.GetString("rtmgr.port"), viper.GetString("rtmgr.baseUrl"), []string{"http"})
76         client := rtmgrclient.New(transport, strfmt.Default)
77         handle := rtmgrhandle.NewProvideXappSubscriptionHandleParamsWithTimeout(10 * time.Second)
78         deleteHandle := rtmgrhandle.NewDeleteXappSubscriptionHandleParamsWithTimeout(10 * time.Second)
79         rtmgrClient := RtmgrClient{client, handle, deleteHandle}
80
81         registry := new(Registry)
82         registry.Initialize()
83         registry.rtmgrClient = &rtmgrClient
84
85         tracker := new(Tracker)
86         tracker.Init()
87
88         timerMap := new(TimerMap)
89         timerMap.Init()
90
91         return &Control{e2ap: new(E2ap),
92                 registry:   registry,
93                 tracker:    tracker,
94                 timerMap:   timerMap,
95                 msgCounter: 0,
96         }
97 }
98
99 func (c *Control) Run() {
100         xapp.Run(c)
101 }
102
103 func (c *Control) rmrSendRaw(desc string, params *RMRParams) (err error) {
104
105         xapp.Logger.Info("%s: %s", desc, params.String())
106         status := false
107         i := 1
108         for ; i <= 10 && status == false; i++ {
109                 c.rmrSendMutex.Lock()
110                 status = xapp.Rmr.Send(params.RMRParams, false)
111                 c.rmrSendMutex.Unlock()
112                 if status == false {
113                         xapp.Logger.Info("rmr.Send() failed. Retry count %d, %s", i, params.String())
114                         time.Sleep(500 * time.Millisecond)
115                 }
116         }
117         if status == false {
118                 err = fmt.Errorf("rmr.Send() failed. Retry count %d, %s", i, params.String())
119                 xapp.Logger.Error("%s: %s", desc, err.Error())
120                 xapp.Rmr.Free(params.Mbuf)
121         }
122         return
123 }
124
125 func (c *Control) rmrSend(desc string, subs *Subscription, trans *Transaction) (err error) {
126         params := &RMRParams{&xapp.RMRParams{}}
127         params.Mtype = trans.GetMtype()
128         params.SubId = int(subs.GetSubId())
129         params.Xid = ""
130         params.Meid = subs.GetMeid()
131         params.Src = ""
132         params.PayloadLen = len(trans.Payload.Buf)
133         params.Payload = trans.Payload.Buf
134         params.Mbuf = nil
135
136         return c.rmrSendRaw(desc, params)
137 }
138
139 func (c *Control) rmrReplyToSender(desc string, subs *Subscription, trans *Transaction) (err error) {
140         params := &RMRParams{&xapp.RMRParams{}}
141         params.Mtype = trans.GetMtype()
142         params.SubId = int(subs.GetSubId())
143         params.Xid = trans.GetXid()
144         params.Meid = trans.GetMeid()
145         params.Src = ""
146         params.PayloadLen = len(trans.Payload.Buf)
147         params.Payload = trans.Payload.Buf
148         params.Mbuf = nil
149
150         return c.rmrSendRaw(desc, params)
151 }
152
153 func (c *Control) Consume(params *xapp.RMRParams) (err error) {
154         xapp.Rmr.Free(params.Mbuf)
155         params.Mbuf = nil
156         msg := &RMRParams{params}
157         c.msgCounter++
158         switch msg.Mtype {
159         case xapp.RICMessageTypes["RIC_SUB_REQ"]:
160                 go c.handleSubscriptionRequest(msg)
161         case xapp.RICMessageTypes["RIC_SUB_RESP"]:
162                 go c.handleSubscriptionResponse(msg)
163         case xapp.RICMessageTypes["RIC_SUB_FAILURE"]:
164                 go c.handleSubscriptionFailure(msg)
165         case xapp.RICMessageTypes["RIC_SUB_DEL_REQ"]:
166                 go c.handleSubscriptionDeleteRequest(msg)
167         case xapp.RICMessageTypes["RIC_SUB_DEL_RESP"]:
168                 go c.handleSubscriptionDeleteResponse(msg)
169         case xapp.RICMessageTypes["RIC_SUB_DEL_FAILURE"]:
170                 go c.handleSubscriptionDeleteFailure(msg)
171         default:
172                 xapp.Logger.Info("Unknown Message Type '%d', discarding", msg.Mtype)
173         }
174
175         return nil
176 }
177
178 func (c *Control) handleSubscriptionRequest(params *RMRParams) {
179         xapp.Logger.Info("SubReq from xapp: %s", params.String())
180
181         //
182         //
183         //
184         trans, err := c.tracker.TrackTransaction(NewRmrEndpoint(params.Src),
185                 params.Xid,
186                 params.Meid,
187                 false,
188                 true)
189
190         if err != nil {
191                 xapp.Logger.Error("SubReq: %s, Dropping this msg. %s", err.Error(), params.String())
192                 return
193         }
194
195         //
196         //
197         //
198         trans.SubReqMsg, err = c.e2ap.UnpackSubscriptionRequest(params.Payload)
199         if err != nil {
200                 xapp.Logger.Error("SubReq: %s Dropping this msg. %s", err.Error(), trans)
201                 trans.Release()
202                 return
203         }
204
205         //
206         //
207         //
208         subs, err := c.registry.ReserveSubscription(&trans.RmrEndpoint, trans.Meid)
209         if err != nil {
210                 xapp.Logger.Error("SubReq: %s, Dropping this msg. %s", err.Error(), trans)
211                 trans.Release()
212                 return
213         }
214
215         err = subs.SetTransaction(trans)
216         if err != nil {
217                 xapp.Logger.Error("SubReq: %s, Dropping this msg. %s", err.Error(), trans)
218                 subs.Release()
219                 trans.Release()
220                 return
221         }
222
223         trans.SubReqMsg.RequestId.Seq = uint32(subs.GetSubId())
224
225         //
226         // TODO: subscription create is in fact owned by subscription and not transaction.
227         //       Transaction is toward xapp while Subscription is toward ran.
228         //       In merge several xapps may wake transactions, while only one subscription
229         //       toward ran occurs -> subscription owns subscription creation toward ran
230         //
231         //       This is intermediate solution while improving message handling
232         //
233         trans.Mtype, trans.Payload, err = c.e2ap.PackSubscriptionRequest(trans.SubReqMsg)
234         if err != nil {
235                 xapp.Logger.Error("SubReq: %s for trans %s", err.Error(), trans)
236                 subs.Release()
237                 trans.Release()
238                 return
239         }
240
241         c.rmrSend("SubReq: SubReq to E2T", subs, trans)
242
243         c.timerMap.StartTimer("RIC_SUB_REQ", int(subs.GetSubId()), subReqTime, FirstTry, c.handleSubscriptionRequestTimer)
244         xapp.Logger.Debug("SubReq: Debugging trans table = %v", c.tracker.transactionXappTable)
245         return
246 }
247
248 func (c *Control) handleSubscriptionResponse(params *RMRParams) {
249         xapp.Logger.Info("SubResp from E2T: %s", params.String())
250
251         //
252         //
253         //
254         SubRespMsg, err := c.e2ap.UnpackSubscriptionResponse(params.Payload)
255         if err != nil {
256                 xapp.Logger.Error("SubResp: %s Dropping this msg. %s", err.Error(), params.String())
257                 return
258         }
259
260         //
261         //
262         //
263         subs := c.registry.GetSubscription(uint16(SubRespMsg.RequestId.Seq))
264         if subs == nil && params.SubId > 0 {
265                 subs = c.registry.GetSubscription(uint16(params.SubId))
266         }
267
268         if subs == nil {
269                 xapp.Logger.Error("SubResp: Not valid subscription found payloadSeqNum: %d, SubId: %d. Dropping this msg. %s", SubRespMsg.RequestId.Seq, params.SubId, params.String())
270                 return
271         }
272         xapp.Logger.Info("SubResp: subscription found payloadSeqNum: %d, SubId: %d", SubRespMsg.RequestId.Seq, subs.GetSubId())
273
274         //
275         //
276         //
277         trans := subs.GetTransaction()
278         if trans == nil {
279                 xapp.Logger.Error("SubResp: Unknown trans. Dropping this msg. SubId: %d", subs.GetSubId())
280                 return
281         }
282
283         trans.SubRespMsg = SubRespMsg
284
285         //
286         //
287         //
288         c.timerMap.StopTimer("RIC_SUB_REQ", int(subs.GetSubId()))
289
290         responseReceived := trans.CheckResponseReceived()
291         if responseReceived == true {
292                 // Subscription timer already received
293                 return
294         }
295
296         trans.Mtype, trans.Payload, err = c.e2ap.PackSubscriptionResponse(trans.SubRespMsg)
297         if err != nil {
298                 xapp.Logger.Error("SubResp: %s for trans %s", err.Error(), trans)
299                 trans.Release()
300                 return
301         }
302
303         subs.Confirmed()
304         trans.Release()
305         c.rmrReplyToSender("SubResp: SubResp to xapp", subs, trans)
306         return
307 }
308
309 func (c *Control) handleSubscriptionFailure(params *RMRParams) {
310         xapp.Logger.Info("SubFail from E2T: %s", params.String())
311
312         //
313         //
314         //
315         SubFailMsg, err := c.e2ap.UnpackSubscriptionFailure(params.Payload)
316         if err != nil {
317                 xapp.Logger.Error("SubFail: %s Dropping this msg. %s", err.Error(), params.String())
318                 return
319         }
320
321         //
322         //
323         //
324         subs := c.registry.GetSubscription(uint16(SubFailMsg.RequestId.Seq))
325         if subs == nil && params.SubId > 0 {
326                 subs = c.registry.GetSubscription(uint16(params.SubId))
327         }
328
329         if subs == nil {
330                 xapp.Logger.Error("SubFail: Not valid subscription found payloadSeqNum: %d, SubId: %d. Dropping this msg. %s", SubFailMsg.RequestId.Seq, params.SubId, params.String())
331                 return
332         }
333         xapp.Logger.Info("SubFail: subscription found payloadSeqNum: %d, SubId: %d", SubFailMsg.RequestId.Seq, subs.GetSubId())
334
335         //
336         //
337         //
338         trans := subs.GetTransaction()
339         if trans == nil {
340                 xapp.Logger.Error("SubFail: Unknown trans. Dropping this msg. SubId: %d", subs.GetSubId())
341                 return
342         }
343         trans.SubFailMsg = SubFailMsg
344
345         //
346         //
347         //
348         c.timerMap.StopTimer("RIC_SUB_REQ", int(subs.GetSubId()))
349
350         responseReceived := trans.CheckResponseReceived()
351         if err != nil {
352                 return
353         }
354
355         if responseReceived == true {
356                 // Subscription timer already received
357                 return
358         }
359
360         trans.Mtype, trans.Payload, err = c.e2ap.PackSubscriptionFailure(trans.SubFailMsg)
361         if err == nil {
362                 c.rmrReplyToSender("SubFail: SubFail to xapp", subs, trans)
363                 time.Sleep(3 * time.Second)
364         } else {
365                 //TODO error handling improvement
366                 xapp.Logger.Error("SubFail: %s for trans %s (continuing cleaning)", err.Error(), trans)
367         }
368
369         trans.Release()
370         subs.Release()
371         return
372 }
373
374 func (c *Control) handleSubscriptionRequestTimer(strId string, nbrId int, tryCount uint64) {
375         xapp.Logger.Info("SubReq timeout: subId: %v,  tryCount: %v", nbrId, tryCount)
376
377         subs := c.registry.GetSubscription(uint16(nbrId))
378         if subs == nil {
379                 xapp.Logger.Error("SubReq timeout: Unknown payloadSeqNum. Dropping this msg. SubId: %v", nbrId)
380                 return
381         }
382
383         trans := subs.GetTransaction()
384         if trans == nil {
385                 xapp.Logger.Error("SubReq timeout: Unknown trans. Dropping this msg. SubId: %v", subs.GetSubId())
386                 return
387         }
388
389         responseReceived := trans.CheckResponseReceived()
390
391         if responseReceived == true {
392                 // Subscription Response or Failure already received
393                 return
394         }
395
396         if tryCount < maxSubReqTryCount {
397                 xapp.Logger.Info("SubReq timeout: subs: %s trans: %s", subs, trans)
398
399                 trans.RetryTransaction()
400
401                 c.rmrSend("SubReq timeout: SubReq to E2T", subs, trans)
402
403                 tryCount++
404                 c.timerMap.StartTimer("RIC_SUB_REQ", int(subs.GetSubId()), subReqTime, tryCount, c.handleSubscriptionRequestTimer)
405                 return
406         }
407
408         // Release CREATE transaction
409         trans.Release()
410
411         // Create DELETE transaction (internal and no messages toward xapp)
412         deltrans, err := c.tracker.TrackTransaction(&trans.RmrEndpoint,
413                 trans.GetXid(),
414                 trans.GetMeid(),
415                 false,
416                 false)
417
418         if err != nil {
419                 xapp.Logger.Error("SubReq timeout: %s, Dropping this msg.", err.Error())
420                 //TODO improve error handling. Important at least in merge
421                 subs.Release()
422                 return
423         }
424
425         deltrans.SubDelReqMsg = &e2ap.E2APSubscriptionDeleteRequest{}
426         deltrans.SubDelReqMsg.RequestId.Id = trans.SubReqMsg.RequestId.Id
427         deltrans.SubDelReqMsg.RequestId.Seq = uint32(subs.GetSubId())
428         deltrans.SubDelReqMsg.FunctionId = trans.SubReqMsg.FunctionId
429         deltrans.Mtype, deltrans.Payload, err = c.e2ap.PackSubscriptionDeleteRequest(deltrans.SubDelReqMsg)
430         if err != nil {
431                 xapp.Logger.Error("SubReq timeout: Packing SubDelReq failed. Err: %v", err)
432                 //TODO improve error handling. Important at least in merge
433                 deltrans.Release()
434                 subs.Release()
435                 return
436         }
437
438         err = subs.SetTransaction(deltrans)
439         if err != nil {
440                 xapp.Logger.Error("SubReq timeout: %s, Dropping this msg.", err.Error())
441                 //TODO improve error handling. Important at least in merge
442                 deltrans.Release()
443                 return
444         }
445
446         c.rmrSend("SubReq timer: SubDelReq to E2T", subs, deltrans)
447         c.timerMap.StartTimer("RIC_SUB_DEL_REQ", int(subs.GetSubId()), subDelReqTime, FirstTry, c.handleSubscriptionDeleteRequestTimer)
448         return
449 }
450
451 func (c *Control) handleSubscriptionDeleteRequest(params *RMRParams) {
452         xapp.Logger.Info("SubDelReq from xapp: %s", params.String())
453
454         //
455         //
456         //
457         trans, err := c.tracker.TrackTransaction(NewRmrEndpoint(params.Src),
458                 params.Xid,
459                 params.Meid,
460                 false,
461                 true)
462
463         if err != nil {
464                 xapp.Logger.Error("SubDelReq: %s, Dropping this msg. %s", err.Error(), params.String())
465                 return
466         }
467
468         //
469         //
470         //
471         trans.SubDelReqMsg, err = c.e2ap.UnpackSubscriptionDeleteRequest(params.Payload)
472         if err != nil {
473                 xapp.Logger.Error("SubDelReq: %s Dropping this msg. %s", err.Error(), trans)
474                 trans.Release()
475                 return
476         }
477
478         //
479         //
480         //
481         subs := c.registry.GetSubscription(uint16(trans.SubDelReqMsg.RequestId.Seq))
482         if subs == nil && params.SubId > 0 {
483                 subs = c.registry.GetSubscription(uint16(params.SubId))
484         }
485
486         if subs == nil {
487                 xapp.Logger.Error("SubDelReq: Not valid subscription found payloadSeqNum: %d, SubId: %d. Dropping this msg. %s", trans.SubDelReqMsg.RequestId.Seq, params.SubId, trans)
488                 trans.Release()
489                 return
490         }
491         xapp.Logger.Info("SubDelReq: subscription found payloadSeqNum: %d, SubId: %d. %s", trans.SubDelReqMsg.RequestId.Seq, params.SubId, trans)
492
493         err = subs.SetTransaction(trans)
494         if err != nil {
495                 xapp.Logger.Error("SubDelReq: %s, Dropping this msg. %s", err.Error(), trans)
496                 trans.Release()
497                 return
498         }
499
500         //
501         // TODO: subscription delete is in fact owned by subscription and not transaction.
502         //       Transaction is toward xapp while Subscription is toward ran.
503         //       In merge several xapps may wake transactions, while only one subscription
504         //       toward ran occurs -> subscription owns subscription creation toward ran
505         //
506         //       This is intermediate solution while improving message handling
507         //
508         trans.Mtype, trans.Payload, err = c.e2ap.PackSubscriptionDeleteRequest(trans.SubDelReqMsg)
509         if err != nil {
510                 xapp.Logger.Error("SubDelReq: %s for trans %s", err.Error(), trans)
511                 trans.Release()
512                 return
513         }
514
515         subs.UnConfirmed()
516
517         c.rmrSend("SubDelReq: SubDelReq to E2T", subs, trans)
518
519         c.timerMap.StartTimer("RIC_SUB_DEL_REQ", int(subs.GetSubId()), subDelReqTime, FirstTry, c.handleSubscriptionDeleteRequestTimer)
520         return
521 }
522
523 func (c *Control) handleSubscriptionDeleteResponse(params *RMRParams) (err error) {
524         xapp.Logger.Info("SubDelResp from E2T:%s", params.String())
525
526         //
527         //
528         //
529         SubDelRespMsg, err := c.e2ap.UnpackSubscriptionDeleteResponse(params.Payload)
530         if err != nil {
531                 xapp.Logger.Error("SubDelResp: %s Dropping this msg. %s", err.Error(), params.String())
532                 return
533         }
534
535         //
536         //
537         //
538         subs := c.registry.GetSubscription(uint16(SubDelRespMsg.RequestId.Seq))
539         if subs == nil && params.SubId > 0 {
540                 subs = c.registry.GetSubscription(uint16(params.SubId))
541         }
542
543         if subs == nil {
544                 xapp.Logger.Error("SubDelResp: Not valid subscription found payloadSeqNum: %d, SubId: %d. Dropping this msg. %s", SubDelRespMsg.RequestId.Seq, params.SubId, params.String())
545                 return
546         }
547         xapp.Logger.Info("SubDelResp: subscription found payloadSeqNum: %d, SubId: %d", SubDelRespMsg.RequestId.Seq, subs.GetSubId())
548
549         //
550         //
551         //
552         trans := subs.GetTransaction()
553         if trans == nil {
554                 xapp.Logger.Error("SubDelResp: Unknown trans. Dropping this msg. SubId: %d", subs.GetSubId())
555                 return
556         }
557
558         trans.SubDelRespMsg = SubDelRespMsg
559
560         //
561         //
562         //
563         c.timerMap.StopTimer("RIC_SUB_DEL_REQ", int(subs.GetSubId()))
564
565         responseReceived := trans.CheckResponseReceived()
566         if responseReceived == true {
567                 // Subscription Delete timer already received
568                 return
569         }
570
571         c.sendSubscriptionDeleteResponse("SubDelResp", trans, subs)
572         return
573 }
574
575 func (c *Control) handleSubscriptionDeleteFailure(params *RMRParams) {
576         xapp.Logger.Info("SubDelFail from E2T:%s", params.String())
577
578         //
579         //
580         //
581         SubDelFailMsg, err := c.e2ap.UnpackSubscriptionDeleteFailure(params.Payload)
582         if err != nil {
583                 xapp.Logger.Error("SubDelFail: %s Dropping this msg. %s", err.Error(), params.String())
584                 return
585         }
586
587         //
588         //
589         //
590         subs := c.registry.GetSubscription(uint16(SubDelFailMsg.RequestId.Seq))
591         if subs == nil && params.SubId > 0 {
592                 subs = c.registry.GetSubscription(uint16(params.SubId))
593         }
594
595         if subs == nil {
596                 xapp.Logger.Error("SubDelFail: Not valid subscription found payloadSeqNum: %d, SubId: %d. Dropping this msg. %s", SubDelFailMsg.RequestId.Seq, params.SubId, params.String())
597                 return
598         }
599         xapp.Logger.Info("SubDelFail: subscription found payloadSeqNum: %d, SubId: %d", SubDelFailMsg.RequestId.Seq, subs.GetSubId())
600
601         //
602         //
603         //
604         trans := subs.GetTransaction()
605         if trans == nil {
606                 xapp.Logger.Error("SubDelFail: Unknown trans. Dropping this msg. SubId: %d", subs.GetSubId())
607                 return
608         }
609         trans.SubDelFailMsg = SubDelFailMsg
610
611         //
612         //
613         //
614         c.timerMap.StopTimer("RIC_SUB_DEL_REQ", int(subs.GetSubId()))
615
616         responseReceived := trans.CheckResponseReceived()
617         if responseReceived == true {
618                 // Subscription Delete timer already received
619                 return
620         }
621
622         c.sendSubscriptionDeleteResponse("SubDelFail", trans, subs)
623         return
624 }
625
626 func (c *Control) handleSubscriptionDeleteRequestTimer(strId string, nbrId int, tryCount uint64) {
627         xapp.Logger.Info("SubDelReq timeout: subId: %v, tryCount: %v", nbrId, tryCount)
628
629         subs := c.registry.GetSubscription(uint16(nbrId))
630         if subs == nil {
631                 xapp.Logger.Error("SubDelReq timeout: Unknown payloadSeqNum. Dropping this msg. SubId: %v", nbrId)
632                 return
633         }
634
635         trans := subs.GetTransaction()
636         if trans == nil {
637                 xapp.Logger.Error("SubDelReq timeout: Unknown trans. Dropping this msg. SubId: %v", subs.GetSubId())
638                 return
639         }
640
641         responseReceived := trans.CheckResponseReceived()
642         if responseReceived == true {
643                 // Subscription Delete Response or Failure already received
644                 return
645         }
646
647         if tryCount < maxSubDelReqTryCount {
648                 // Set possible to handle new response for the subId
649                 trans.RetryTransaction()
650                 c.rmrSend("SubDelReq timeout: SubDelReq to E2T", subs, trans)
651                 tryCount++
652                 c.timerMap.StartTimer("RIC_SUB_DEL_REQ", int(subs.GetSubId()), subReqTime, tryCount, c.handleSubscriptionDeleteRequestTimer)
653                 return
654         }
655
656         c.sendSubscriptionDeleteResponse("SubDelReq(timer)", trans, subs)
657         return
658 }
659
660 func (c *Control) sendSubscriptionDeleteResponse(desc string, trans *Transaction, subs *Subscription) {
661
662         if trans.ForwardRespToXapp == true {
663                 //Always generate SubDelResp
664                 trans.SubDelRespMsg = &e2ap.E2APSubscriptionDeleteResponse{}
665                 trans.SubDelRespMsg.RequestId.Id = trans.SubDelReqMsg.RequestId.Id
666                 trans.SubDelRespMsg.RequestId.Seq = uint32(subs.GetSubId())
667                 trans.SubDelRespMsg.FunctionId = trans.SubDelReqMsg.FunctionId
668
669                 var err error
670                 trans.Mtype, trans.Payload, err = c.e2ap.PackSubscriptionDeleteResponse(trans.SubDelRespMsg)
671                 if err == nil {
672                         c.rmrReplyToSender(desc+": SubDelResp to xapp", subs, trans)
673                         time.Sleep(3 * time.Second)
674                 } else {
675                         //TODO error handling improvement
676                         xapp.Logger.Error("%s: %s for trans %s (continuing cleaning)", desc, err.Error(), trans)
677                 }
678         }
679
680         trans.Release()
681         subs.Release()
682 }