From aca6323c6fd2332713844e97af6a67e6d4fa3428 Mon Sep 17 00:00:00 2001 From: TeoV Date: Mon, 21 Dec 2020 13:55:41 +0200 Subject: [PATCH] Add infrastructure for AccountS in migrator --- apier/v1/accountprofiles.go | 164 +++++++++++++++++++++++++++++++++++ migrator/account_profiles.go | 95 ++++++++++++++++++++ migrator/migrator.go | 2 + 3 files changed, 261 insertions(+) create mode 100644 apier/v1/accountprofiles.go create mode 100644 migrator/account_profiles.go diff --git a/apier/v1/accountprofiles.go b/apier/v1/accountprofiles.go new file mode 100644 index 000000000..d39fc8d78 --- /dev/null +++ b/apier/v1/accountprofiles.go @@ -0,0 +1,164 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package v1 + +import ( + "time" + + "github.com/cgrates/cgrates/accounts" + + "github.com/cgrates/cgrates/utils" +) + +// GetAccountProfile returns an Account Profile +func (apierSv1 *APIerSv1) GetAccountProfile(arg *utils.TenantIDWithOpts, reply *utils.AccountProfile) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + ap, err := apierSv1.DataManager.GetAccountProfile(tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err + } + *reply = *ap + return nil +} + +// GetAccountProfileIDs returns list of action profile IDs registered for a tenant +func (apierSv1 *APIerSv1) GetAccountProfileIDs(args *utils.PaginatorWithTenant, actPrfIDs *[]string) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + prfx := utils.AccountProfilePrefix + tnt + utils.CONCATENATED_KEY_SEP + keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[len(prfx):] + } + *actPrfIDs = args.PaginateStringSlice(retIDs) + return nil +} + +// GetAccountProfileIDsCount sets in reply var the total number of AccountProfileIDs registered for a tenant +// returns ErrNotFound in case of 0 AccountProfileIDs +func (apierSv1 *APIerSv1) GetAccountProfileIDsCount(args *utils.TenantWithOpts, reply *int) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + var keys []string + prfx := utils.AccountProfilePrefix + tnt + utils.CONCATENATED_KEY_SEP + if keys, err = apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx); err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + *reply = len(keys) + return +} + +type AccountProfileWithCache struct { + *utils.AccountProfileWithOpts + Cache *string +} + +//SetAccountProfile add/update a new Account Profile +func (apierSv1 *APIerSv1) SetAccountProfile(ap *AccountProfileWithCache, reply *string) error { + if missing := utils.MissingStructFields(ap.AccountProfile, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if ap.Tenant == utils.EmptyString { + ap.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } + + if err := apierSv1.DataManager.SetAccountProfile(ap.AccountProfile, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheAccountProfiles and store it in database + if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheAccountProfiles: time.Now().UnixNano()}); err != nil { + return utils.APIErrorHandler(err) + } + if err := apierSv1.CallCache(ap.Cache, ap.Tenant, utils.CacheAccountProfiles, + ap.TenantID(), &ap.FilterIDs, nil, ap.Opts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// RemoveAccountProfile remove a specific Account Profile +func (apierSv1 *APIerSv1) RemoveAccountProfile(arg *utils.TenantIDWithCache, reply *string) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = apierSv1.Config.GeneralCfg().DefaultTenant + } + if err := apierSv1.DataManager.RemoveAccountProfile(tnt, arg.ID, + utils.NonTransactional, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheAccountProfiles and store it in database + if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheAccountProfiles: time.Now().UnixNano()}); err != nil { + return utils.APIErrorHandler(err) + } + if err := apierSv1.CallCache(arg.Cache, tnt, utils.CacheAccountProfiles, + utils.ConcatenatedKey(tnt, arg.ID), nil, nil, arg.Opts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// NewAccountSv1 initializes AccountSv1 +func NewAccountSv1(aS *accounts.AccountS) *AccountSv1 { + return &AccountSv1{aS: aS} +} + +// AccountSv1 exports RPC from RLs +type AccountSv1 struct { + aS *accounts.AccountS +} + +// Call implements rpcclient.ClientConnector interface for internal RPC +func (aSv1 *AccountSv1) Call(serviceMethod string, + args interface{}, reply interface{}) error { + return utils.APIerRPCCall(aSv1, serviceMethod, args, reply) +} + +// Ping return pong if the service is active +func (aSv1 *AccountSv1) Ping(ign *utils.CGREventWithOpts, reply *string) error { + *reply = utils.Pong + return nil +} diff --git a/migrator/account_profiles.go b/migrator/account_profiles.go new file mode 100644 index 000000000..a91d972a2 --- /dev/null +++ b/migrator/account_profiles.go @@ -0,0 +1,95 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package migrator + +import ( + "fmt" + "strings" + + "github.com/cgrates/cgrates/engine" + + "github.com/cgrates/cgrates/utils" +) + +func (m *Migrator) migrateCurrentAccountProfiles() (err error) { + var ids []string + ids, err = m.dmIN.DataManager().DataDB().GetKeysForPrefix(utils.AccountProfilePrefix) + if err != nil { + return err + } + for _, id := range ids { + tntID := strings.SplitN(strings.TrimPrefix(id, utils.AccountProfilePrefix), utils.InInFieldSep, 2) + if len(tntID) < 2 { + return fmt.Errorf("Invalid key <%s> when migrating from account profiles", id) + } + ap, err := m.dmIN.DataManager().GetAccountProfile(tntID[0], tntID[1], false, false, utils.NonTransactional) + if err != nil { + return err + } + if ap == nil || m.dryRun { + continue + } + if err := m.dmOut.DataManager().SetAccountProfile(ap, true); err != nil { + return err + } + if err := m.dmIN.DataManager().RemoveAccountProfile(tntID[0], tntID[1], utils.NonTransactional, false); err != nil { + return err + } + m.stats[utils.AccountProfilesString]++ + } + return +} + +func (m *Migrator) migrateAccountProfiles() (err error) { + var vrs engine.Versions + current := engine.CurrentDataDBVersions() + if vrs, err = m.getVersions(utils.AccountProfilesString); err != nil { + return + } + migrated := true + for { + version := vrs[utils.AccountProfilesString] + for { + switch version { + default: + return fmt.Errorf("Unsupported version %v", version) + case current[utils.AccountProfilesString]: + migrated = false + if m.sameDataDB { + break + } + if err = m.migrateCurrentAccountProfiles(); err != nil { + return + } + } + if version == current[utils.AccountProfilesString] || err == utils.ErrNoMoreData { + break + } + } + if err == utils.ErrNoMoreData || !migrated { + break + } + m.stats[utils.AccountProfilesString]++ + } + //All done, update version with current one + if err = m.setVersions(utils.AccountProfilesString); err != nil { + return + } + return m.ensureIndexesDataDB(engine.ColApp) +} diff --git a/migrator/migrator.go b/migrator/migrator.go index ad04b0f86..8dba6828d 100755 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -129,6 +129,8 @@ func (m *Migrator) Migrate(taskIDs []string) (err error, stats map[string]int) { err = m.migrateFilters() case utils.MetaRoutes: err = m.migrateRouteProfiles() + case utils.MetaAccountProfiles: + err = m.migrateAccountProfiles() //only Move case utils.MetaRatingPlans: err = m.migrateRatingPlans()