From 79d654c2a0c9ce601f2219870f558de594a3a0a4 Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 11 May 2014 21:48:51 +0200 Subject: [PATCH] Aliases now with multi-tenant support, ApierV1.GetRatingSubjectAliases, ApierV1.GetAccountAliases RPC commands implementation --- apier/apier_local_test.go | 26 +++++++++++++++++++ engine/calldesc.go | 4 +-- engine/loader_csv.go | 4 +-- engine/loader_csv_test.go | 10 ++++---- engine/loader_db.go | 4 +-- engine/storage_interface.go | 2 ++ engine/storage_map.go | 20 +++++++++++++++ engine/storage_redis.go | 43 +++++++++++++++++++++++++++++++ engine/storage_test.go | 51 ++++++++++++++++++++++++++++++++++++- pkg/debian/copyright | 2 +- utils/apitpdata.go | 26 +++++++++++++++++++ utils/coreutils.go | 8 ++++++ 12 files changed, 187 insertions(+), 13 deletions(-) diff --git a/apier/apier_local_test.go b/apier/apier_local_test.go index 6f60b5f91..591b2e40e 100644 --- a/apier/apier_local_test.go +++ b/apier/apier_local_test.go @@ -1458,6 +1458,32 @@ func TestLocalRemDC(t *testing.T) { } } +func TestLocalGetRatingSubjectAliases(t *testing.T) { + if !*testLocal { + return + } + attrs := utils.AttrGetRatingSubjectAliases{Tenant: "cgrates.org", Subject: "1001"} + var subjAliases []string + if err := rater.Call("ApierV1.GetRatingSubjectAliases", attrs, &subjAliases); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(subjAliases) != 0 { + t.Error("Unexpected subject aliases returned", subjAliases) + } +} + +func TestLocalAccountAliases(t *testing.T) { + if !*testLocal { + return + } + attrs := utils.AttrGetAccountAliases{Tenant: "cgrates.org", Account: "1001"} + var acntAliases []string + if err := rater.Call("ApierV1.GetAccountAliases", attrs, &acntAliases); err != nil { + t.Error("Unexpected error", err.Error()) + } else if len(acntAliases) != 0 { + t.Error("Unexpected subject aliases returned", acntAliases) + } +} + // Simply kill the engine after we are done with tests within this file func TestStopEngine(t *testing.T) { if !*testLocal { diff --git a/engine/calldesc.go b/engine/calldesc.go index 8196b0950..4b4020ba1 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -277,7 +277,7 @@ func (cd *CallDescriptor) addRatingInfos(ris RatingInfos) bool { // The prefixLen is limiting the length of the destination prefix. func (cd *CallDescriptor) GetKey(subject string) string { // check if subject is alias - if rs, err := cache2go.GetCached(RP_ALIAS_PREFIX + subject); err == nil { + if rs, err := cache2go.GetCached(RP_ALIAS_PREFIX + utils.RatingProfileAliasKey(cd.Tenant,subject)); err == nil { realSubject := rs.(string) subject = realSubject cd.Subject = realSubject @@ -290,7 +290,7 @@ func (cd *CallDescriptor) GetAccountKey() string { subj := cd.Subject if cd.Account != "" { // check if subject is alias - if realSubject, err := cache2go.GetCached(ACC_ALIAS_PREFIX + subj); err == nil { + if realSubject, err := cache2go.GetCached(ACC_ALIAS_PREFIX + utils.AccountAliasKey(cd.Tenant,subj)); err == nil { cd.Account = realSubject.(string) } subj = cd.Account diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 41e4be6c0..4eb8a34fb 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -518,7 +518,7 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) { if len(aliases) > 1 { subject = aliases[0] for _, alias := range aliases[1:] { - csvr.rpAliases[alias] = subject + csvr.rpAliases[utils.RatingProfileAliasKey(tenant, alias)] = subject } } key := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, subject) @@ -796,7 +796,7 @@ func (csvr *CSVReader) LoadAccountActions() (err error) { if len(aliases) > 1 { account = aliases[0] for _, alias := range aliases[1:] { - csvr.accAliases[alias] = account + csvr.accAliases[utils.AccountAliasKey(tenant, alias)] = account } } tag := fmt.Sprintf("%s:%s:%s", direction, tenant, account) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 9ee930b59..0174e565e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -911,9 +911,9 @@ func TestLoadRpAliases(t *testing.T) { if len(csvr.rpAliases) != 3 { t.Error("Failed to load rp aliases: ", csvr.rpAliases) } - if csvr.rpAliases["a1"] != "minu" || - csvr.rpAliases["a2"] != "minu" || - csvr.rpAliases["a3"] != "minu" { + if csvr.rpAliases[utils.RatingProfileAliasKey("vdf", "a1")] != "minu" || + csvr.rpAliases[utils.RatingProfileAliasKey("vdf", "a2")] != "minu" || + csvr.rpAliases[utils.RatingProfileAliasKey("vdf", "a3")] != "minu" { t.Error("Error loading rp aliases: ", csvr.rpAliases) } } @@ -922,8 +922,8 @@ func TestLoadAccAliases(t *testing.T) { if len(csvr.accAliases) != 2 { t.Error("Failed to load acc aliases: ", csvr.accAliases) } - if csvr.accAliases["a1"] != "minitsboy" || - csvr.accAliases["a2"] != "minitsboy" { + if csvr.accAliases[utils.AccountAliasKey("vdf", "a1")] != "minitsboy" || + csvr.accAliases[utils.AccountAliasKey("vdf", "a2")] != "minitsboy" { t.Error("Error loading acc aliases: ", csvr.accAliases) } } diff --git a/engine/loader_db.go b/engine/loader_db.go index bdabf56e7..f8919ac50 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -348,7 +348,7 @@ func (dbr *DbReader) LoadRatingProfiles() error { if len(aliases) > 1 { tpRpf.Subject = aliases[0] for _, alias := range aliases[1:] { - dbr.rpAliases[alias] = tpRpf.Subject + dbr.rpAliases[utils.RatingProfileAliasKey(tpRpf.Tenant, alias)] = tpRpf.Subject } } rpf := &RatingProfile{Id: tpRpf.KeyId()} @@ -584,7 +584,7 @@ func (dbr *DbReader) LoadAccountActions() (err error) { if len(aliases) > 1 { aa.Account = aliases[0] for _, alias := range aliases[1:] { - dbr.accAliases[alias] = aa.Account + dbr.accAliases[utils.AccountAliasKey(aa.Tenant, alias)] = aa.Account } } aTriggers, exists := dbr.actionsTriggers[aa.ActionTriggersId] diff --git a/engine/storage_interface.go b/engine/storage_interface.go index bbdb7294b..362040b1b 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -80,6 +80,7 @@ type RatingStorage interface { GetRpAlias(string, bool) (string, error) SetRpAlias(string, string) error RemoveRpAliases([]string) error + GetRPAliases(string, string) ([]string, error) GetDestination(string) (*Destination, error) SetDestination(*Destination) error GetLCR(string, bool) (*LCR, error) @@ -99,6 +100,7 @@ type AccountingStorage interface { GetAccAlias(string, bool) (string, error) SetAccAlias(string, string) error RemoveAccAliases([]string) error + GetAccountAliases(string, string) ([]string, error) GetActionTimings(string) (ActionPlan, error) SetActionTimings(string, ActionPlan) error GetAllActionTimings() (map[string]ActionPlan, error) diff --git a/engine/storage_map.go b/engine/storage_map.go index 849587503..0fc5a4285 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -288,6 +288,16 @@ func (ms *MapStorage) RemoveRpAliases(accounts []string) (err error) { return } +func (ms *MapStorage) GetRPAliases(tenant, subject string) (aliases []string, err error) { + for key, value := range ms.dict { + tenantPrfx := RP_ALIAS_PREFIX + tenant + utils.CONCATENATED_KEY_SEP + if strings.HasPrefix(key, RP_ALIAS_PREFIX) && len(key) >= len(tenantPrfx) && key[:len(tenantPrfx)] == tenantPrfx && subject == string(value) { + aliases = append(aliases, key[len(tenantPrfx):]) + } + } + return aliases, nil +} + func (ms *MapStorage) GetAccAlias(key string, checkDb bool) (alias string, err error) { key = ACC_ALIAS_PREFIX + key if x, err := cache2go.GetCached(key); err == nil { @@ -320,6 +330,16 @@ func (ms *MapStorage) RemoveAccAliases(accounts []string) (err error) { return } +func (ms *MapStorage) GetAccountAliases(tenant, account string) (aliases []string, err error) { + for key, value := range ms.dict { + tenantPrfx := ACC_ALIAS_PREFIX + tenant + utils.CONCATENATED_KEY_SEP + if strings.HasPrefix(key, ACC_ALIAS_PREFIX) && len(key) >= len(tenantPrfx) && key[:len(tenantPrfx)] == tenantPrfx && account == string(value) { + aliases = append(aliases, key[len(tenantPrfx):]) + } + } + return aliases, nil +} + func (ms *MapStorage) GetDestination(key string) (dest *Destination, err error) { key = DESTINATION_PREFIX + key if values, ok := ms.dict[key]; ok { diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 673782d47..127cef286 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -343,6 +343,7 @@ func (rs *RedisStorage) SetRpAlias(key, alias string) (err error) { return } +// Returns the aliases of one specific rating profile subject on a tenant func (rs *RedisStorage) RemoveRpAliases(accounts []string) (err error) { if alsKeys, err := rs.db.Keys(RP_ALIAS_PREFIX + "*"); err != nil { return err @@ -363,6 +364,26 @@ func (rs *RedisStorage) RemoveRpAliases(accounts []string) (err error) { return } +func (rs *RedisStorage) GetRPAliases(tenant, subject string) (aliases []string, err error) { + alsKeys, err := rs.db.Keys(RP_ALIAS_PREFIX + "*") + if err != nil { + return nil, err + } + for _, key := range alsKeys { + tenantPrfx := RP_ALIAS_PREFIX + tenant + utils.CONCATENATED_KEY_SEP + if len(key) < len(tenantPrfx) || tenantPrfx != key[:len(tenantPrfx)] { // filter out the tenant for accounts + continue + } + if alsSubj, err := rs.GetRpAlias(key[len(ACC_ALIAS_PREFIX):], true); err != nil { + return nil, err + } else if alsSubj == subject { + alsFromKey := key[len(tenantPrfx):] // take out the alias out of key+tenant + aliases = append(aliases, alsFromKey) + } + } + return aliases, nil +} + func (rs *RedisStorage) GetLCR(key string, checkDb bool) (lcr *LCR, err error) { key = LCR_PREFIX + key if x, err := cache2go.GetCached(key); err == nil { @@ -402,6 +423,7 @@ func (rs *RedisStorage) GetAccAlias(key string, checkDb bool) (alias string, err return } +// Adds one alias for one account func (rs *RedisStorage) SetAccAlias(key, alias string) (err error) { err = rs.db.Set(ACC_ALIAS_PREFIX+key, []byte(alias)) //cache2go.Cache(ALIAS_PREFIX+key, alias) @@ -428,6 +450,27 @@ func (rs *RedisStorage) RemoveAccAliases(accounts []string) (err error) { return } +// Returns the aliases of one specific account on a tenant +func (rs *RedisStorage) GetAccountAliases(tenant, account string) (aliases []string, err error) { + alsKeys, err := rs.db.Keys(ACC_ALIAS_PREFIX + "*") + if err != nil { + return nil, err + } + for _, key := range alsKeys { + tenantPrfx := ACC_ALIAS_PREFIX + tenant + utils.CONCATENATED_KEY_SEP + if len(key) < len(tenantPrfx) || tenantPrfx != key[:len(tenantPrfx)] { // filter out the tenant for accounts + continue + } + if alsAcnt, err := rs.GetAccAlias(key[len(ACC_ALIAS_PREFIX):], true); err != nil { + return nil, err + } else if alsAcnt == account { + alsFromKey := key[len(tenantPrfx):] // take out the alias out of key+tenant + aliases = append(aliases, alsFromKey) + } + } + return aliases, nil +} + func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) { key = DESTINATION_PREFIX + key var values []byte diff --git a/engine/storage_test.go b/engine/storage_test.go index 917744b61..62fc5d173 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -19,10 +19,13 @@ along with this program. If not, see package engine import ( + "reflect" + "sort" "testing" "time" "github.com/cgrates/cgrates/cache2go" + "github.com/cgrates/cgrates/utils" ) func TestMsgpackStructsAdded(t *testing.T) { @@ -110,7 +113,7 @@ func TestCacheRefresh(t *testing.T) { } func TestCacheAliases(t *testing.T) { - if subj, err := cache2go.GetCached(RP_ALIAS_PREFIX + "a3"); err != nil || subj != "minu" { + if subj, err := cache2go.GetCached(RP_ALIAS_PREFIX + utils.RatingProfileAliasKey("vdf", "a3")); err != nil || subj != "minu" { t.Error("Error caching alias: ", subj, err) } } @@ -125,6 +128,52 @@ func TestStoreInterfaces(t *testing.T) { var _ LogStorage = sql } +func TestGetRPAliases(t *testing.T) { + if err := dataStorage.SetRpAlias(utils.RatingProfileAliasKey("cgrates.org", "2001"), "1001"); err != nil { + t.Error(err) + } + if err := dataStorage.SetRpAlias(utils.RatingProfileAliasKey("cgrates.org", "2002"), "1001"); err != nil { + t.Error(err) + } + if err := dataStorage.SetRpAlias(utils.RatingProfileAliasKey("itsyscom.com", "2003"), "1001"); err != nil { + t.Error(err) + } + expectAliases := sort.StringSlice([]string{"2001", "2002"}) + expectAliases.Sort() + if aliases, err := dataStorage.GetRPAliases("cgrates.org", "1001"); err != nil { + t.Error(err) + } else { + aliases := sort.StringSlice(aliases) + aliases.Sort() + if !reflect.DeepEqual(aliases, expectAliases) { + t.Errorf("Expecting: %v, received: %v", expectAliases, aliases) + } + } +} + +func TestGetAccountAliases(t *testing.T) { + if err := accountingStorage.SetAccAlias(utils.AccountAliasKey("cgrates.org", "2001"), "1001"); err != nil { + t.Error(err) + } + if err := accountingStorage.SetAccAlias(utils.AccountAliasKey("cgrates.org", "2002"), "1001"); err != nil { + t.Error(err) + } + if err := accountingStorage.SetAccAlias(utils.AccountAliasKey("itsyscom.com", "2003"), "1001"); err != nil { + t.Error(err) + } + expectAliases := sort.StringSlice([]string{"2001", "2002"}) + expectAliases.Sort() + if aliases, err := accountingStorage.GetAccountAliases("cgrates.org", "1001"); err != nil { + t.Error(err) + } else { + aliases := sort.StringSlice(aliases) + aliases.Sort() + if !reflect.DeepEqual(aliases, expectAliases) { + t.Errorf("Expecting: %v, received: %v", expectAliases, aliases) + } + } +} + /************************** Benchmarks *****************************/ func GetUB() *Account { diff --git a/pkg/debian/copyright b/pkg/debian/copyright index 7d1076f61..6b10f41c4 100644 --- a/pkg/debian/copyright +++ b/pkg/debian/copyright @@ -8,5 +8,5 @@ The Debian packaging is: Copyright (C) 2011 Canonical Ltd. Further development: - Copyright (C) 2014 ITsysCOM + Copyright (C) 2014 ITsysCOM GmbH diff --git a/utils/apitpdata.go b/utils/apitpdata.go index e5fb815e6..faa01e0a2 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -384,3 +384,29 @@ type AttrGetDestination struct { type AttrDerivedChargers struct { Direction, Tenant, Category, Account, Subject string } + +type AttrGetRatingSubjectAliases struct { + Tenant, Subject string +} + +type AttrRemRatingSubjectAliases struct { + Tenant, Subject string +} + +type AttrAddRatingSubjectAliases struct { + Tenant, Subject string + Aliases []string +} + +type AttrGetAccountAliases struct { + Tenant, Account string +} + +type AttrRemAccountAliases struct { + Tenant, Account string +} + +type AttrAddAccountAliases struct { + Tenant, Account string + Aliases []string +} diff --git a/utils/coreutils.go b/utils/coreutils.go index bb8e78664..3471b297b 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -234,3 +234,11 @@ func ConcatenatedKey(keyVals ...string) string { } return resKey } + +func RatingProfileAliasKey(tenant, subject string) string { + return ConcatenatedKey(tenant, subject) +} + +func AccountAliasKey(tenant, account string) string { + return ConcatenatedKey(tenant, account) +}