2023-03-16 20:05:39 +01:00
|
|
|
package limits
|
|
|
|
|
|
|
|
import (
|
2023-03-21 19:06:23 +01:00
|
|
|
"github.com/jmoiron/sqlx"
|
2023-03-27 21:29:25 +02:00
|
|
|
"github.com/openziti/zrok/controller/emailUi"
|
2023-03-16 20:05:39 +01:00
|
|
|
"github.com/openziti/zrok/controller/metrics"
|
|
|
|
"github.com/openziti/zrok/controller/store"
|
|
|
|
"github.com/openziti/zrok/controller/zrokEdgeSdk"
|
2024-05-23 20:08:14 +02:00
|
|
|
"github.com/openziti/zrok/sdk/golang/sdk"
|
2023-03-21 19:06:23 +01:00
|
|
|
"github.com/pkg/errors"
|
2023-03-16 20:05:39 +01:00
|
|
|
"github.com/sirupsen/logrus"
|
2023-03-28 20:39:42 +02:00
|
|
|
"reflect"
|
2023-03-17 18:13:33 +01:00
|
|
|
"time"
|
2023-03-16 20:05:39 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Agent struct {
|
2024-06-03 19:37:32 +02:00
|
|
|
cfg *Config
|
|
|
|
ifx *influxReader
|
|
|
|
zCfg *zrokEdgeSdk.Config
|
|
|
|
str *store.Store
|
|
|
|
queue chan *metrics.Usage
|
|
|
|
warningActions []AccountAction
|
|
|
|
limitActions []AccountAction
|
|
|
|
relaxActions []AccountAction
|
|
|
|
close chan struct{}
|
|
|
|
join chan struct{}
|
2023-03-16 20:05:39 +01:00
|
|
|
}
|
|
|
|
|
2023-03-27 21:29:25 +02:00
|
|
|
func NewAgent(cfg *Config, ifxCfg *metrics.InfluxConfig, zCfg *zrokEdgeSdk.Config, emailCfg *emailUi.Config, str *store.Store) (*Agent, error) {
|
2023-03-27 17:34:29 +02:00
|
|
|
a := &Agent{
|
2024-06-03 19:37:32 +02:00
|
|
|
cfg: cfg,
|
|
|
|
ifx: newInfluxReader(ifxCfg),
|
|
|
|
zCfg: zCfg,
|
|
|
|
str: str,
|
|
|
|
queue: make(chan *metrics.Usage, 1024),
|
2024-06-04 16:33:39 +02:00
|
|
|
warningActions: []AccountAction{newWarningAction(emailCfg, str)},
|
|
|
|
limitActions: []AccountAction{newLimitAction(str, zCfg)},
|
|
|
|
relaxActions: []AccountAction{newRelaxAction(str, zCfg)},
|
2024-06-03 19:37:32 +02:00
|
|
|
close: make(chan struct{}),
|
|
|
|
join: make(chan struct{}),
|
2023-03-27 17:34:29 +02:00
|
|
|
}
|
|
|
|
return a, nil
|
2023-03-16 20:05:39 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 18:13:33 +01:00
|
|
|
func (a *Agent) Start() {
|
|
|
|
go a.run()
|
2023-03-16 20:05:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Agent) Stop() {
|
2023-03-17 18:13:33 +01:00
|
|
|
close(a.close)
|
|
|
|
<-a.join
|
2023-03-16 20:05:39 +01:00
|
|
|
}
|
|
|
|
|
2023-03-21 21:34:45 +01:00
|
|
|
func (a *Agent) CanCreateEnvironment(acctId int, trx *sqlx.Tx) (bool, error) {
|
2023-03-29 19:29:12 +02:00
|
|
|
if a.cfg.Enforcing {
|
2024-05-15 21:48:42 +02:00
|
|
|
if err := a.str.LimitCheckLock(acctId, trx); err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2024-06-04 22:27:05 +02:00
|
|
|
|
2024-06-06 20:00:49 +02:00
|
|
|
ul, err := a.getUserLimits(acctId, trx)
|
2024-06-04 22:27:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2023-03-29 19:29:12 +02:00
|
|
|
|
2024-06-06 20:00:49 +02:00
|
|
|
if ul.resource.GetEnvironments() > store.Unlimited {
|
2023-03-29 19:29:12 +02:00
|
|
|
envs, err := a.str.FindEnvironmentsForAccount(acctId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if len(envs)+1 > a.cfg.Environments {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 21:34:45 +01:00
|
|
|
}
|
2024-06-04 22:43:38 +02:00
|
|
|
|
2023-03-21 21:34:45 +01:00
|
|
|
return true, nil
|
|
|
|
}
|
2023-03-21 21:18:17 +01:00
|
|
|
|
2024-06-07 16:21:26 +02:00
|
|
|
func (a *Agent) CanCreateShare(acctId, envId int, reserved, uniqueName bool, _ sdk.ShareMode, backendMode sdk.BackendMode, trx *sqlx.Tx) (bool, error) {
|
2023-03-29 19:29:12 +02:00
|
|
|
if a.cfg.Enforcing {
|
2024-05-15 21:48:42 +02:00
|
|
|
if err := a.str.LimitCheckLock(acctId, trx); err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2024-06-04 22:43:38 +02:00
|
|
|
|
2024-06-07 16:21:26 +02:00
|
|
|
ul, err := a.getUserLimits(acctId, trx)
|
2024-06-04 22:43:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2024-06-07 19:29:51 +02:00
|
|
|
if scopedBwc, found := ul.scopes[backendMode]; found {
|
|
|
|
latestScopedJe, err := a.isBandwidthClassLimitedForAccount(acctId, scopedBwc, trx)
|
2024-06-07 17:38:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
2024-06-04 22:43:38 +02:00
|
|
|
}
|
2024-06-07 19:29:51 +02:00
|
|
|
if latestScopedJe != nil {
|
2024-06-07 17:38:49 +02:00
|
|
|
return false, nil
|
2023-03-29 19:29:12 +02:00
|
|
|
}
|
2024-06-07 19:29:51 +02:00
|
|
|
} else {
|
|
|
|
for _, bwc := range ul.bandwidth {
|
|
|
|
latestJe, err := a.isBandwidthClassLimitedForAccount(acctId, bwc, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if latestJe != nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 21:18:17 +01:00
|
|
|
}
|
2023-03-29 19:29:12 +02:00
|
|
|
|
2024-06-07 21:05:18 +02:00
|
|
|
rc := ul.resource
|
|
|
|
if scopeRc, found := ul.scopes[backendMode]; found {
|
|
|
|
rc = scopeRc
|
|
|
|
}
|
|
|
|
if rc.GetShares() > store.Unlimited || (reserved && rc.GetReservedShares() > store.Unlimited) || (reserved && uniqueName && rc.GetUniqueNames() > store.Unlimited) {
|
2023-03-29 19:29:12 +02:00
|
|
|
envs, err := a.str.FindEnvironmentsForAccount(acctId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
total := 0
|
2024-05-22 22:21:52 +02:00
|
|
|
reserveds := 0
|
|
|
|
uniqueNames := 0
|
2023-03-29 19:29:12 +02:00
|
|
|
for i := range envs {
|
|
|
|
shrs, err := a.str.FindSharesForEnvironment(envs[i].Id, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, errors.Wrapf(err, "unable to find shares for environment '%v'", envs[i].ZId)
|
|
|
|
}
|
|
|
|
total += len(shrs)
|
2024-05-22 22:21:52 +02:00
|
|
|
for _, shr := range shrs {
|
|
|
|
if shr.Reserved {
|
|
|
|
reserveds++
|
|
|
|
}
|
|
|
|
if shr.UniqueName {
|
|
|
|
uniqueNames++
|
|
|
|
}
|
|
|
|
}
|
2024-06-07 21:05:18 +02:00
|
|
|
if total+1 > rc.GetShares() {
|
2024-05-23 17:50:55 +02:00
|
|
|
logrus.Debugf("account '%d', environment '%d' over shares limit '%d'", acctId, envId, a.cfg.Shares)
|
2023-03-29 19:29:12 +02:00
|
|
|
return false, nil
|
|
|
|
}
|
2024-06-07 21:05:18 +02:00
|
|
|
if reserved && reserveds+1 > rc.GetReservedShares() {
|
2024-05-23 17:50:55 +02:00
|
|
|
logrus.Debugf("account '%v', environment '%d' over reserved shares limit '%d'", acctId, envId, a.cfg.ReservedShares)
|
2024-05-22 22:21:52 +02:00
|
|
|
return false, nil
|
|
|
|
}
|
2024-06-07 21:05:18 +02:00
|
|
|
if reserved && uniqueName && uniqueNames+1 > rc.GetUniqueNames() {
|
2024-05-23 17:50:55 +02:00
|
|
|
logrus.Debugf("account '%v', environment '%d' over unique names limit '%d'", acctId, envId, a.cfg.UniqueNames)
|
2024-05-22 22:21:52 +02:00
|
|
|
return false, nil
|
|
|
|
}
|
2023-03-29 19:29:12 +02:00
|
|
|
logrus.Infof("total = %d", total)
|
|
|
|
}
|
2023-03-21 21:18:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2023-06-06 17:29:22 +02:00
|
|
|
func (a *Agent) CanAccessShare(shrId int, trx *sqlx.Tx) (bool, error) {
|
|
|
|
if a.cfg.Enforcing {
|
|
|
|
shr, err := a.str.GetShare(shrId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2024-06-04 22:50:08 +02:00
|
|
|
env, err := a.str.GetEnvironment(shr.EnvironmentId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if env.AccountId != nil {
|
2024-06-07 16:21:26 +02:00
|
|
|
ul, err := a.getUserLimits(*env.AccountId, trx)
|
2023-06-06 17:29:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2024-06-04 22:50:08 +02:00
|
|
|
|
2024-06-07 16:21:26 +02:00
|
|
|
if ul.resource.IsGlobal() {
|
2024-06-04 22:50:08 +02:00
|
|
|
if empty, err := a.str.IsBandwidthLimitJournalEmptyForGlobal(*env.AccountId, trx); err == nil && !empty {
|
|
|
|
lj, err := a.str.FindLatestBandwidthLimitJournalForGlobal(*env.AccountId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if lj.Action == store.LimitLimitAction {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2024-06-07 16:21:26 +02:00
|
|
|
if empty, err := a.str.IsBandwidthLimitJournalEmptyForLimitClass(*env.AccountId, ul.resource.GetLimitClassId(), trx); err == nil && !empty {
|
|
|
|
lj, err := a.str.FindLatestBandwidthLimitJournalForLimitClass(*env.AccountId, ul.resource.GetLimitClassId(), trx)
|
2024-06-04 22:50:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if lj.Action == store.LimitLimitAction {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false, nil
|
2023-06-06 17:29:22 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-06 16:54:57 +02:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2023-03-16 20:05:39 +01:00
|
|
|
func (a *Agent) Handle(u *metrics.Usage) error {
|
2023-03-21 18:05:22 +01:00
|
|
|
logrus.Debugf("handling: %v", u)
|
|
|
|
a.queue <- u
|
2023-03-16 20:05:39 +01:00
|
|
|
return nil
|
|
|
|
}
|
2023-03-17 18:13:33 +01:00
|
|
|
|
|
|
|
func (a *Agent) run() {
|
|
|
|
logrus.Info("started")
|
|
|
|
defer logrus.Info("stopped")
|
|
|
|
|
2023-03-29 23:03:42 +02:00
|
|
|
lastCycle := time.Now()
|
2023-03-17 18:13:33 +01:00
|
|
|
mainLoop:
|
|
|
|
for {
|
|
|
|
select {
|
2023-03-21 18:05:22 +01:00
|
|
|
case usage := <-a.queue:
|
2023-06-16 19:18:46 +02:00
|
|
|
if usage.ShareToken != "" {
|
|
|
|
if err := a.enforce(usage); err != nil {
|
|
|
|
logrus.Errorf("error running enforcement: %v", err)
|
|
|
|
}
|
|
|
|
if time.Since(lastCycle) > a.cfg.Cycle {
|
|
|
|
if err := a.relax(); err != nil {
|
|
|
|
logrus.Errorf("error running relax cycle: %v", err)
|
|
|
|
}
|
|
|
|
lastCycle = time.Now()
|
2023-03-29 23:03:42 +02:00
|
|
|
}
|
2023-06-16 19:18:46 +02:00
|
|
|
} else {
|
2023-06-16 19:28:00 +02:00
|
|
|
logrus.Warnf("not enforcing for usage with no share token: %v", usage.String())
|
2023-03-29 23:03:42 +02:00
|
|
|
}
|
2023-03-21 18:05:22 +01:00
|
|
|
|
2023-03-17 18:13:33 +01:00
|
|
|
case <-time.After(a.cfg.Cycle):
|
2023-03-23 20:13:59 +01:00
|
|
|
if err := a.relax(); err != nil {
|
|
|
|
logrus.Errorf("error running relax cycle: %v", err)
|
|
|
|
}
|
2023-03-29 23:03:42 +02:00
|
|
|
lastCycle = time.Now()
|
2023-03-17 18:13:33 +01:00
|
|
|
|
|
|
|
case <-a.close:
|
|
|
|
close(a.join)
|
|
|
|
break mainLoop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 18:05:22 +01:00
|
|
|
|
2023-03-21 19:06:23 +01:00
|
|
|
func (a *Agent) enforce(u *metrics.Usage) error {
|
|
|
|
trx, err := a.str.Begin()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error starting transaction")
|
|
|
|
}
|
|
|
|
defer func() { _ = trx.Rollback() }()
|
|
|
|
|
2023-06-07 17:00:42 +02:00
|
|
|
acct, err := a.str.GetAccount(int(u.AccountId), trx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if acct.Limitless {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-05 23:03:24 +02:00
|
|
|
shr, err := a.str.FindShareWithTokenEvenIfDeleted(u.ShareToken, trx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
|
2024-06-05 22:54:05 +02:00
|
|
|
ul, err := a.getUserLimits(int(u.AccountId), trx)
|
2024-06-04 20:06:44 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-05 22:54:05 +02:00
|
|
|
|
2024-06-07 17:38:49 +02:00
|
|
|
exceededBwc, rxBytes, txBytes, err := a.anyBandwidthLimitExceeded(u, ul.toBandwidthArray(sdk.BackendMode(shr.BackendMode)))
|
2024-06-04 20:06:44 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error checking limit classes")
|
|
|
|
}
|
|
|
|
|
2024-06-07 17:38:49 +02:00
|
|
|
if exceededBwc != nil {
|
|
|
|
latestJe, err := a.isBandwidthClassLimitedForAccount(int(u.AccountId), exceededBwc, trx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2024-06-04 20:06:44 +02:00
|
|
|
}
|
2024-06-07 17:38:49 +02:00
|
|
|
if latestJe == nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
je := &store.BandwidthLimitJournalEntry{
|
|
|
|
AccountId: int(u.AccountId),
|
|
|
|
RxBytes: rxBytes,
|
|
|
|
TxBytes: txBytes,
|
2024-06-07 17:38:49 +02:00
|
|
|
Action: exceededBwc.GetLimitAction(),
|
2023-03-22 19:10:07 +01:00
|
|
|
}
|
2024-06-07 17:38:49 +02:00
|
|
|
if !exceededBwc.IsGlobal() {
|
|
|
|
lcId := exceededBwc.GetLimitClassId()
|
2024-06-04 20:06:44 +02:00
|
|
|
je.LimitClassId = &lcId
|
|
|
|
}
|
2024-06-06 03:11:23 +02:00
|
|
|
if _, err := a.str.CreateBandwidthLimitJournalEntry(je, trx); err != nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
acct, err := a.str.GetAccount(int(u.AccountId), trx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-07 17:38:49 +02:00
|
|
|
switch exceededBwc.GetLimitAction() {
|
2024-06-04 20:06:44 +02:00
|
|
|
case store.LimitLimitAction:
|
|
|
|
for _, limitAction := range a.limitActions {
|
2024-06-07 17:38:49 +02:00
|
|
|
if err := limitAction.HandleAccount(acct, rxBytes, txBytes, exceededBwc, ul, trx); err != nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
return errors.Wrapf(err, "%v", reflect.TypeOf(limitAction).String())
|
2023-03-27 19:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
|
|
|
|
case store.WarningLimitAction:
|
|
|
|
for _, warningAction := range a.warningActions {
|
2024-06-07 17:38:49 +02:00
|
|
|
if err := warningAction.HandleAccount(acct, rxBytes, txBytes, exceededBwc, ul, trx); err != nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
return errors.Wrapf(err, "%v", reflect.TypeOf(warningAction).String())
|
|
|
|
}
|
2023-03-22 19:10:07 +01:00
|
|
|
}
|
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
if err := trx.Commit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2024-06-07 17:38:49 +02:00
|
|
|
logrus.Debugf("limit '%v' already applied for '%v' (at: %v)", exceededBwc, acct.Email, latestJe.CreatedAt)
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 20:13:59 +01:00
|
|
|
func (a *Agent) relax() error {
|
2023-03-28 21:19:01 +02:00
|
|
|
logrus.Debug("relaxing")
|
2023-03-23 20:13:59 +01:00
|
|
|
|
|
|
|
trx, err := a.str.Begin()
|
2023-03-21 19:06:23 +01:00
|
|
|
if err != nil {
|
2023-03-23 20:13:59 +01:00
|
|
|
return errors.Wrap(err, "error starting transaction")
|
|
|
|
}
|
|
|
|
defer func() { _ = trx.Rollback() }()
|
|
|
|
|
|
|
|
commit := false
|
|
|
|
|
2024-06-04 20:06:44 +02:00
|
|
|
if bwjes, err := a.str.FindAllBandwidthLimitJournal(trx); err == nil {
|
|
|
|
periodBw := make(map[int]struct {
|
|
|
|
rx int64
|
|
|
|
tx int64
|
|
|
|
})
|
|
|
|
|
|
|
|
accounts := make(map[int]*store.Account)
|
2024-06-06 19:49:36 +02:00
|
|
|
uls := make(map[int]*userLimits)
|
2024-06-04 20:06:44 +02:00
|
|
|
|
|
|
|
for _, bwje := range bwjes {
|
|
|
|
if _, found := accounts[bwje.AccountId]; !found {
|
|
|
|
if acct, err := a.str.GetAccount(bwje.AccountId, trx); err == nil {
|
|
|
|
accounts[bwje.AccountId] = acct
|
2024-06-06 19:49:36 +02:00
|
|
|
ul, err := a.getUserLimits(acct.Id, trx)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "error getting user limits for '%v'", acct.Email)
|
|
|
|
}
|
|
|
|
uls[bwje.AccountId] = ul
|
|
|
|
|
2024-06-04 20:06:44 +02:00
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var bwc store.BandwidthClass
|
2024-06-05 17:45:14 +02:00
|
|
|
if bwje.LimitClassId == nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
globalBwcs := newConfigBandwidthClasses(a.cfg.Bandwidth)
|
|
|
|
if bwje.Action == store.WarningLimitAction {
|
|
|
|
bwc = globalBwcs[0]
|
|
|
|
} else {
|
|
|
|
bwc = globalBwcs[1]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lc, err := a.str.GetLimitClass(*bwje.LimitClassId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bwc = lc
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, found := periodBw[bwc.GetPeriodMinutes()]; !found {
|
|
|
|
rx, tx, err := a.ifx.totalRxTxForAccount(int64(bwje.AccountId), time.Duration(bwc.GetPeriodMinutes())*time.Minute)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
periodBw[bwc.GetPeriodMinutes()] = struct {
|
|
|
|
rx int64
|
|
|
|
tx int64
|
|
|
|
}{
|
|
|
|
rx: rx,
|
|
|
|
tx: tx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
used := periodBw[bwc.GetPeriodMinutes()]
|
2024-06-07 17:38:49 +02:00
|
|
|
if !a.transferBytesExceeded(used.rx, used.tx, bwc) {
|
2024-06-04 20:06:44 +02:00
|
|
|
if bwc.GetLimitAction() == store.LimitLimitAction {
|
2024-06-05 17:45:14 +02:00
|
|
|
logrus.Infof("relaxing limit '%v' for '%v'", bwc.String(), accounts[bwje.AccountId].Email)
|
2024-06-04 20:06:44 +02:00
|
|
|
for _, action := range a.relaxActions {
|
2024-06-06 19:49:36 +02:00
|
|
|
if err := action.HandleAccount(accounts[bwje.AccountId], used.rx, used.tx, bwc, uls[bwje.AccountId], trx); err != nil {
|
2024-06-04 20:06:44 +02:00
|
|
|
return errors.Wrapf(err, "%v", reflect.TypeOf(action).String())
|
2023-03-23 20:13:59 +01:00
|
|
|
}
|
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
} else {
|
2024-06-05 17:45:14 +02:00
|
|
|
logrus.Infof("relaxing warning '%v' for '%v'", bwc.String(), accounts[bwje.AccountId].Email)
|
2024-06-04 20:06:44 +02:00
|
|
|
}
|
2024-06-05 17:45:14 +02:00
|
|
|
if bwc.IsGlobal() {
|
|
|
|
if err := a.str.DeleteBandwidthLimitJournalEntryForGlobal(bwje.AccountId, trx); err == nil {
|
|
|
|
commit = true
|
|
|
|
} else {
|
|
|
|
logrus.Errorf("error deleting global bandwidth limit journal entry for '%v': %v", accounts[bwje.AccountId].Email, err)
|
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
} else {
|
2024-06-05 17:45:14 +02:00
|
|
|
if err := a.str.DeleteBandwidthLimitJournalEntryForLimitClass(bwje.AccountId, *bwje.LimitClassId, trx); err == nil {
|
|
|
|
commit = true
|
|
|
|
} else {
|
|
|
|
logrus.Errorf("error deleting bandwidth limit journal entry for '%v': %v", accounts[bwje.AccountId].Email, err)
|
|
|
|
}
|
2023-03-23 20:13:59 +01:00
|
|
|
}
|
|
|
|
} else {
|
2024-06-06 19:49:36 +02:00
|
|
|
logrus.Infof("account '%v' still over limit: '%v' with rx: %d, tx: %d, total: %d", accounts[bwje.AccountId].Email, bwc, used.rx, used.tx, used.rx+used.tx)
|
2023-03-23 20:13:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return err
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
|
|
|
|
2023-03-23 20:13:59 +01:00
|
|
|
if commit {
|
|
|
|
if err := trx.Commit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-07 17:38:49 +02:00
|
|
|
func (a *Agent) isBandwidthClassLimitedForAccount(acctId int, bwc store.BandwidthClass, trx *sqlx.Tx) (*store.BandwidthLimitJournalEntry, error) {
|
|
|
|
if bwc.IsGlobal() {
|
|
|
|
if empty, err := a.str.IsBandwidthLimitJournalEmptyForGlobal(acctId, trx); err == nil && !empty {
|
|
|
|
je, err := a.str.FindLatestBandwidthLimitJournalForGlobal(acctId, trx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if je.Action == store.LimitLimitAction {
|
|
|
|
logrus.Infof("account '#%d' over bandwidth for global bandwidth class '%v'", acctId, bwc)
|
|
|
|
return je, nil
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if empty, err := a.str.IsBandwidthLimitJournalEmptyForLimitClass(acctId, bwc.GetLimitClassId(), trx); err == nil && !empty {
|
|
|
|
je, err := a.str.FindLatestBandwidthLimitJournalForLimitClass(acctId, bwc.GetLimitClassId(), trx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if je.Action == store.LimitLimitAction {
|
|
|
|
logrus.Infof("account '#%d' over bandwidth for limit class '%v'", acctId, bwc)
|
|
|
|
return je, nil
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Agent) anyBandwidthLimitExceeded(u *metrics.Usage, bwcs []store.BandwidthClass) (store.BandwidthClass, int64, int64, error) {
|
2024-06-04 20:06:44 +02:00
|
|
|
periodBw := make(map[int]struct {
|
|
|
|
rx int64
|
|
|
|
tx int64
|
|
|
|
})
|
|
|
|
|
2024-06-05 22:54:05 +02:00
|
|
|
var selectedLc store.BandwidthClass
|
|
|
|
var rxBytes int64
|
|
|
|
var txBytes int64
|
2024-06-04 20:06:44 +02:00
|
|
|
|
2024-06-05 22:54:05 +02:00
|
|
|
for _, bwc := range bwcs {
|
2024-06-04 20:06:44 +02:00
|
|
|
if _, found := periodBw[bwc.GetPeriodMinutes()]; !found {
|
|
|
|
rx, tx, err := a.ifx.totalRxTxForAccount(u.AccountId, time.Minute*time.Duration(bwc.GetPeriodMinutes()))
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, 0, errors.Wrapf(err, "error getting rx/tx for account '%d'", u.AccountId)
|
|
|
|
}
|
|
|
|
periodBw[bwc.GetPeriodMinutes()] = struct {
|
|
|
|
rx int64
|
|
|
|
tx int64
|
|
|
|
}{
|
|
|
|
rx: rx,
|
|
|
|
tx: tx,
|
|
|
|
}
|
|
|
|
}
|
2024-06-05 22:54:05 +02:00
|
|
|
period := periodBw[bwc.GetPeriodMinutes()]
|
2024-06-04 20:06:44 +02:00
|
|
|
|
2024-06-07 17:38:49 +02:00
|
|
|
if a.transferBytesExceeded(period.rx, period.tx, bwc) {
|
2024-06-05 22:54:05 +02:00
|
|
|
selectedLc = bwc
|
|
|
|
rxBytes = period.rx
|
|
|
|
txBytes = period.tx
|
|
|
|
} else {
|
|
|
|
logrus.Debugf("limit ok '%v' with rx: %d, tx: %d, total: %d", bwc, period.rx, period.tx, period.rx+period.tx)
|
2024-06-04 20:06:44 +02:00
|
|
|
}
|
2023-03-21 18:05:22 +01:00
|
|
|
}
|
2023-03-21 19:06:23 +01:00
|
|
|
|
2024-06-05 22:54:05 +02:00
|
|
|
if selectedLc != nil {
|
|
|
|
logrus.Infof("exceeded limit '%v' with rx: %d, tx: %d, total: %d", selectedLc, rxBytes, txBytes, rxBytes+txBytes)
|
|
|
|
}
|
|
|
|
|
2024-06-04 20:06:44 +02:00
|
|
|
return selectedLc, rxBytes, txBytes, nil
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
|
|
|
|
2024-06-07 17:38:49 +02:00
|
|
|
func (a *Agent) transferBytesExceeded(rx, tx int64, bwc store.BandwidthClass) bool {
|
2024-06-05 21:20:33 +02:00
|
|
|
if bwc.GetTxBytes() != store.Unlimited && tx >= bwc.GetTxBytes() {
|
2024-06-04 20:06:44 +02:00
|
|
|
return true
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
2024-06-05 21:20:33 +02:00
|
|
|
if bwc.GetRxBytes() != store.Unlimited && rx >= bwc.GetRxBytes() {
|
2024-06-04 20:06:44 +02:00
|
|
|
return true
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
2024-06-05 21:20:33 +02:00
|
|
|
if bwc.GetTotalBytes() != store.Unlimited && tx+rx >= bwc.GetTotalBytes() {
|
2024-06-04 20:06:44 +02:00
|
|
|
return true
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|
2024-06-04 20:06:44 +02:00
|
|
|
return false
|
2023-03-21 19:06:23 +01:00
|
|
|
}
|