diff --git a/config/cdrcconfig.go b/config/cdrcconfig.go index 829834144..f6dd6d1c7 100644 --- a/config/cdrcconfig.go +++ b/config/cdrcconfig.go @@ -75,3 +75,23 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { } return nil } + +// Clone itself into a new CdrcConfig +func (self *CdrcConfig) Clone() *CdrcConfig { + clnCdrc := new(CdrcConfig) + clnCdrc.Enabled = self.Enabled + clnCdrc.CdrsAddress = self.CdrsAddress + clnCdrc.CdrFormat = self.CdrFormat + clnCdrc.FieldSeparator = self.FieldSeparator + clnCdrc.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor + clnCdrc.RunDelay = self.RunDelay + clnCdrc.CdrInDir = self.CdrInDir + clnCdrc.CdrOutDir = self.CdrOutDir + clnCdrc.CdrSourceId = self.CdrSourceId + clnCdrc.CdrFields = make([]*CfgCdrField, len(self.CdrFields)) + for idx, fld := range self.CdrFields { + clonedVal := *fld + clnCdrc.CdrFields[idx] = &clonedVal + } + return clnCdrc +} diff --git a/config/cdreconfig.go b/config/cdreconfig.go index 8e05a23d0..2359a5537 100644 --- a/config/cdreconfig.go +++ b/config/cdreconfig.go @@ -84,3 +84,33 @@ func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { } return nil } + +// Clone itself into a new CdreConfig +func (self *CdreConfig) Clone() *CdreConfig { + clnCdre := new(CdreConfig) + clnCdre.CdrFormat = self.CdrFormat + clnCdre.FieldSeparator = self.FieldSeparator + clnCdre.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor + clnCdre.CostMultiplyFactor = self.CostMultiplyFactor + clnCdre.CostRoundingDecimals = self.CostRoundingDecimals + clnCdre.CostShiftDigits = self.CostShiftDigits + clnCdre.MaskDestId = self.MaskDestId + clnCdre.MaskLength = self.MaskLength + clnCdre.ExportDir = self.ExportDir + clnCdre.HeaderFields = make([]*CfgCdrField, len(self.HeaderFields)) + for idx, fld := range self.HeaderFields { + clonedVal := *fld + clnCdre.HeaderFields[idx] = &clonedVal + } + clnCdre.ContentFields = make([]*CfgCdrField, len(self.ContentFields)) + for idx, fld := range self.ContentFields { + clonedVal := *fld + clnCdre.ContentFields[idx] = &clonedVal + } + clnCdre.TrailerFields = make([]*CfgCdrField, len(self.TrailerFields)) + for idx, fld := range self.TrailerFields { + clonedVal := *fld + clnCdre.TrailerFields[idx] = &clonedVal + } + return clnCdre +} diff --git a/config/cdreconfig_test.go b/config/cdreconfig_test.go index 819e5259e..878d8bd6b 100644 --- a/config/cdreconfig_test.go +++ b/config/cdreconfig_test.go @@ -16,3 +16,82 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ package config + +import ( + "github.com/cgrates/cgrates/utils" + "reflect" + "testing" +) + +func TestCdreCfgClone(t *testing.T) { + cgrIdRsrs, _ := utils.ParseRSRFields("cgrid", utils.INFIELD_SEP) + runIdRsrs, _ := utils.ParseRSRFields("mediation_runid", utils.INFIELD_SEP) + emptyFields := []*CfgCdrField{} + initContentFlds := []*CfgCdrField{ + &CfgCdrField{Tag: "CgrId", + Type: "cdrfield", + CdrFieldId: "cgrid", + Value: cgrIdRsrs}, + &CfgCdrField{Tag: "RunId", + Type: "cdrfield", + CdrFieldId: "mediation_runid", + Value: runIdRsrs}, + } + initCdreCfg := &CdreConfig{ + CdrFormat: "csv", + FieldSeparator: rune(','), + DataUsageMultiplyFactor: 1.0, + CostMultiplyFactor: 1.0, + CostRoundingDecimals: -1, + CostShiftDigits: 0, + MaskDestId: "MASKED_DESTINATIONS", + MaskLength: 0, + ExportDir: "/var/log/cgrates/cdre", + ContentFields: initContentFlds, + } + eClnContentFlds := []*CfgCdrField{ + &CfgCdrField{Tag: "CgrId", + Type: "cdrfield", + CdrFieldId: "cgrid", + Value: cgrIdRsrs}, + &CfgCdrField{Tag: "RunId", + Type: "cdrfield", + CdrFieldId: "mediation_runid", + Value: runIdRsrs}, + } + eClnCdreCfg := &CdreConfig{ + CdrFormat: "csv", + FieldSeparator: rune(','), + DataUsageMultiplyFactor: 1.0, + CostMultiplyFactor: 1.0, + CostRoundingDecimals: -1, + CostShiftDigits: 0, + MaskDestId: "MASKED_DESTINATIONS", + MaskLength: 0, + ExportDir: "/var/log/cgrates/cdre", + HeaderFields: emptyFields, + ContentFields: eClnContentFlds, + TrailerFields: emptyFields, + } + clnCdreCfg := initCdreCfg.Clone() + if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { + t.Errorf("Cloned result: %+v", clnCdreCfg) + } + initCdreCfg.DataUsageMultiplyFactor = 1024.0 + if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance + t.Errorf("Cloned result: %+v", clnCdreCfg) + } + initContentFlds[0].Tag = "Destination" + if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance + t.Errorf("Cloned result: %+v", clnCdreCfg) + } + clnCdreCfg.CostShiftDigits = 2 + if initCdreCfg.CostShiftDigits != 0 { + t.Error("Unexpected CostShiftDigits: ", initCdreCfg.CostShiftDigits) + } + clnCdreCfg.ContentFields[0].CdrFieldId = "destination" + if initCdreCfg.ContentFields[0].CdrFieldId != "cgrid" { + t.Error("Unexpected change of CdrFieldId: ", initCdreCfg.ContentFields[0].CdrFieldId) + } + +} diff --git a/config/config.go b/config/config.go index 9c0cc60a2..c9f174d69 100644 --- a/config/config.go +++ b/config/config.go @@ -63,6 +63,8 @@ func NewDefaultCGRConfig() (*CGRConfig, error) { if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil { return nil, err } + cfg.dfltCdreProfile = cfg.CdreProfiles[utils.META_DEFAULT].Clone() // So default will stay unique, will have nil pointer in case of no defaults loaded which is an extra check + cfg.dfltCdrcProfile = cfg.CdrcProfiles[utils.META_DEFAULT].Clone() if err := cfg.checkConfigSanity(); err != nil { return nil, err } @@ -169,7 +171,9 @@ type CGRConfig struct { CDRSStoreDisable bool // When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario CDRStatsEnabled bool // Enable CDR Stats service CDRStatConfig *CdrStatsConfig // Active cdr stats configuration instances, platform level + dfltCdreProfile *CdreConfig // Use it to cache the default cdreConfig profile, so we can clone the orginal one in separate instances CdreProfiles map[string]*CdreConfig + dfltCdrcProfile *CdrcConfig // Use it to cache the default cdrcConfig profile CdrcProfiles map[string]*CdrcConfig // Number of CDRC instances running imports SMEnabled bool SMSwitchType string @@ -481,7 +485,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { if _, hasProfile := self.CdreProfiles[profileName]; !hasProfile { // New profile, create before loading from json self.CdreProfiles[profileName] = new(CdreConfig) if profileName != utils.META_DEFAULT { - self.CdreProfiles[profileName] = self.CdreProfiles[utils.META_DEFAULT] // Load defaults into newly initialized config + self.CdreProfiles[profileName] = self.dfltCdreProfile.Clone() // Clone default so we do not inherit pointers } } if err = self.CdreProfiles[profileName].loadFromJsonCfg(jsnCdre1Cfg); err != nil { // Update the existing profile with content from json config @@ -497,7 +501,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { if _, hasProfile := self.CdrcProfiles[profileName]; !hasProfile { self.CdrcProfiles[profileName] = new(CdrcConfig) if profileName != utils.META_DEFAULT { - self.CdrcProfiles[profileName] = self.CdrcProfiles[utils.META_DEFAULT] // Load defaults into newly initialized config + self.CdrcProfiles[profileName] = self.dfltCdrcProfile.Clone() // Clone default so we do not inherit pointers } } if err = self.CdrcProfiles[profileName].loadFromJsonCfg(jsnCrc1Cfg); err != nil { diff --git a/config/multifiles_local_test.go b/config/multifiles_local_test.go index 52116eddb..fb2c5bbad 100644 --- a/config/multifiles_local_test.go +++ b/config/multifiles_local_test.go @@ -66,16 +66,13 @@ func TestMfCdreDefaultInstance(t *testing.T) { if mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor != 1024.0 { t.Error("Default instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor) } - if len(mfCgrCfg.CdreProfiles[prfl].HeaderFields) != 2 { + if len(mfCgrCfg.CdreProfiles[prfl].HeaderFields) != 0 { t.Error("Default instance has number of header fields: ", len(mfCgrCfg.CdreProfiles[prfl].HeaderFields)) } - if mfCgrCfg.CdreProfiles[prfl].HeaderFields[1].Tag != "RunId" { - t.Error("Unexpected headerField value: ", mfCgrCfg.CdreProfiles[prfl].HeaderFields[1].Tag) - } - if len(mfCgrCfg.CdreProfiles[prfl].ContentFields) != 9 { + if len(mfCgrCfg.CdreProfiles[prfl].ContentFields) != 12 { t.Error("Default instance has number of content fields: ", len(mfCgrCfg.CdreProfiles[prfl].ContentFields)) } - if mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag != "Account" { + if mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag != "Direction" { t.Error("Unexpected headerField value: ", mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag) } } @@ -89,7 +86,7 @@ func TestMfCdreExport1Instance(t *testing.T) { t.Error("Export1 instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].CdrFormat) } if mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor != 1.0 { - t.Error("Export1 instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor) + t.Error("Export1 instance has DataUsageMultiplyFormat: ", mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor) } if mfCgrCfg.CdreProfiles[prfl].CostRoundingDecimals != 3.0 { t.Error("Export1 instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].CostRoundingDecimals) diff --git a/data/conf/samples/multifiles/a.json b/data/conf/samples/multifiles/a.json index 69f44552a..1834c4631 100644 --- a/data/conf/samples/multifiles/a.json +++ b/data/conf/samples/multifiles/a.json @@ -10,9 +10,6 @@ "cdre": { "*default": { "content_fields": [ // template of the exported content fields - {"tag": "CgrId", "cdr_field_id": "cgrid", "type": "cdrfield", "value": "cgrid"}, - {"tag":"RunId", "cdr_field_id": "mediation_runid", "type": "cdrfield", "value": "mediation_runid"}, - {"tag":"Tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "tor"}, {"tag":"AccId", "cdr_field_id": "accid", "type": "cdrfield", "value": "accid"}, {"tag":"ReqType", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "reqtype"}, {"tag":"Direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "direction"},