From 897d6f0da1e782f9382c12b7a8cb6ce6eb50ff96 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Mon, 8 Dec 2025 12:36:32 +0200 Subject: [PATCH] integrate IPs with SessionS --- agents/libagents.go | 4 + agents/radagent.go | 4 + agents/sipagent.go | 1 + engine/ips.go | 10 + engine/storage_mongo_datadb.go | 3 + engine/stored_session.go | 1 + ers/ers.go | 4 + general_tests/fraud_detection_it_test.go | 8 +- general_tests/sessionroutes_it_test.go | 16 +- sessions/session.go | 4 + sessions/sessions.go | 392 ++++++++++++++++++----- sessions/sessions_test.go | 36 +-- sessions/sessionscover_test.go | 85 ++--- utils/consts.go | 1 + utils/errors.go | 4 + 15 files changed, 413 insertions(+), 160 deletions(-) diff --git a/agents/libagents.go b/agents/libagents.go index e3d681a9a..7f66a1e17 100644 --- a/agents/libagents.go +++ b/agents/libagents.go @@ -86,6 +86,7 @@ func processRequest(ctx *context.Context, reqProcessor *config.RequestProcessor, reqProcessor.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.GetBool(utils.MetaResources), reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaRoutes), @@ -112,6 +113,7 @@ func processRequest(ctx *context.Context, reqProcessor *config.RequestProcessor, reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.Has(utils.MetaAccounts), cgrEv, reqProcessor.Flags.Has(utils.MetaFD)) rply := new(sessions.V1InitSessionReply) @@ -144,6 +146,7 @@ func processRequest(ctx *context.Context, reqProcessor *config.RequestProcessor, terminateArgs := sessions.NewV1TerminateSessionArgs( reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.GetBool(utils.MetaThresholds), reqProcessor.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaStats), @@ -165,6 +168,7 @@ func processRequest(ctx *context.Context, reqProcessor *config.RequestProcessor, reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaRoutes), reqProcessor.Flags.Has(utils.MetaRoutesIgnoreErrors), diff --git a/agents/radagent.go b/agents/radagent.go index 0411dd4d5..7b359baad 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -357,6 +357,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R reqProcessor.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.GetBool(utils.MetaResources), reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaRoutes), @@ -382,6 +383,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.Has(utils.MetaAccounts), cgrEv, reqProcessor.Flags.Has(utils.MetaFD)) rply := new(sessions.V1InitSessionReply) @@ -414,6 +416,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R terminateArgs := sessions.NewV1TerminateSessionArgs( reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.GetBool(utils.MetaThresholds), reqProcessor.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaStats), @@ -435,6 +438,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaResources), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaRoutes), reqProcessor.Flags.Has(utils.MetaRoutesIgnoreErrors), diff --git a/agents/sipagent.go b/agents/sipagent.go index 0880cc7a1..97a522ad5 100644 --- a/agents/sipagent.go +++ b/agents/sipagent.go @@ -430,6 +430,7 @@ func (sa *SIPAgent) processRequest(reqProcessor *config.RequestProcessor, reqProcessor.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), reqProcessor.Flags.GetBool(utils.MetaStats), reqProcessor.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), + reqProcessor.Flags.GetBool(utils.MetaIPs), reqProcessor.Flags.GetBool(utils.MetaResources), reqProcessor.Flags.Has(utils.MetaAccounts), reqProcessor.Flags.GetBool(utils.MetaRoutes), diff --git a/engine/ips.go b/engine/ips.go index dff3a81db..4ca805b02 100644 --- a/engine/ips.go +++ b/engine/ips.go @@ -183,6 +183,16 @@ func (ip *AllocatedIP) AsNavigableMap() map[string]*utils.DataNode { } } +// Digest returns a string representation of the allocated IP for digest replies. +func (ip *AllocatedIP) Digest() string { + return utils.ConcatenatedKey( + ip.ProfileID, + ip.PoolID, + ip.Message, + ip.Address.String(), + ) +} + // IPAllocations represents IP allocations with usage tracking and TTL management. type IPAllocations struct { Tenant string diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 665dbe4b9..de1696dd7 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -2212,6 +2212,7 @@ type mongoStoredSession struct { NodeID string CGRID string Tenant string + IPAllocID string ResourceID string ClientConnID string EventStart MapEvent @@ -2238,6 +2239,7 @@ func (ms *MongoStorage) SetBackupSessionsDrv(nodeID, tnt string, storedSessions NodeID: nodeID, CGRID: sess.CGRID, Tenant: sess.Tenant, + IPAllocID: sess.IPAllocID, ResourceID: sess.ResourceID, ClientConnID: sess.ClientConnID, EventStart: sess.EventStart, @@ -2286,6 +2288,7 @@ func (ms *MongoStorage) GetSessionsBackupDrv(nodeID, tnt string) ([]*StoredSessi oneStSession := &StoredSession{ CGRID: result.CGRID, Tenant: result.Tenant, + IPAllocID: result.IPAllocID, ResourceID: result.ResourceID, ClientConnID: result.ClientConnID, EventStart: result.EventStart, diff --git a/engine/stored_session.go b/engine/stored_session.go index 517c7912a..5f93fa405 100644 --- a/engine/stored_session.go +++ b/engine/stored_session.go @@ -38,6 +38,7 @@ type StoredSRun struct { type StoredSession struct { CGRID string Tenant string + IPAllocID string ResourceID string ClientConnID string // connection ID towards the client so we can recover from passive EventStart MapEvent // Event which started the session diff --git a/ers/ers.go b/ers/ers.go index 2d19a8b4c..ec310e8ca 100644 --- a/ers/ers.go +++ b/ers/ers.go @@ -334,6 +334,7 @@ func (erS *ERService) processEvent(cgrEv *utils.CGREvent, rdrCfg.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), rdrCfg.Flags.Has(utils.MetaStats), rdrCfg.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), + rdrCfg.Flags.Has(utils.MetaIPs), rdrCfg.Flags.Has(utils.MetaResources), rdrCfg.Flags.Has(utils.MetaAccounts), rdrCfg.Flags.Has(utils.MetaRoutes), @@ -358,6 +359,7 @@ func (erS *ERService) processEvent(cgrEv *utils.CGREvent, rdrCfg.Flags.Has(utils.MetaStats), rdrCfg.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), rdrCfg.Flags.Has(utils.MetaResources), + rdrCfg.Flags.Has(utils.MetaIPs), rdrCfg.Flags.Has(utils.MetaAccounts), cgrEv, rdrCfg.Flags.Has(utils.MetaFD)) rply := new(sessions.V1InitSessionReply) @@ -386,6 +388,7 @@ func (erS *ERService) processEvent(cgrEv *utils.CGREvent, terminateArgs := sessions.NewV1TerminateSessionArgs( rdrCfg.Flags.Has(utils.MetaAccounts), rdrCfg.Flags.Has(utils.MetaResources), + rdrCfg.Flags.Has(utils.MetaIPs), rdrCfg.Flags.Has(utils.MetaThresholds), rdrCfg.Flags.ParamsSlice(utils.MetaThresholds, utils.MetaIDs), rdrCfg.Flags.Has(utils.MetaStats), @@ -406,6 +409,7 @@ func (erS *ERService) processEvent(cgrEv *utils.CGREvent, rdrCfg.Flags.Has(utils.MetaStats), rdrCfg.Flags.ParamsSlice(utils.MetaStats, utils.MetaIDs), rdrCfg.Flags.Has(utils.MetaResources), + rdrCfg.Flags.Has(utils.MetaIPs), rdrCfg.Flags.Has(utils.MetaAccounts), rdrCfg.Flags.Has(utils.MetaRoutes), rdrCfg.Flags.Has(utils.MetaRoutesIgnoreErrors), diff --git a/general_tests/fraud_detection_it_test.go b/general_tests/fraud_detection_it_test.go index aa28f1a5d..75675ab96 100644 --- a/general_tests/fraud_detection_it_test.go +++ b/general_tests/fraud_detection_it_test.go @@ -274,7 +274,7 @@ func testFraudAuthorizeandProcess1(t *testing.T) { APIOpts: map[string]any{}, } args := sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, false, false, false, cgrEv, utils.Paginator{}, false, "") var rply sessions.V1AuthorizeReply @@ -324,7 +324,7 @@ func testFraudAuthorizeandProcess2(t *testing.T) { APIOpts: map[string]any{}, } args := sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, false, false, false, cgrEv, utils.Paginator{}, false, "") var rply sessions.V1AuthorizeReply @@ -374,7 +374,7 @@ func testFraudAuthorizeandProcess3(t *testing.T) { APIOpts: map[string]any{}, } args := sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, false, false, false, cgrEv, utils.Paginator{}, false, "") var rply sessions.V1AuthorizeReply @@ -425,7 +425,7 @@ func testFraudFinalAuthorize(t *testing.T) { APIOpts: map[string]any{}, } args := sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, false, false, false, cgrEv, utils.Paginator{}, false, "") expErr := `RALS_ERROR:ACCOUNT_DISABLED` diff --git a/general_tests/sessionroutes_it_test.go b/general_tests/sessionroutes_it_test.go index 07048ac4c..b1c7ace6b 100644 --- a/general_tests/sessionroutes_it_test.go +++ b/general_tests/sessionroutes_it_test.go @@ -133,7 +133,7 @@ func testSesRoutesAuthorizeEvent(t *testing.T) { APIOpts: map[string]any{utils.OptsRoutesProfileCount: 1}, } args := sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "") var rply sessions.V1AuthorizeReply @@ -177,7 +177,7 @@ func testSesRoutesAuthorizeEvent(t *testing.T) { t.Errorf("Expected: %s, received: %s", utils.ToJSON(expected), utils.ToJSON(rply)) } args = sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "2") rply = sessions.V1AuthorizeReply{} @@ -217,7 +217,7 @@ func testSesRoutesAuthorizeEvent(t *testing.T) { }} // now we will set the maxCOst to be 1 in order to match route3 and route1 args = sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "1") rply = sessions.V1AuthorizeReply{} @@ -231,7 +231,7 @@ func testSesRoutesAuthorizeEvent(t *testing.T) { } args = sessions.NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, true, cgrEv, utils.Paginator{}, false, "") rply = sessions.V1AuthorizeReply{} @@ -266,7 +266,7 @@ func testSesRoutesProcessMessage(t *testing.T) { }, } args := sessions.NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "") var rply sessions.V1ProcessMessageReply @@ -312,7 +312,7 @@ func testSesRoutesProcessMessage(t *testing.T) { } args = sessions.NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "2") rply = sessions.V1ProcessMessageReply{} @@ -352,7 +352,7 @@ func testSesRoutesProcessMessage(t *testing.T) { }} // now we will set the maxCOst to be 1 in order to match route3 and route1 args = sessions.NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEv, utils.Paginator{}, false, "1") rply = sessions.V1ProcessMessageReply{} @@ -366,7 +366,7 @@ func testSesRoutesProcessMessage(t *testing.T) { } args = sessions.NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, true, cgrEv, utils.Paginator{}, false, "") rply = sessions.V1ProcessMessageReply{} diff --git a/sessions/session.go b/sessions/session.go index e820121df..aee111f48 100644 --- a/sessions/session.go +++ b/sessions/session.go @@ -73,6 +73,7 @@ type Session struct { CGRID string Tenant string + IPAllocID string ResourceID string ClientConnID string // connection ID towards the client so we can recover from passive EventStart engine.MapEvent // Event which started the session @@ -119,6 +120,7 @@ func (s *Session) Clone() (cln *Session) { cln = &Session{ CGRID: s.CGRID, Tenant: s.Tenant, + IPAllocID: s.IPAllocID, ResourceID: s.ResourceID, ClientConnID: s.ClientConnID, EventStart: s.EventStart.Clone(), @@ -152,6 +154,7 @@ func (s *Session) asStoredSession() *engine.StoredSession { return &engine.StoredSession{ CGRID: s.CGRID, Tenant: s.Tenant, + IPAllocID: s.IPAllocID, ResourceID: s.ResourceID, ClientConnID: s.ClientConnID, EventStart: s.EventStart, @@ -181,6 +184,7 @@ func newSessionFromStoredSession(s *engine.StoredSession) *Session { return &Session{ CGRID: s.CGRID, Tenant: s.Tenant, + IPAllocID: s.IPAllocID, ResourceID: s.ResourceID, ClientConnID: s.ClientConnID, EventStart: s.EventStart, diff --git a/sessions/sessions.go b/sessions/sessions.go index dd259f4fd..46740fe84 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -405,6 +405,27 @@ func (sS *SessionS) forceSTerminate(s *Session, extraUsage time.Duration, tUsage utils.SessionS, err.Error(), s.ResourceID)) } } + if len(sS.cgrCfg.SessionSCfg().IPsConns) != 0 && s.IPAllocID != "" { + var reply string + cgrEv := &utils.CGREvent{ + Tenant: s.Tenant, + ID: utils.GenUUID(), + Event: s.EventStart, + APIOpts: s.OptsStart, + } + if cgrEv.APIOpts == nil { + cgrEv.APIOpts = make(map[string]any) + } + cgrEv.APIOpts[utils.OptsIPsAllocationID] = s.IPAllocID + cgrEv.SetCloneable(true) + if err := sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, + utils.IPsV1ReleaseIP, + cgrEv, &reply); err != nil { + utils.Logger.Warning( + fmt.Sprintf("<%s> could not release IP allocation %q: %v", + utils.SessionS, s.IPAllocID, err)) + } + } sS.replicateSessions(s.CGRID, false, sS.cgrCfg.SessionSCfg().ReplicationConns) if clnt := sS.biJClnt(s.ClientConnID); clnt != nil { go func() { @@ -1249,7 +1270,7 @@ func (sS *SessionS) filterSessionsCount(sf *utils.SessionFilter, psv bool) (coun // newSession will populate SRuns within a Session based on ChargerS output // forSession can only be called once per Session // not thread-safe since it should be called in init where there is no concurrency -func (sS *SessionS) newSession(cgrEv *utils.CGREvent, resID, clntConnID string, +func (sS *SessionS) newSession(cgrEv *utils.CGREvent, originID, clntConnID string, dbtItval time.Duration, forceDuration, isMsg bool) (s *Session, err error) { if len(sS.cgrCfg.SessionSCfg().ChargerSConns) == 0 { err = errors.New("ChargerS is disabled") @@ -1263,7 +1284,8 @@ func (sS *SessionS) newSession(cgrEv *utils.CGREvent, resID, clntConnID string, s = &Session{ CGRID: cgrID, Tenant: cgrEv.Tenant, - ResourceID: resID, + IPAllocID: originID, + ResourceID: originID, EventStart: evStart.Clone(), // decouple the event from the request so we can avoid concurrency with debit and ttl OptsStart: engine.MapEvent(cgrEv.APIOpts).Clone(), ClientConnID: clntConnID, @@ -1638,9 +1660,9 @@ func (sS *SessionS) restoreSessions(sessions []*Session) { // initSession handles a new session // not thread-safe for Session since it is constructed here -func (sS *SessionS) initSession(cgrEv *utils.CGREvent, clntConnID, - resID string, dbtItval time.Duration, isMsg, forceDuration bool) (s *Session, err error) { - if s, err = sS.newSession(cgrEv, resID, clntConnID, dbtItval, forceDuration, isMsg); err != nil { +func (sS *SessionS) initSession(cgrEv *utils.CGREvent, clntConnID, originID string, + dbtItval time.Duration, isMsg, forceDuration bool) (s *Session, err error) { + if s, err = sS.newSession(cgrEv, originID, clntConnID, dbtItval, forceDuration, isMsg); err != nil { return nil, err } if !isMsg { @@ -1971,11 +1993,12 @@ func (sS *SessionS) BiRPCv1ReplicateSessions(ctx *context.Context, // NewV1AuthorizeArgs is a constructor for V1AuthorizeArgs func NewV1AuthorizeArgs(attrs bool, attributeIDs []string, thrslds bool, thresholdIDs []string, statQueues bool, statIDs []string, - res, maxUsage, routes, routesIgnoreErrs, routesEventCost bool, + ips, res, maxUsage, routes, routesIgnoreErrs, routesEventCost bool, cgrEv *utils.CGREvent, routePaginator utils.Paginator, forceDuration bool, routesMaxCost string) (args *V1AuthorizeArgs) { args = &V1AuthorizeArgs{ GetAttributes: attrs, + AuthorizeIP: ips, AuthorizeResources: res, GetMaxUsage: maxUsage, ProcessThresholds: thrslds, @@ -2007,6 +2030,7 @@ func NewV1AuthorizeArgs(attrs bool, attributeIDs []string, // V1AuthorizeArgs are options available in auth request type V1AuthorizeArgs struct { GetAttributes bool + AuthorizeIP bool AuthorizeResources bool GetMaxUsage bool ForceDuration bool @@ -2030,6 +2054,8 @@ func (args *V1AuthorizeArgs) ParseFlags(flags, sep string) { switch { case subsystem == utils.MetaAccounts: args.GetMaxUsage = true + case subsystem == utils.MetaIPs: + args.AuthorizeIP = true case subsystem == utils.MetaResources: args.AuthorizeResources = true case subsystem == utils.MetaRoutes: @@ -2059,6 +2085,7 @@ func (args *V1AuthorizeArgs) ParseFlags(flags, sep string) { // V1AuthorizeReply are options available in auth reply type V1AuthorizeReply struct { Attributes *engine.AttrSProcessEventReply `json:",omitempty"` + AllocatedIP *engine.AllocatedIP `json:",omitempty"` ResourceAllocation *string `json:",omitempty"` MaxUsage *time.Duration `json:",omitempty"` RouteProfiles engine.SortedRoutesList `json:",omitempty"` @@ -2070,49 +2097,55 @@ type V1AuthorizeReply struct { // SetMaxUsageNeeded used by agent that use the reply as NavigableMapper // only used for gob encoding -func (v1AuthReply *V1AuthorizeReply) SetMaxUsageNeeded(getMaxUsage bool) { - if v1AuthReply == nil { +func (r *V1AuthorizeReply) SetMaxUsageNeeded(getMaxUsage bool) { + if r == nil { return } - v1AuthReply.needsMaxUsage = getMaxUsage + r.needsMaxUsage = getMaxUsage } // AsNavigableMap is part of engine.NavigableMapper interface -func (v1AuthReply *V1AuthorizeReply) AsNavigableMap() map[string]*utils.DataNode { +func (r *V1AuthorizeReply) AsNavigableMap() map[string]*utils.DataNode { cgrReply := make(map[string]*utils.DataNode) - if v1AuthReply.Attributes != nil { + if r.Attributes != nil { attrs := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for _, fldName := range v1AuthReply.Attributes.AlteredFields { + for _, fldName := range r.Attributes.AlteredFields { fldName = strings.TrimPrefix(fldName, utils.MetaReq+utils.NestingSep) - if v1AuthReply.Attributes.CGREvent.HasField(fldName) { - attrs.Map[fldName] = utils.NewLeafNode(v1AuthReply.Attributes.CGREvent.Event[fldName]) + if r.Attributes.CGREvent.HasField(fldName) { + attrs.Map[fldName] = utils.NewLeafNode(r.Attributes.CGREvent.Event[fldName]) } } cgrReply[utils.CapAttributes] = attrs } - if v1AuthReply.ResourceAllocation != nil { - cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*v1AuthReply.ResourceAllocation) + if r.AllocatedIP != nil { + cgrReply[utils.CapAllocatedIP] = &utils.DataNode{ + Type: utils.NMMapType, + Map: r.AllocatedIP.AsNavigableMap(), + } } - if v1AuthReply.MaxUsage != nil { - cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*v1AuthReply.MaxUsage) - } else if v1AuthReply.needsMaxUsage { + if r.ResourceAllocation != nil { + cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*r.ResourceAllocation) + } + if r.MaxUsage != nil { + cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*r.MaxUsage) + } else if r.needsMaxUsage { cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(0) } - if v1AuthReply.RouteProfiles != nil { - nm := v1AuthReply.RouteProfiles.AsNavigableMap() + if r.RouteProfiles != nil { + nm := r.RouteProfiles.AsNavigableMap() cgrReply[utils.CapRouteProfiles] = nm } - if v1AuthReply.ThresholdIDs != nil { - thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1AuthReply.ThresholdIDs))} - for i, v := range *v1AuthReply.ThresholdIDs { + if r.ThresholdIDs != nil { + thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.ThresholdIDs))} + for i, v := range *r.ThresholdIDs { thIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapThresholds] = thIDs } - if v1AuthReply.StatQueueIDs != nil { - stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1AuthReply.StatQueueIDs))} - for i, v := range *v1AuthReply.StatQueueIDs { + if r.StatQueueIDs != nil { + stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.StatQueueIDs))} + for i, v := range *r.StatQueueIDs { stIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapStatQueues] = stIDs @@ -2157,7 +2190,7 @@ func (sS *SessionS) BiRPCv1AuthorizeEvent(ctx *context.Context, } // end of RPC caching - if !args.GetAttributes && !args.AuthorizeResources && + if !args.GetAttributes && !args.AuthorizeResources && !args.AuthorizeIP && !args.GetMaxUsage && !args.GetRoutes { return utils.NewErrMandatoryIeMissing(utils.Subsystems) } @@ -2206,6 +2239,25 @@ func (sS *SessionS) BiRPCv1AuthorizeEvent(ctx *context.Context, } authReply.ResourceAllocation = &allocMsg } + if args.AuthorizeIP { + if len(sS.cgrCfg.SessionSCfg().IPsConns) == 0 { + return utils.NewErrNotConnected(utils.IPs) + } + originID, _ := args.CGREvent.FieldAsString(utils.OriginID) + if originID == "" { + originID = utils.UUIDSha1Prefix() + } + var allocIP engine.AllocatedIP + if args.APIOpts == nil { + args.APIOpts = make(map[string]any) + } + args.CGREvent.APIOpts[utils.OptsIPsAllocationID] = originID + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1AuthorizeIP, + args.CGREvent, &allocIP); err != nil { + return utils.NewErrIPs(err) + } + authReply.AllocatedIP = &allocIP + } if args.GetRoutes { routesReply, err := sS.getRoutes(args.CGREvent.Clone(), args.Paginator, args.RoutesIgnoreErrors, args.RoutesMaxCost, false) @@ -2246,6 +2298,7 @@ func (sS *SessionS) BiRPCv1AuthorizeEvent(ctx *context.Context, // V1AuthorizeReplyWithDigest contains return options for auth with digest type V1AuthorizeReplyWithDigest struct { AttributesDigest *string + AllocatedIP *string ResourceAllocation *string MaxUsage float64 // special treat returning time.Duration.Seconds() RoutesDigest *string @@ -2267,6 +2320,9 @@ func (sS *SessionS) BiRPCv1AuthorizeEventWithDigest(ctx *context.Context, if args.AuthorizeResources { authReply.ResourceAllocation = initAuthRply.ResourceAllocation } + if args.AuthorizeIP { + authReply.AllocatedIP = utils.StringPointer(initAuthRply.AllocatedIP.Digest()) + } if args.GetMaxUsage { authReply.MaxUsage = initAuthRply.MaxUsage.Seconds() } @@ -2287,9 +2343,10 @@ func (sS *SessionS) BiRPCv1AuthorizeEventWithDigest(ctx *context.Context, // NewV1InitSessionArgs is a constructor for V1InitSessionArgs func NewV1InitSessionArgs(attrs bool, attributeIDs []string, thrslds bool, thresholdIDs []string, stats bool, statIDs []string, - resrc, acnt bool, cgrEv *utils.CGREvent, forceDuration bool) (args *V1InitSessionArgs) { + resrc, ips, acnt bool, cgrEv *utils.CGREvent, forceDuration bool) (args *V1InitSessionArgs) { args = &V1InitSessionArgs{ GetAttributes: attrs, + AllocateIP: ips, AllocateResources: resrc, InitSession: acnt, ProcessThresholds: thrslds, @@ -2312,6 +2369,7 @@ func NewV1InitSessionArgs(attrs bool, attributeIDs []string, // V1InitSessionArgs are options for session initialization request type V1InitSessionArgs struct { GetAttributes bool + AllocateIP bool AllocateResources bool InitSession bool ForceDuration bool @@ -2333,6 +2391,8 @@ func (args *V1InitSessionArgs) ParseFlags(flags, sep string) { args.InitSession = true case subsystem == utils.MetaResources: args.AllocateResources = true + case subsystem == utils.MetaIPs: + args.AllocateIP = true case strings.HasPrefix(subsystem, utils.MetaAttributes): args.GetAttributes = true args.AttributeIDs = getFlagIDs(subsystem) @@ -2351,6 +2411,7 @@ func (args *V1InitSessionArgs) ParseFlags(flags, sep string) { // V1InitSessionReply are options for initialization reply type V1InitSessionReply struct { Attributes *engine.AttrSProcessEventReply `json:",omitempty"` + AllocatedIP *engine.AllocatedIP `json:",omitempty"` ResourceAllocation *string `json:",omitempty"` MaxUsage *time.Duration `json:",omitempty"` ThresholdIDs *[]string `json:",omitempty"` @@ -2369,37 +2430,43 @@ func (v1Rply *V1InitSessionReply) SetMaxUsageNeeded(getMaxUsage bool) { } // AsNavigableMap is part of engine.NavigableMapper interface -func (v1Rply *V1InitSessionReply) AsNavigableMap() map[string]*utils.DataNode { +func (r *V1InitSessionReply) AsNavigableMap() map[string]*utils.DataNode { cgrReply := make(map[string]*utils.DataNode) - if v1Rply.Attributes != nil { + if r.Attributes != nil { attrs := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for _, fldName := range v1Rply.Attributes.AlteredFields { + for _, fldName := range r.Attributes.AlteredFields { fldName = strings.TrimPrefix(fldName, utils.MetaReq+utils.NestingSep) - if v1Rply.Attributes.CGREvent.HasField(fldName) { - attrs.Map[fldName] = utils.NewLeafNode(v1Rply.Attributes.CGREvent.Event[fldName]) + if r.Attributes.CGREvent.HasField(fldName) { + attrs.Map[fldName] = utils.NewLeafNode(r.Attributes.CGREvent.Event[fldName]) } } cgrReply[utils.CapAttributes] = attrs } - if v1Rply.ResourceAllocation != nil { - cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*v1Rply.ResourceAllocation) + if r.AllocatedIP != nil { + cgrReply[utils.CapAllocatedIP] = &utils.DataNode{ + Type: utils.NMMapType, + Map: r.AllocatedIP.AsNavigableMap(), + } } - if v1Rply.MaxUsage != nil { - cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*v1Rply.MaxUsage) - } else if v1Rply.needsMaxUsage { + if r.ResourceAllocation != nil { + cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*r.ResourceAllocation) + } + if r.MaxUsage != nil { + cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*r.MaxUsage) + } else if r.needsMaxUsage { cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(0) } - if v1Rply.ThresholdIDs != nil { - thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1Rply.ThresholdIDs))} - for i, v := range *v1Rply.ThresholdIDs { + if r.ThresholdIDs != nil { + thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.ThresholdIDs))} + for i, v := range *r.ThresholdIDs { thIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapThresholds] = thIDs } - if v1Rply.StatQueueIDs != nil { - stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1Rply.StatQueueIDs))} - for i, v := range *v1Rply.StatQueueIDs { + if r.StatQueueIDs != nil { + stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.StatQueueIDs))} + for i, v := range *r.StatQueueIDs { stIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapStatQueues] = stIDs @@ -2444,7 +2511,7 @@ func (sS *SessionS) BiRPCv1InitiateSession(ctx *context.Context, } // end of RPC caching - if !args.GetAttributes && !args.AllocateResources && !args.InitSession { + if !args.GetAttributes && !args.AllocateResources && !args.AllocateIP && !args.InitSession { return utils.NewErrMandatoryIeMissing(utils.Subsystems) } originID, _ := args.CGREvent.FieldAsString(utils.OriginID) @@ -2476,6 +2543,24 @@ func (sS *SessionS) BiRPCv1InitiateSession(ctx *context.Context, } rply.ResourceAllocation = &allocMessage } + if args.AllocateIP { + if len(sS.cgrCfg.SessionSCfg().IPsConns) == 0 { + return utils.NewErrNotConnected(utils.IPs) + } + if originID == "" { + return utils.NewErrMandatoryIeMissing(utils.OriginID) + } + if args.APIOpts == nil { + args.APIOpts = make(map[string]any) + } + args.CGREvent.APIOpts[utils.OptsIPsAllocationID] = originID + var allocIP engine.AllocatedIP + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, + utils.IPsV1AllocateIP, args.CGREvent, &allocIP); err != nil { + return utils.NewErrIPs(err) + } + rply.AllocatedIP = &allocIP + } if args.InitSession { var err error opts := engine.MapEvent(args.APIOpts) @@ -2547,6 +2632,7 @@ func (sS *SessionS) BiRPCv1InitiateSession(ctx *context.Context, // V1InitReplyWithDigest is the formated reply type V1InitReplyWithDigest struct { AttributesDigest *string + AllocatedIP *string ResourceAllocation *string MaxUsage float64 Thresholds *string @@ -2570,6 +2656,10 @@ func (sS *SessionS) BiRPCv1InitiateSessionWithDigest(ctx *context.Context, initReply.ResourceAllocation = initSessionRply.ResourceAllocation } + if args.AllocateIP { + initReply.AllocatedIP = utils.StringPointer(initSessionRply.AllocatedIP.Digest()) + } + if args.InitSession { initReply.MaxUsage = initSessionRply.MaxUsage.Seconds() } @@ -2772,12 +2862,13 @@ func (sS *SessionS) BiRPCv1UpdateSession(ctx *context.Context, } // NewV1TerminateSessionArgs creates a new V1TerminateSessionArgs using the given arguments -func NewV1TerminateSessionArgs(acnts, resrc, +func NewV1TerminateSessionArgs(acnts, resrc, ips, thrds bool, thresholdIDs []string, stats bool, statIDs []string, cgrEv *utils.CGREvent, forceDuration bool) (args *V1TerminateSessionArgs) { args = &V1TerminateSessionArgs{ TerminateSession: acnts, ReleaseResources: resrc, + ReleaseIP: ips, ProcessThresholds: thrds, ProcessStats: stats, CGREvent: cgrEv, @@ -2796,6 +2887,7 @@ func NewV1TerminateSessionArgs(acnts, resrc, type V1TerminateSessionArgs struct { TerminateSession bool ForceDuration bool + ReleaseIP bool ReleaseResources bool ProcessThresholds bool ProcessStats bool @@ -2814,6 +2906,8 @@ func (args *V1TerminateSessionArgs) ParseFlags(flags, sep string) { args.TerminateSession = true case subsystem == utils.MetaResources: args.ReleaseResources = true + case subsystem == utils.MetaIPs: + args.ReleaseIP = true case strings.Contains(subsystem, utils.MetaThresholds): args.ProcessThresholds = true args.ThresholdIDs = getFlagIDs(subsystem) @@ -2861,7 +2955,7 @@ func (sS *SessionS) BiRPCv1TerminateSession(ctx *context.Context, nil, true, utils.NonTransactional) } // end of RPC caching - if !args.TerminateSession && !args.ReleaseResources { + if !args.TerminateSession && !args.ReleaseResources && !args.ReleaseIP { return utils.NewErrMandatoryIeMissing(utils.Subsystems) } @@ -2935,6 +3029,23 @@ func (sS *SessionS) BiRPCv1TerminateSession(ctx *context.Context, return utils.NewErrResourceS(err) } } + if args.ReleaseIP { + if len(sS.cgrCfg.SessionSCfg().IPsConns) == 0 { + return utils.NewErrNotConnected(utils.IPs) + } + if originID == "" { + return utils.NewErrMandatoryIeMissing(utils.OriginID) + } + var reply string + if args.APIOpts == nil { + args.APIOpts = make(map[string]any) + } + args.CGREvent.APIOpts[utils.OptsIPsAllocationID] = originID + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1ReleaseIP, + args.CGREvent, &reply); err != nil { + return utils.NewErrIPs(err) + } + } if args.ProcessThresholds { _, err := sS.processThreshold(args.CGREvent, args.ThresholdIDs, true) if err != nil && @@ -3004,11 +3115,12 @@ func (sS *SessionS) BiRPCv1ProcessCDR(ctx *context.Context, // NewV1ProcessMessageArgs is a constructor for MessageArgs used by ProcessMessage func NewV1ProcessMessageArgs(attrs bool, attributeIDs []string, - thds bool, thresholdIDs []string, stats bool, statIDs []string, resrc, acnts, + thds bool, thresholdIDs []string, stats bool, statIDs []string, resrc, ips, acnts, routes, routesIgnoreErrs, routesEventCost bool, cgrEv *utils.CGREvent, routePaginator utils.Paginator, forceDuration bool, routesMaxCost string) (args *V1ProcessMessageArgs) { args = &V1ProcessMessageArgs{ AllocateResources: resrc, + AllocateIP: ips, Debit: acnts, GetAttributes: attrs, ProcessThresholds: thds, @@ -3039,6 +3151,7 @@ func NewV1ProcessMessageArgs(attrs bool, attributeIDs []string, // V1ProcessMessageArgs are the options passed to ProcessMessage API type V1ProcessMessageArgs struct { GetAttributes bool + AllocateIP bool AllocateResources bool Debit bool ForceDuration bool @@ -3064,6 +3177,8 @@ func (args *V1ProcessMessageArgs) ParseFlags(flags, sep string) { args.Debit = true case subsystem == utils.MetaResources: args.AllocateResources = true + case subsystem == utils.MetaIPs: + args.AllocateIP = true case subsystem == utils.MetaRoutes: args.GetRoutes = true case subsystem == utils.MetaRoutesIgnoreErrors: @@ -3091,6 +3206,7 @@ func (args *V1ProcessMessageArgs) ParseFlags(flags, sep string) { // V1ProcessMessageReply is the reply for the ProcessMessage API type V1ProcessMessageReply struct { MaxUsage *time.Duration `json:",omitempty"` + AllocatedIP *engine.AllocatedIP `json:",omitempty"` ResourceAllocation *string `json:",omitempty"` Attributes *engine.AttrSProcessEventReply `json:",omitempty"` RouteProfiles engine.SortedRoutesList `json:",omitempty"` @@ -3102,47 +3218,53 @@ type V1ProcessMessageReply struct { // SetMaxUsageNeeded used by agent that use the reply as NavigableMapper // only used for gob encoding -func (v1Rply *V1ProcessMessageReply) SetMaxUsageNeeded(getMaxUsage bool) { - if v1Rply == nil { +func (r *V1ProcessMessageReply) SetMaxUsageNeeded(getMaxUsage bool) { + if r == nil { return } - v1Rply.needsMaxUsage = getMaxUsage + r.needsMaxUsage = getMaxUsage } // AsNavigableMap is part of engine.NavigableMapper interface -func (v1Rply *V1ProcessMessageReply) AsNavigableMap() map[string]*utils.DataNode { +func (r *V1ProcessMessageReply) AsNavigableMap() map[string]*utils.DataNode { cgrReply := make(map[string]*utils.DataNode) - if v1Rply.MaxUsage != nil { - cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*v1Rply.MaxUsage) - } else if v1Rply.needsMaxUsage { + if r.MaxUsage != nil { + cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(*r.MaxUsage) + } else if r.needsMaxUsage { cgrReply[utils.CapMaxUsage] = utils.NewLeafNode(0) } - if v1Rply.ResourceAllocation != nil { - cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*v1Rply.ResourceAllocation) + if r.ResourceAllocation != nil { + cgrReply[utils.CapResourceAllocation] = utils.NewLeafNode(*r.ResourceAllocation) } - if v1Rply.Attributes != nil { + if r.AllocatedIP != nil { + cgrReply[utils.CapAllocatedIP] = &utils.DataNode{ + Type: utils.NMMapType, + Map: r.AllocatedIP.AsNavigableMap(), + } + } + if r.Attributes != nil { attrs := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for _, fldName := range v1Rply.Attributes.AlteredFields { + for _, fldName := range r.Attributes.AlteredFields { fldName = strings.TrimPrefix(fldName, utils.MetaReq+utils.NestingSep) - if v1Rply.Attributes.CGREvent.HasField(fldName) { - attrs.Map[fldName] = utils.NewLeafNode(v1Rply.Attributes.CGREvent.Event[fldName]) + if r.Attributes.CGREvent.HasField(fldName) { + attrs.Map[fldName] = utils.NewLeafNode(r.Attributes.CGREvent.Event[fldName]) } } cgrReply[utils.CapAttributes] = attrs } - if v1Rply.RouteProfiles != nil { - cgrReply[utils.CapRouteProfiles] = v1Rply.RouteProfiles.AsNavigableMap() + if r.RouteProfiles != nil { + cgrReply[utils.CapRouteProfiles] = r.RouteProfiles.AsNavigableMap() } - if v1Rply.ThresholdIDs != nil { - thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1Rply.ThresholdIDs))} - for i, v := range *v1Rply.ThresholdIDs { + if r.ThresholdIDs != nil { + thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.ThresholdIDs))} + for i, v := range *r.ThresholdIDs { thIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapThresholds] = thIDs } - if v1Rply.StatQueueIDs != nil { - stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*v1Rply.StatQueueIDs))} - for i, v := range *v1Rply.StatQueueIDs { + if r.StatQueueIDs != nil { + stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(*r.StatQueueIDs))} + for i, v := range *r.StatQueueIDs { stIDs.Slice[i] = utils.NewLeafNode(v) } cgrReply[utils.CapStatQueues] = stIDs @@ -3218,6 +3340,24 @@ func (sS *SessionS) BiRPCv1ProcessMessage(ctx *context.Context, } rply.ResourceAllocation = &allocMessage } + if args.AllocateIP { + if len(sS.cgrCfg.SessionSCfg().IPsConns) == 0 { + return utils.NewErrNotConnected(utils.IPs) + } + if originID == "" { + return utils.NewErrMandatoryIeMissing(utils.OriginID) + } + if args.APIOpts == nil { + args.APIOpts = make(map[string]any) + } + args.CGREvent.APIOpts[utils.OptsIPsAllocationID] = originID + var allocIP engine.AllocatedIP + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1AllocateIP, + args.CGREvent, &allocIP); err != nil { + return utils.NewErrIPs(err) + } + rply.AllocatedIP = &allocIP + } if args.GetRoutes { routesReply, err := sS.getRoutes(args.CGREvent.Clone(), args.Paginator, args.RoutesIgnoreErrors, args.RoutesMaxCost, false) @@ -3278,6 +3418,7 @@ type V1ProcessEventReply struct { MaxUsage map[string]time.Duration `json:",omitempty"` Cost map[string]float64 `json:",omitempty"` // Cost is the cost received from Rater, ignoring accounting part ResourceAllocation map[string]string `json:",omitempty"` + AllocatedIP map[string]*engine.AllocatedIP `json:",omitempty"` Attributes map[string]*engine.AttrSProcessEventReply `json:",omitempty"` RouteProfiles map[string]engine.SortedRoutesList `json:",omitempty"` ThresholdIDs map[string][]string `json:",omitempty"` @@ -3286,25 +3427,41 @@ type V1ProcessEventReply struct { } // AsNavigableMap is part of engine.NavigableMapper interface -func (v1Rply *V1ProcessEventReply) AsNavigableMap() map[string]*utils.DataNode { +func (r *V1ProcessEventReply) AsNavigableMap() map[string]*utils.DataNode { cgrReply := make(map[string]*utils.DataNode) - if v1Rply.MaxUsage != nil { + if r.MaxUsage != nil { usage := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, v := range v1Rply.MaxUsage { + for k, v := range r.MaxUsage { usage.Map[k] = utils.NewLeafNode(v) } cgrReply[utils.CapMaxUsage] = usage } - if v1Rply.ResourceAllocation != nil { - res := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, v := range v1Rply.ResourceAllocation { + if r.ResourceAllocation != nil { + res := &utils.DataNode{ + Type: utils.NMMapType, + Map: make(map[string]*utils.DataNode), + } + for k, v := range r.ResourceAllocation { res.Map[k] = utils.NewLeafNode(v) } cgrReply[utils.CapResourceAllocation] = res } - if v1Rply.Attributes != nil { + if r.AllocatedIP != nil { + ips := &utils.DataNode{ + Type: utils.NMMapType, + Map: make(map[string]*utils.DataNode), + } + for k, v := range r.AllocatedIP { + ips.Map[k] = &utils.DataNode{ + Type: utils.NMMapType, + Map: v.AsNavigableMap(), + } + } + cgrReply[utils.CapAllocatedIP] = ips + } + if r.Attributes != nil { atts := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, att := range v1Rply.Attributes { + for k, att := range r.Attributes { attrs := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} for _, fldName := range att.AlteredFields { fldName = strings.TrimPrefix(fldName, utils.MetaReq+utils.NestingSep) @@ -3316,16 +3473,16 @@ func (v1Rply *V1ProcessEventReply) AsNavigableMap() map[string]*utils.DataNode { } cgrReply[utils.CapAttributes] = atts } - if v1Rply.RouteProfiles != nil { + if r.RouteProfiles != nil { routes := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, route := range v1Rply.RouteProfiles { + for k, route := range r.RouteProfiles { routes.Map[k] = route.AsNavigableMap() } cgrReply[utils.CapRouteProfiles] = routes } - if v1Rply.ThresholdIDs != nil { + if r.ThresholdIDs != nil { th := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, thr := range v1Rply.ThresholdIDs { + for k, thr := range r.ThresholdIDs { thIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(thr))} for i, v := range thr { thIDs.Slice[i] = utils.NewLeafNode(v) @@ -3334,9 +3491,9 @@ func (v1Rply *V1ProcessEventReply) AsNavigableMap() map[string]*utils.DataNode { } cgrReply[utils.CapThresholds] = th } - if v1Rply.StatQueueIDs != nil { + if r.StatQueueIDs != nil { st := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, sts := range v1Rply.StatQueueIDs { + for k, sts := range r.StatQueueIDs { stIDs := &utils.DataNode{Type: utils.NMSliceType, Slice: make([]*utils.DataNode, len(sts))} for i, v := range sts { stIDs.Slice[i] = utils.NewLeafNode(v) @@ -3345,15 +3502,15 @@ func (v1Rply *V1ProcessEventReply) AsNavigableMap() map[string]*utils.DataNode { } cgrReply[utils.CapStatQueues] = st } - if v1Rply.Cost != nil { + if r.Cost != nil { costs := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, cost := range v1Rply.Cost { + for k, cost := range r.Cost { costs.Map[k] = utils.NewLeafNode(cost) } } - if v1Rply.STIRIdentity != nil { + if r.STIRIdentity != nil { stir := &utils.DataNode{Type: utils.NMMapType, Map: make(map[string]*utils.DataNode)} - for k, v := range v1Rply.STIRIdentity { + for k, v := range r.STIRIdentity { stir.Map[k] = utils.NewLeafNode(v) } cgrReply[utils.OptsStirIdentity] = stir @@ -3613,6 +3770,65 @@ func (sS *SessionS) BiRPCv1ProcessEvent(ctx *context.Context, } } + // check for *ips + if argsFlagsWithParams.GetBool(utils.MetaIPs) { + if len(sS.cgrCfg.SessionSCfg().IPsConns) == 0 { + return utils.NewErrNotConnected(utils.IPs) + } + rply.AllocatedIP = make(map[string]*engine.AllocatedIP) + if ipsOpt := argsFlagsWithParams[utils.MetaIPs]; len(ipsOpt) != 0 { + for runID, cgrEv := range getDerivedEvents(events, ipsOpt.Has(utils.MetaDerivedReply)) { + originID := engine.MapEvent(cgrEv.Event).GetStringIgnoreErrors(utils.OriginID) + if originID == "" { + return utils.NewErrMandatoryIeMissing(utils.OriginID) + } + if args.APIOpts == nil { + args.APIOpts = make(map[string]any) + } + args.CGREvent.APIOpts[utils.OptsIPsAllocationID] = originID + cgrEv.SetCloneable(true) + var allocIP engine.AllocatedIP + switch { + case ipsOpt.Has(utils.MetaAuthorize): + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1AuthorizeIP, + args.CGREvent, &allocIP); err != nil { + if blockError { + return utils.NewErrIPs(err) + } + utils.Logger.Warning( + fmt.Sprintf("<%s> error: <%s> processing event %+v for RunID <%s> with IPs.", + utils.SessionS, err.Error(), cgrEv, runID)) + withErrors = true + } + case ipsOpt.Has(utils.MetaAllocate): + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1AllocateIP, + args.CGREvent, &allocIP); err != nil { + if blockError { + return utils.NewErrIPs(err) + } + utils.Logger.Warning( + fmt.Sprintf("<%s> error: <%s> processing event %+v for RunID <%s> with IPs.", + utils.SessionS, err.Error(), cgrEv, runID)) + withErrors = true + } + case ipsOpt.Has(utils.MetaRelease): + var reply string + if err = sS.connMgr.Call(context.TODO(), sS.cgrCfg.SessionSCfg().IPsConns, utils.IPsV1ReleaseIP, + args.CGREvent, &reply); err != nil { + if blockError { + return utils.NewErrIPs(err) + } + utils.Logger.Warning( + fmt.Sprintf("<%s> error: <%s> processing event %+v for RunID <%s> with IPs.", + utils.SessionS, err.Error(), cgrEv, runID)) + withErrors = true + } + } + rply.AllocatedIP[runID] = &allocIP + } + } + } + // check what we need to do for RALs (*authorize/*initiate/*update/*terminate) dbtItvl := sS.cgrCfg.SessionSCfg().DebitInterval if argsFlagsWithParams.GetBool(utils.MetaRALs) { diff --git a/sessions/sessions_test.go b/sessions/sessions_test.go index 4402e8e0b..e9dda1ce4 100644 --- a/sessions/sessions_test.go +++ b/sessions/sessions_test.go @@ -949,7 +949,7 @@ func TestSessionSNewV1AuthorizeArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, true, false, false, false, false, cgrEv, utils.Paginator{}, true, "") + rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, false, true, false, false, false, false, cgrEv, utils.Paginator{}, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -965,7 +965,7 @@ func TestSessionSNewV1AuthorizeArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, true, false, true, true, cgrEv, utils.Paginator{}, true, "") + rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, false, true, false, true, true, cgrEv, utils.Paginator{}, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v,\n received: %+v", expected, rply) } @@ -988,7 +988,7 @@ func TestSessionSNewV1AuthorizeArgs(t *testing.T) { StatIDs: []string{"test3", "test4"}, } rply = NewV1AuthorizeArgs(true, attributeIDs, false, thresholdIDs, - true, statIDs, false, true, false, true, true, cgrEv, utils.Paginator{}, false, "") + true, statIDs, false, false, true, false, true, true, cgrEv, utils.Paginator{}, false, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v,\n received: %+v", expected, rply) } @@ -1007,7 +1007,7 @@ func TestSessionSNewV1AuthorizeArgs(t *testing.T) { StatIDs: []string{"test3", "test4"}, } rply = NewV1AuthorizeArgs(true, attributeIDs, false, thresholdIDs, - true, statIDs, false, true, false, true, false, cgrEv, utils.Paginator{}, false, "100") + true, statIDs, false, false, true, false, true, false, cgrEv, utils.Paginator{}, false, "100") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v,\n received: %+v", expected, rply) } @@ -1155,7 +1155,7 @@ func TestSessionSNewV1TerminateSessionArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply := NewV1TerminateSessionArgs(true, false, true, nil, false, nil, cgrEv, true) + rply := NewV1TerminateSessionArgs(true, false, false, true, nil, false, nil, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1163,7 +1163,7 @@ func TestSessionSNewV1TerminateSessionArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1TerminateSessionArgs(false, false, false, nil, false, nil, cgrEv, true) + rply = NewV1TerminateSessionArgs(false, false, false, false, nil, false, nil, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1176,7 +1176,7 @@ func TestSessionSNewV1TerminateSessionArgs(t *testing.T) { StatIDs: []string{"test1", "test2"}, ForceDuration: true, } - rply = NewV1TerminateSessionArgs(false, false, false, thresholdIDs, false, statIDs, cgrEv, true) + rply = NewV1TerminateSessionArgs(false, false, false, false, thresholdIDs, false, statIDs, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1201,7 +1201,7 @@ func TestSessionSNewV1ProcessMessageArgs(t *testing.T) { ForceDuration: true, } rply := NewV1ProcessMessageArgs(true, nil, false, nil, false, - nil, true, true, true, false, false, cgrEv, utils.Paginator{}, true, "") + nil, true, false, true, true, false, false, cgrEv, utils.Paginator{}, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1215,7 +1215,7 @@ func TestSessionSNewV1ProcessMessageArgs(t *testing.T) { ForceDuration: true, } rply = NewV1ProcessMessageArgs(true, nil, false, nil, false, - nil, true, false, true, true, true, cgrEv, utils.Paginator{}, true, "") + nil, true, false, false, true, true, true, cgrEv, utils.Paginator{}, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1237,7 +1237,7 @@ func TestSessionSNewV1ProcessMessageArgs(t *testing.T) { ForceDuration: true, } rply = NewV1ProcessMessageArgs(true, attributeIDs, false, thresholdIDs, false, statIDs, - true, false, true, true, true, cgrEv, utils.Paginator{}, true, "") + true, false, false, true, true, true, cgrEv, utils.Paginator{}, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1251,7 +1251,7 @@ func TestSessionSNewV1ProcessMessageArgs(t *testing.T) { ForceDuration: true, } rply = NewV1ProcessMessageArgs(true, nil, false, nil, false, - nil, true, false, true, true, false, cgrEv, utils.Paginator{}, true, "100") + nil, true, false, false, true, true, false, cgrEv, utils.Paginator{}, true, "100") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1282,7 +1282,7 @@ func TestSessionSNewV1InitSessionArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply := NewV1InitSessionArgs(true, attributeIDs, true, thresholdIDs, true, statIDs, true, true, cgrEv, true) + rply := NewV1InitSessionArgs(true, attributeIDs, true, thresholdIDs, true, statIDs, true, false, true, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1305,7 +1305,7 @@ func TestSessionSNewV1InitSessionArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1InitSessionArgs(true, nil, true, nil, true, nil, true, true, cgrEv, true) + rply = NewV1InitSessionArgs(true, nil, true, nil, true, nil, true, false, true, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1318,7 +1318,7 @@ func TestSessionSNewV1InitSessionArgs(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1InitSessionArgs(true, nil, false, nil, true, nil, false, true, cgrEv, true) + rply = NewV1InitSessionArgs(true, nil, false, nil, true, nil, false, false, true, cgrEv, true) if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", expected, rply) } @@ -1745,7 +1745,7 @@ func TestSessionSNewV1AuthorizeArgsWithOpts(t *testing.T) { ForceDuration: true, } cgrArgs, _ := utils.GetRoutePaginatorFromOpts(cgrEv.APIOpts) - rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, true, false, + rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, false, true, false, false, false, false, cgrEv, cgrArgs, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rply)) @@ -1762,7 +1762,7 @@ func TestSessionSNewV1AuthorizeArgsWithOpts(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, true, + rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, false, true, false, true, true, cgrEv, cgrArgs, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rply)) @@ -1789,7 +1789,7 @@ func TestSessionSNewV1AuthorizeArgsWithOpts2(t *testing.T) { ForceDuration: true, } cgrArgs, _ := utils.GetRoutePaginatorFromOpts(cgrEv.APIOpts) - rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, true, false, false, + rply := NewV1AuthorizeArgs(true, nil, false, nil, false, nil, false, true, false, false, false, false, cgrEv, cgrArgs, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rply)) @@ -1806,7 +1806,7 @@ func TestSessionSNewV1AuthorizeArgsWithOpts2(t *testing.T) { CGREvent: cgrEv, ForceDuration: true, } - rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, true, false, + rply = NewV1AuthorizeArgs(true, nil, false, nil, true, nil, false, false, true, false, true, true, cgrEv, cgrArgs, true, "") if !reflect.DeepEqual(expected, rply) { t.Errorf("Expecting %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rply)) diff --git a/sessions/sessionscover_test.go b/sessions/sessionscover_test.go index bb521ee54..46c9f5f9e 100644 --- a/sessions/sessionscover_test.go +++ b/sessions/sessionscover_test.go @@ -1478,7 +1478,7 @@ func TestNewSession(t *testing.T) { } expectedErr := "ChargerS is disabled" - if _, err := sessions.newSession(cgrEv, "resourceID", "clientConnID", + if _, err := sessions.newSession(cgrEv, "originID", "clientConnID", time.Second, false, false); err == nil || err.Error() != expectedErr { t.Errorf("Expected %+v, received %+v", expectedErr, err) } @@ -1488,7 +1488,8 @@ func TestNewSession(t *testing.T) { expectedSess := &Session{ CGRID: "da39a3ee5e6b4b0d3255bfef95601890afd80709", Tenant: "cgrates.org", - ResourceID: "resourceID", + ResourceID: "originID", + IPAllocID: "originID", ClientConnID: "clientConnID", EventStart: map[string]any{ utils.CGRID: "da39a3ee5e6b4b0d3255bfef95601890afd80709", @@ -1511,7 +1512,7 @@ func TestNewSession(t *testing.T) { }, Chargeable: true, } - if rcv, err := sessions.newSession(cgrEv, "resourceID", "clientConnID", + if rcv, err := sessions.newSession(cgrEv, "originID", "clientConnID", time.Second, false, false); err != nil { t.Error(err) } else if !reflect.DeepEqual(rcv, expectedSess) { @@ -1520,7 +1521,7 @@ func TestNewSession(t *testing.T) { //error in mocking the call from connMgr cgrEv.ID = utils.EmptyString - if _, err := sessions.newSession(cgrEv, "resourceID", "clientConnID", + if _, err := sessions.newSession(cgrEv, "originID", "clientConnID", time.Second, false, false); err == nil || err.Error() != utils.NewErrChargerS(utils.ErrNotImplemented).Error() { t.Errorf("Expected %+v, received %+v", utils.NewErrChargerS(utils.ErrNotImplemented), err) } @@ -1529,7 +1530,7 @@ func TestNewSession(t *testing.T) { "da39a3ee5e6b4b0d3255bfef95601890afd80709": {}, } //sessions already exists - if _, err := sessions.newSession(cgrEv, "resourceID", "clientConnID", + if _, err := sessions.newSession(cgrEv, "originID", "clientConnID", time.Second, false, false); err == nil || err.Error() != utils.ErrExists.Error() { t.Errorf("Expected %+v, received %+v", utils.ErrExists, err) } @@ -2547,7 +2548,7 @@ func TestBiRPCv1AuthorizeEvent(t *testing.T) { }, } args := NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, false, false, false, nil, utils.Paginator{}, false, "") rply := &V1AuthorizeReply{ @@ -2588,7 +2589,7 @@ func TestBiRPCv1AuthorizeEvent(t *testing.T) { //Get Attributes sessions.cgrCfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 0 args = NewV1AuthorizeArgs(true, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEvent, utils.Paginator{}, false, "") args.CGREvent.ID = "TestID" args.CGREvent.Tenant = "cgrates.org" @@ -2671,7 +2672,7 @@ func TestBiRPCv1AuthorizeEvent2(t *testing.T) { APIOpts: map[string]any{}, } args := NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, false, false, false, cgrEvent, utils.Paginator{}, false, "") rply := &V1AuthorizeReply{ @@ -2695,7 +2696,7 @@ func TestBiRPCv1AuthorizeEvent2(t *testing.T) { //AuthorizeResources args = NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, false, true, false, false, false, false, cgrEvent, utils.Paginator{}, false, "") expected = "NOT_CONNECTED: ResourceS" if err := sessions.BiRPCv1AuthorizeEvent(context.Background(), args, rply); err == nil || err.Error() != expected { @@ -2715,7 +2716,7 @@ func TestBiRPCv1AuthorizeEvent2(t *testing.T) { //GetRoutes args = NewV1AuthorizeArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, cgrEvent, utils.Paginator{}, false, "") expected = "NOT_CONNECTED: RouteS" if err := sessions.BiRPCv1AuthorizeEvent(context.Background(), args, rply); err == nil || err.Error() != expected { @@ -2729,7 +2730,7 @@ func TestBiRPCv1AuthorizeEvent2(t *testing.T) { //ProcessThresholds args = NewV1AuthorizeArgs(false, []string{}, - true, []string{"TestID"}, false, []string{}, false, false, + true, []string{"TestID"}, false, []string{}, false, false, false, true, false, false, cgrEvent, utils.Paginator{}, false, "") if err := sessions.BiRPCv1AuthorizeEvent(context.Background(), args, rply); err == nil || err != utils.ErrPartiallyExecuted { t.Errorf("Expected %+v, received %+v", utils.ErrPartiallyExecuted, err) @@ -2737,7 +2738,7 @@ func TestBiRPCv1AuthorizeEvent2(t *testing.T) { //ProcessStats args = NewV1AuthorizeArgs(false, []string{}, - false, []string{}, true, []string{"TestID"}, false, false, + false, []string{}, true, []string{"TestID"}, false, false, false, true, false, false, cgrEvent, utils.Paginator{}, false, "") if err := sessions.BiRPCv1AuthorizeEvent(context.Background(), args, rply); err == nil || err != utils.ErrPartiallyExecuted { t.Errorf("Expected %+v, received %+v", utils.ErrPartiallyExecuted, err) @@ -2834,7 +2835,7 @@ func TestBiRPCv1AuthorizeEventWithDigest(t *testing.T) { APIOpts: map[string]any{}, } args := NewV1AuthorizeArgs(true, []string{}, - true, []string{}, true, []string{}, true, true, + true, []string{}, true, []string{}, false, true, true, true, false, false, cgrEvent, utils.Paginator{}, false, "") authReply := new(V1AuthorizeReplyWithDigest) @@ -2942,7 +2943,7 @@ func TestBiRPCv1InitiateSession1(t *testing.T) { APIOpts: map[string]any{}, } args := NewV1InitSessionArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, nil, true) rply := &V1InitSessionReply{} @@ -2959,7 +2960,7 @@ func TestBiRPCv1InitiateSession1(t *testing.T) { //get from cache error cgrEvent.ID = "INITIATE_SESSION_ACTIVE" args = NewV1InitSessionArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, cgrEvent, true) caches := engine.NewCacheS(cfg, dm, nil) //value's error will be nil, so the error of the initiate sessions will be the same @@ -2993,7 +2994,7 @@ func TestBiRPCv1InitiateSession1(t *testing.T) { sessions.cgrCfg.SessionSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)} args = NewV1InitSessionArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, cgrEvent, true) delete(args.CGREvent.Event, utils.OriginID) expected = "MANDATORY_IE_MISSING: [OriginID]" @@ -3010,7 +3011,7 @@ func TestBiRPCv1InitiateSession1(t *testing.T) { }, } args = NewV1InitSessionArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, cgrEvent, true) expected = "RESOURCES_ERROR:NOT_IMPLEMENTED" if err := sessions.BiRPCv1InitiateSession(context.Background(), args, rply); err == nil || err.Error() != expected { @@ -3019,7 +3020,7 @@ func TestBiRPCv1InitiateSession1(t *testing.T) { //missing subsystems args = NewV1InitSessionArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, cgrEvent, true) expected = "MANDATORY_IE_MISSING: [Subsystems]" if err := sessions.BiRPCv1InitiateSession(context.Background(), args, rply); err == nil || err.Error() != expected { @@ -3091,7 +3092,7 @@ func TestBiRPCv1InitiateSession2(t *testing.T) { } args := NewV1InitSessionArgs(false, []string{}, - false, []string{}, false, []string{}, false, true, + false, []string{}, false, []string{}, false, false, true, cgrEvent, true) rply := &V1InitSessionReply{} @@ -3120,7 +3121,7 @@ func TestBiRPCv1InitiateSession2(t *testing.T) { //here we process the thresholds args = NewV1InitSessionArgs(false, []string{}, - true, []string{}, true, []string{}, false, true, + true, []string{}, true, []string{}, false, false, true, cgrEvent, true) sessions = NewSessionS(cfg, dm, connMgr) if err := sessions.BiRPCv1InitiateSession(context.Background(), args, rply); err == nil || err != utils.ErrPartiallyExecuted { @@ -3139,7 +3140,7 @@ func TestBiRPCv1InitiateSession2(t *testing.T) { } sessions = NewSessionS(cfg, dm, connMgr) args = NewV1InitSessionArgs(false, []string{}, - true, []string{}, true, []string{}, false, true, + true, []string{}, true, []string{}, false, false, true, cgrEvent, true) expected = "EXISTS" if err := sessions.BiRPCv1InitiateSession(context.Background(), args, rply); err == nil || err != utils.ErrPartiallyExecuted { @@ -3236,7 +3237,7 @@ func TestBiRPCv1InitiateSessionWithDigest(t *testing.T) { } args := NewV1InitSessionArgs(true, []string{}, - true, []string{}, true, []string{}, true, true, + true, []string{}, true, []string{}, true, false, true, cgrEvent, true) authReply := new(V1InitReplyWithDigest) @@ -3473,7 +3474,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { }, } - args := NewV1TerminateSessionArgs(true, false, false, nil, false, nil, nil, true) + args := NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, nil, true) var reply string expected := "MANDATORY_IE_MISSING: [CGREvent]" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3481,7 +3482,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { } cgrEvent.ID = utils.EmptyString - args = NewV1TerminateSessionArgs(false, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(false, false, false, false, nil, false, nil, cgrEvent, true) expected = "MANDATORY_IE_MISSING: [Subsystems]" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) @@ -3502,7 +3503,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { engine.Cache = tmp cgrEvent.Event[utils.OriginID] = utils.EmptyString - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) expected = "MANDATORY_IE_MISSING: [OriginID]" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) @@ -3511,7 +3512,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { cgrEvent.APIOpts = make(map[string]any) cgrEvent.APIOpts[utils.OptsDebitInterval] = "invalid_time_format" - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) expected = "RALS_ERROR:time: invalid duration \"invalid_time_format\"" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) @@ -3523,13 +3524,13 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { sessions.aSessions = map[string]*Session{ "CGR_ID": {}, } - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err != nil { t.Error(err) } cgrEvent.Event[utils.CGRID] = "CHANGED_CGRID" - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) sessions.cgrCfg.SessionSCfg().ChargerSConns = []string{} expected = "RALS_ERROR:ChargerS is disabled" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3539,7 +3540,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { //update session error cgrEvent.Event[utils.Usage] = "invalid_dur_time" - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) expected = "time: invalid duration \"invalid_dur_time\"" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) @@ -3567,7 +3568,7 @@ func TestBiRPCv1TerminateSession1(t *testing.T) { sessions = NewSessionS(cfg, dm, connMgr) caches = engine.NewCacheS(cfg, dm, nil) engine.Cache = caches - args = NewV1TerminateSessionArgs(true, false, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(true, false, false, false, nil, false, nil, cgrEvent, true) expected = "RALS_ERROR:NOT_IMPLEMENTED" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) @@ -3610,7 +3611,7 @@ func TestBiRPCv1TerminateSession2(t *testing.T) { }, APIOpts: map[string]any{}, } - args := NewV1TerminateSessionArgs(false, true, false, nil, false, nil, cgrEvent, true) + args := NewV1TerminateSessionArgs(false, true, false, false, nil, false, nil, cgrEvent, true) var reply string expected := "RESOURCES_ERROR:NOT_IMPLEMENTED" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3618,14 +3619,14 @@ func TestBiRPCv1TerminateSession2(t *testing.T) { } cgrEvent.Event[utils.OriginID] = utils.EmptyString - args = NewV1TerminateSessionArgs(false, true, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(false, true, false, false, nil, false, nil, cgrEvent, true) expected = "MANDATORY_IE_MISSING: [OriginID]" if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { t.Errorf("Exepected %+v, received %+v", expected, err) } cgrEvent.Event[utils.OriginID] = "ORIGIN_ID" - args = NewV1TerminateSessionArgs(false, true, false, nil, false, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(false, true, false, false, nil, false, nil, cgrEvent, true) expected = "NOT_CONNECTED: ResourceS" sessions.cgrCfg.SessionSCfg().ResourceSConns = []string{} if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3634,7 +3635,7 @@ func TestBiRPCv1TerminateSession2(t *testing.T) { sessions.cgrCfg.SessionSCfg().ResourceSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)} cgrEvent.Tenant = "CHANGED_ID" - args = NewV1TerminateSessionArgs(false, true, true, nil, true, nil, cgrEvent, true) + args = NewV1TerminateSessionArgs(false, true, false, true, nil, true, nil, cgrEvent, true) if err := sessions.BiRPCv1TerminateSession(context.Background(), args, &reply); err == nil || err != utils.ErrPartiallyExecuted { t.Errorf("Exepected %+v, received %+v", utils.ErrPartiallyExecuted, err) } @@ -3721,7 +3722,7 @@ func TestBiRPCv1ProcessMessage1(t *testing.T) { } args := NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, false, false, + false, []string{}, false, []string{}, false, false, false, true, false, false, nil, utils.Paginator{}, false, "1") reply := V1ProcessMessageReply{} expected := "MANDATORY_IE_MISSING: [CGREvent]" @@ -3731,7 +3732,7 @@ func TestBiRPCv1ProcessMessage1(t *testing.T) { cgrEvent.ID = utils.EmptyString args = NewV1ProcessMessageArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, false, false, false, cgrEvent, utils.Paginator{}, false, "1") expected = "ATTRIBUTES_ERROR:NOT_IMPLEMENTED" if err := sessions.BiRPCv1ProcessMessage(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3740,7 +3741,7 @@ func TestBiRPCv1ProcessMessage1(t *testing.T) { cgrEvent.ID = "test_id" args = NewV1ProcessMessageArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, false, false, false, cgrEvent, utils.Paginator{}, false, "1") expected = "NOT_CONNECTED: ResourceS" if err := sessions.BiRPCv1ProcessMessage(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3756,7 +3757,7 @@ func TestBiRPCv1ProcessMessage1(t *testing.T) { } engine.Cache = caches args = NewV1ProcessMessageArgs(true, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, false, false, false, cgrEvent, utils.Paginator{}, false, "1") engine.Cache.SetWithoutReplicate(utils.CacheRPCResponses, utils.ConcatenatedKey(utils.SessionSv1ProcessMessage, args.CGREvent.ID), value, nil, true, utils.NonTransactional) @@ -3830,7 +3831,7 @@ func TestBiRPCv1ProcessMessage2(t *testing.T) { } args := NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, true, false, false, cgrEvent, utils.Paginator{}, false, "1") reply := V1ProcessMessageReply{} expected := "MANDATORY_IE_MISSING: [OriginID]" @@ -3840,7 +3841,7 @@ func TestBiRPCv1ProcessMessage2(t *testing.T) { cgrEvent.Event[utils.OriginID] = "ID" args = NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, true, false, + false, []string{}, false, []string{}, true, false, false, false, false, false, cgrEvent, utils.Paginator{}, false, "1") reply = V1ProcessMessageReply{} expected = "RESOURCES_ERROR:NOT_IMPLEMENTED" @@ -3850,7 +3851,7 @@ func TestBiRPCv1ProcessMessage2(t *testing.T) { cgrEvent.Event[utils.OriginID] = "ORIGIN_ID" args = NewV1ProcessMessageArgs(false, []string{}, - false, []string{}, false, []string{}, true, true, + false, []string{}, false, []string{}, true, false, true, true, false, false, cgrEvent, utils.Paginator{}, false, "1") expected = "NOT_CONNECTED: RouteS" if err := sessions.BiRPCv1ProcessMessage(context.Background(), args, &reply); err == nil || err.Error() != expected { @@ -3864,7 +3865,7 @@ func TestBiRPCv1ProcessMessage2(t *testing.T) { } args = NewV1ProcessMessageArgs(false, []string{}, - true, []string{}, true, []string{}, true, true, + true, []string{}, true, []string{}, true, false, true, true, false, false, cgrEvent, utils.Paginator{}, false, "1") sessions.cgrCfg.SessionSCfg().ChargerSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaChargers)} diff --git a/utils/consts.go b/utils/consts.go index 4df816ca4..ff362484f 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1336,6 +1336,7 @@ const ( MetaGigawords = "*gigawords" CapAttributes = "Attributes" CapResourceAllocation = "ResourceAllocation" + CapAllocatedIP = "AllocatedIP" CapMaxUsage = "MaxUsage" CapRoutes = "Routes" CapRouteProfiles = "RouteProfiles" diff --git a/utils/errors.go b/utils/errors.go index 85d5e6534..d375f69c9 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -194,6 +194,10 @@ func NewErrResourceS(err error) error { return fmt.Errorf("RESOURCES_ERROR:%s", err) } +func NewErrIPs(err error) error { + return fmt.Errorf("IPS_ERROR:%s", err) +} + func NewErrRouteS(err error) error { return fmt.Errorf("ROUTES_ERROR:%s", err) }