| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "database/sql"
- "fmt"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
- "yunion.io/x/onecloud/pkg/cloudcommon/validators"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/logclient"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- // +onecloud:swagger-gen-model-singular=scalinggroup
- // +onecloud:swagger-gen-model-plural=scalinggroups
- type SScalingGroupManager struct {
- db.SVirtualResourceBaseManager
- SCloudregionResourceBaseManager
- SVpcResourceBaseManager
- SLoadbalancerBackendgroupResourceBaseManager
- SGroupResourceBaseManager
- SGuestTemplateResourceBaseManager
- SGuestResourceBaseManager
- db.SEnabledResourceBaseManager
- }
- type SScalingGroup struct {
- db.SVirtualResourceBase
- SCloudregionResourceBase
- SVpcResourceBase
- SLoadbalancerBackendgroupResourceBase
- // GuestGroupId represent the guest gropu related to this scaling group.
- // Every scaling group will have only one guest group related to itself.
- SGroupResourceBase
- SGuestTemplateResourceBase
- db.SEnabledResourceBase
- Hypervisor string `width:"16" charset:"ascii" default:"kvm" create:"required" list:"user" get:"user" update:"user"`
- MinInstanceNumber int `nullable:"false" default:"0" create:"required" list:"user" get:"user" update:"user"`
- MaxInstanceNumber int `nullable:"false" default:"10" create:"required" list:"user" get:"user" update:"user"`
- // DesireInstanceNumber represent the number of instances that should exist in the scaling group.
- // Scaling controller will monitor and ensure this in real time.
- // Scaling activities triggered by various policies will also modify this value.
- // This value should between MinInstanceNumber and MaxInstanceNumber
- DesireInstanceNumber int `nullable:"false" default:"0" create:"required" list:"user" get:"user" update:"user"`
- // ExpansionPrinciple represent the principle when creating new instance to join in.
- ExpansionPrinciple string `width:"32" charset:"ascii" default:"balanced" create:"optional" list:"user" update:"user" get:"user"`
- // ShrinkPrinciple represent the principle when removing instance from scaling group.
- ShrinkPrinciple string `width:"32" charset:"ascii" default:"earliest" create:"optional" list:"user" update:"user" get:"user"`
- HealthCheckMode string `width:"32" charset:"ascii" default:"normal" create:"optional" list:"user" update:"user" get:"user"`
- HealthCheckCycle int `nullable:"false" default:"300" create:"optional" list:"user" update:"user" get:"user"`
- HealthCheckGov int `nullable:"false" default:"180" create:"optional" list:"user" update:"user" get:"user"`
- LoadbalancerBackendPort int `nullable:"false" default:"80" create:"optional" list:"user" get:"user"`
- LoadbalancerBackendWeight int `nillable:"false" default:"1" create:"optional" list:"user" get:"user"`
- // Time to allow scale
- AllowScaleTime time.Time
- // NextCheckTime descripe the next time to check instance's health
- NextCheckTime time.Time
- }
- var ScalingGroupManager *SScalingGroupManager
- func init() {
- ScalingGroupManager = &SScalingGroupManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SScalingGroup{},
- "scalinggroups_tbl",
- "scalinggroup",
- "scalinggroups",
- ),
- }
- ScalingGroupManager.SetVirtualObject(ScalingGroupManager)
- }
- func (sgm *SScalingGroupManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ScalingGroupCreateInput) (api.ScalingGroupCreateInput,
- error) {
- var err error
- input.VirtualResourceCreateInput, err = sgm.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred,
- ownerId, query, input.VirtualResourceCreateInput)
- // check InstanceNumber
- if input.MinInstanceNumber < 0 {
- return input, httperrors.NewInputParameterError("min_instance_number should not be smaller than 0")
- }
- if input.MinInstanceNumber > input.MaxInstanceNumber {
- return input, httperrors.NewInputParameterError(
- "min_instance_number should not be bigger than max_instance_number")
- }
- if input.DesireInstanceNumber < input.MinInstanceNumber || input.DesireInstanceNumber > input.MaxInstanceNumber {
- return input, httperrors.NewInputParameterError(
- "desire_instance_number should between min_instance_number and max_instance_number")
- }
- // check cloudregion
- idOrName := input.Cloudregion
- if len(input.CloudregionId) != 0 {
- idOrName = input.CloudregionId
- }
- cloudregion, err := CloudregionManager.FetchByIdOrName(ctx, userCred, idOrName)
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewInputParameterError("no such cloud region %s", idOrName)
- }
- if err != nil {
- return input, errors.Wrap(err, "CloudregionManager.FetchByIdOrName")
- }
- input.CloudregionId = cloudregion.GetId()
- // check vpc
- _, err = validators.ValidateModel(ctx, userCred, VpcManager, &input.VpcId)
- if err != nil {
- return input, err
- }
- // check networks
- if len(input.Networks) == 0 {
- return input, httperrors.NewInputParameterError("ScalingGroup should have some networks")
- }
- networks := make([]SNetwork, 0, len(input.Networks))
- q := NetworkManager.Query()
- if len(input.Networks) == 1 {
- q = q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("id"), input.Networks[0]), sqlchemy.Equals(q.Field("name"), input.Networks[0])))
- } else {
- q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("id"), input.Networks), sqlchemy.In(q.Field("name"), input.Networks)))
- }
- err = db.FetchModelObjects(NetworkManager, q, &networks)
- if err != nil {
- return input, errors.Wrap(err, "db.FetchModelObjects")
- }
- if len(networks) != len(input.Networks) {
- return input, httperrors.NewInputParameterError("some networks not exist")
- }
- // check networks in vpc
- for i := range networks {
- vpc, _ := networks[i].GetVpc()
- if vpc == nil {
- return input, fmt.Errorf("Get vpc of network '%s' failed", networks[i].Id)
- }
- if vpc.Id != input.VpcId {
- return input, httperrors.NewInputParameterError("network '%s' not in vpc '%s'", networks[i].Id, input.VpcId)
- }
- input.Networks[i] = networks[i].Id
- }
- // check Guest Template
- idOrName = input.GuestTemplate
- if len(input.GuestTemplateId) != 0 {
- idOrName = input.GuestTemplateId
- }
- guestTemplate, err := GuestTemplateManager.FetchByIdOrName(ctx, userCred, idOrName)
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewInputParameterError("no such guest template %s", idOrName)
- }
- if err != nil {
- return input, errors.Wrap(err, "GuestTempalteManager.FetchByIdOrName")
- }
- if ok, reason := guestTemplate.(*SGuestTemplate).Validate(ctx, userCred, ownerId,
- SGuestTemplateValidate{input.Hypervisor, input.CloudregionId, input.VpcId, input.Networks}); !ok {
- return input, httperrors.NewInputParameterError("the guest template %s is not valid in cloudregion %s, "+
- "reason: %s", idOrName, input.CloudregionId, reason)
- }
- input.GuestTemplateId = guestTemplate.GetId()
- // check Expansion Principle
- if !utils.IsInStringArray(input.ExpansionPrinciple, []string{api.EXPANSION_BALANCED, ""}) {
- return input, httperrors.NewInputParameterError("unkown expansion principle %s", input.ExpansionPrinciple)
- }
- // check Shrink Principle
- if !utils.IsInStringArray(input.ShrinkPrinciple, []string{api.SHRINK_EARLIEST_CREATION_FIRST, api.SHRINK_LATEST_CREATION_FIRST,
- api.SHRINK_CONFIG_EARLIEST_CREATION_FIRST, api.SHRINK_CONFIG_LATEST_CREATION_FIRST, ""}) {
- return input, httperrors.NewInputParameterError("unkown shrink principle %s", input.ShrinkPrinciple)
- }
- // check health check mod
- if !utils.IsInStringArray(input.HealthCheckMode, []string{api.HEALTH_CHECK_MODE_LOADBALANCER, api.HEALTH_CHECK_MODE_NORMAL, ""}) {
- return input, httperrors.NewInputParameterError("unkown health check mode %s", input.HealthCheckMode)
- }
- // check lb
- if len(input.LbBackendGroup) != 0 {
- idOrName = input.LbBackendGroup
- lb, err := LoadbalancerBackendGroupManager.FetchByIdOrName(ctx, userCred, idOrName)
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewInputParameterError("no such loadbalancer backend group '%s'", idOrName)
- }
- if err != nil {
- return input, errors.Wrap(err, "LoadbalancerBackendGroupManager.FetchByIdOrName")
- }
- input.BackendGroupId = lb.GetId()
- // check lbeg port
- if input.LoadbalancerBackendPort < 1 || input.LoadbalancerBackendPort > 65535 {
- return input, httperrors.NewInputParameterError("invalid loadbalancer backend port '%d'", input.LoadbalancerBackendPort)
- }
- // check lbeg weight
- if input.LoadbalancerBackendWeight < 1 {
- return input, httperrors.NewInputParameterError("invalid loadbalancer backend weight '%d'", input.LoadbalancerBackendWeight)
- }
- }
- return input, nil
- }
- func (sg *SScalingGroup) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
- // check enabled
- if sg.Enabled.IsTrue() {
- return httperrors.NewForbiddenError("Please disable this ScalingGroup firstly")
- }
- count, err := sg.GuestNumber()
- if err != nil {
- return errors.Wrap(err, "ScalingGroup.GuestNumber error")
- }
- if count != 0 {
- return httperrors.NewForbiddenError("There are some guests in this ScalingGroup, please delete them firstly")
- }
- return nil
- }
- func (sg *SScalingGroup) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- log.Infof("SScaling Group delete do nothing")
- return nil
- }
- func (sg *SScalingGroup) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- err := db.DeleteModel(ctx, userCred, sg)
- if err != nil {
- return errors.Wrap(err, "db.DeleteModel")
- }
- sg.SetStatus(ctx, userCred, api.SG_STATUS_DELETED, "")
- return nil
- }
- func (sg *SScalingGroup) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return sg.StartScalingGroupDeleteTask(ctx, userCred)
- }
- func (sg *SScalingGroup) StartScalingGroupDeleteTask(ctx context.Context, userCred mcclient.TokenCredential) error {
- task, err := taskman.TaskManager.NewTask(ctx, "ScalingGroupDeleteTask", sg, userCred, nil, "", "", nil)
- if err != nil {
- return errors.Wrap(err, "Start ScalingGroupDeleteTask failed")
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (sg *SScalingGroup) StartScalingGroupCreateTask(ctx context.Context, userCred mcclient.TokenCredential) error {
- task, err := taskman.TaskManager.NewTask(ctx, "ScalingGroupCreateTask", sg, userCred, nil, "", "", nil)
- if err != nil {
- return errors.Wrap(err, "Start ScalingGroupCreateTask failed")
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (sg *SScalingGroup) ScalingPolicies() ([]SScalingPolicy, error) {
- ret := make([]SScalingPolicy, 0)
- q := ScalingPolicyManager.Query().Equals("scaling_group_id", sg.Id)
- err := db.FetchModelObjects(ScalingPolicyManager, q, &ret)
- if err != nil {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return ret, nil
- }
- func (sgm *SScalingGroupManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential, input api.ScalingGroupListInput) (*sqlchemy.SQuery, error) {
- // hack
- // vpc and backendgroup may be empty, and these subresoruce shouldn't be fiter by brand
- brand := input.Brand
- input.Brand = ""
- q, err := sgm.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, input.RegionalFilterListInput)
- if err != nil {
- return q, err
- }
- q, err = sgm.SVpcResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VpcFilterListInput)
- if err != nil {
- return q, err
- }
- q, err = sgm.SLoadbalancerBackendgroupResourceBaseManager.ListItemFilter(ctx, q, userCred, input.LoadbalancerBackendGroupFilterListInput)
- if err != nil {
- return q, err
- }
- q, err = sgm.SGroupResourceBaseManager.ListItemFilter(ctx, q, userCred, input.GroupFilterListInput)
- if err != nil {
- return q, err
- }
- q, err = sgm.SGuestTemplateResourceBaseManager.ListItemFilter(ctx, q, userCred, input.GuestTemplateFilterListInput)
- if err != nil {
- return q, err
- }
- q, err = sgm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
- if err != nil {
- return q, err
- }
- if len(input.Hypervisor) > 0 {
- q = q.Equals("hypervisor", input.Hypervisor)
- }
- if len(brand) > 0 {
- q = q.Equals("hypervisor", Brand2Hypervisor(input.Brand))
- }
- input.Brand = brand
- return q, nil
- }
- func (sgm *SScalingGroupManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.ScalingGroupDetails {
- virtRows := sgm.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- crRows := sgm.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- vpcRows := sgm.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- lbbgRows := sgm.SLoadbalancerBackendgroupResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- groupRows := sgm.SGroupResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- gtRows := sgm.SGuestTemplateResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- rows := make([]api.ScalingGroupDetails, len(objs))
- for i := range rows {
- rows[i] = api.ScalingGroupDetails{
- VirtualResourceDetails: virtRows[i],
- CloudregionResourceInfo: crRows[i],
- LoadbalancerBackendGroupResourceInfo: lbbgRows[i],
- VpcResourceInfo: vpcRows[i],
- GroupResourceInfo: groupRows[i],
- GuestTemplateResourceInfo: gtRows[i],
- }
- sg := objs[i].(*SScalingGroup)
- n, _ := sg.GuestNumber()
- rows[i].InstanceNumber = n
- n, _ = sg.ScalingPolicyNumber()
- rows[i].ScalingPolicyNumber = n
- rows[i].Brand = Hypervisor2Brand(sg.Hypervisor)
- nets, err := sg.Networks()
- if err != nil {
- log.Errorf("sg.Networks error: %s", err)
- continue
- }
- sgNets := make([]api.ScalingGroupNetwork, 0, len(nets))
- for i := range nets {
- sgNets = append(sgNets, api.ScalingGroupNetwork{
- Id: nets[i].GetId(),
- Name: nets[i].GetName(),
- GuestIpStart: nets[i].GuestIpStart,
- GuestIpEnd: nets[i].GuestIpEnd,
- })
- }
- rows[i].Networks = sgNets
- }
- return rows
- }
- func (sg *SScalingGroup) GuestNumber() (int, error) {
- q := GuestManager.Query().In("id", ScalingGroupGuestManager.Query("guest_id").Equals("scaling_group_id",
- sg.Id).SubQuery()).IsFalse("pending_deleted")
- return q.CountWithError()
- }
- func (sg *SScalingGroup) ScalingPolicyNumber() (int, error) {
- q := ScalingPolicyManager.Query().Equals("scaling_group_id", sg.Id)
- return q.CountWithError()
- }
- /*func (sg *SScalingGroup) RemoveAllGuests(ctx context.Context, userCred mcclient.TokenCredential) error {
- q := ScalingGroupGuestManager.Query().Equals("scaling_group_id", sg.Id)
- scalingGroupGuests := make([]SScalingGroupGuest, 0)
- err := db.FetchModelObjects(ScalingGroupGuestManager, q, &scalingGroupGuests)
- if err != nil {
- return errors.Wrap(err, "db.FetchModelObjects")
- }
- for i := range scalingGroupGuests {
- err := scalingGroupGuests[i].Delete(ctx, userCred)
- return errors.Wrapf(err, "delete ScalingGroupGuests(ScalingGroupId: '%s', GuestId: '%s') failed",
- scalingGroupGuests[i].ScalingGroupId, scalingGroupGuests[i].GuestId)
- }
- return nil
- }*/
- func (sg *SScalingGroup) ScalingGroupGuests(guestIds []string) ([]SScalingGroupGuest, error) {
- q := ScalingGroupGuestManager.Query().Equals("scaling_group_id", sg.GetId())
- if len(guestIds) == 0 {
- return nil, nil
- }
- if len(guestIds) == 1 {
- q = q.Equals("guest_id", guestIds[0])
- }
- if len(guestIds) > 1 {
- q = q.In("guest_id", guestIds)
- }
- sggs := make([]SScalingGroupGuest, 0, len(guestIds))
- err := db.FetchModelObjects(ScalingGroupGuestManager, q, &sggs)
- return sggs, err
- }
- func (sg *SScalingGroup) Guests() ([]SGuest, error) {
- q := GuestManager.Query().In("id", ScalingGroupGuestManager.Query("guest_id").Equals("scaling_group_id", sg.Id).SubQuery())
- guests := make([]SGuest, 0, 1)
- err := db.FetchModelObjects(GuestManager, q, &guests)
- if err != nil {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return guests, nil
- }
- type sExecResult struct {
- // 0: success; 1: part success; 2: reject; 3: fail
- code uint8
- actionStr string
- reason string
- intanceNum int
- }
- func (sg *SScalingGroup) exec(ctx context.Context, action IScalingAction) (ret sExecResult) {
- ret.code = 3
- ret.intanceNum = -1
- // query again to fetch the latest desire instance number of sg
- model, err := ScalingGroupManager.FetchById(sg.Id)
- if err != nil {
- ret.reason = fmt.Sprintf("fail to get ScalingGroup: %s", err.Error())
- return
- }
- sg = model.(*SScalingGroup)
- targetNum := action.Exec(sg.DesireInstanceNumber)
- // targetNum must between sg.MinInstanceNumber and sg.MaxInstanceNumber
- ret.code = 0
- if targetNum > sg.MaxInstanceNumber {
- if sg.DesireInstanceNumber == sg.MaxInstanceNumber {
- ret.code = 2
- ret.reason = fmt.Sprintf(
- `Want to change the Desired Instance Number from "%d" to "%d", but the Desired Instance Number has reached the Max Instance Number`,
- sg.DesireInstanceNumber, targetNum,
- )
- return
- }
- ret.code = 1
- ret.reason = fmt.Sprintf(
- `Want to change the Desired Instance Number from "%d" to "%d", but "%d" is greater than the Max Instance Number "%d"`,
- sg.DesireInstanceNumber, targetNum, targetNum, sg.MaxInstanceNumber,
- )
- targetNum = sg.MaxInstanceNumber
- } else if targetNum < sg.MinInstanceNumber {
- if sg.DesireInstanceNumber == sg.MinInstanceNumber {
- ret.code = 2
- ret.reason = fmt.Sprintf(
- `Want to change the Desired Instance Number from "%d" to "%d", but the Desired Instance Number has reached the Min Instance Number`,
- sg.DesireInstanceNumber, targetNum,
- )
- return
- }
- ret.code = 1
- ret.reason = fmt.Sprintf(
- `Want to change the Desired Instance Number from "%d" to "%d", but "%d" is less than the Min Instance Number "%d"`,
- sg.DesireInstanceNumber, targetNum, targetNum, sg.MinInstanceNumber,
- )
- targetNum = sg.MinInstanceNumber
- }
- ret.actionStr = fmt.Sprintf(`Change the Desired Instance Number from "%d" to "%d"`, sg.DesireInstanceNumber, targetNum)
- _, err = db.Update(sg, func() error {
- sg.DesireInstanceNumber = targetNum
- return nil
- })
- if err != nil {
- ret.code = 3
- ret.reason = fmt.Sprintf("Update ScalingGroup's DesireInstanceNumber failed: %s", err.Error())
- }
- return
- }
- var CoolingTimeLocation, _ = time.LoadLocation("Asia/Shanghai")
- // Scale will modify SScalingGroup.DesireInstanceNumber and generate SScalingActivity based on the trigger and its
- // corresponding SScalingPolicy.
- func (sg *SScalingGroup) Scale(ctx context.Context, triggerDesc IScalingTriggerDesc, action IScalingAction,
- coolingTime int) error {
- lockman.LockObject(ctx, sg)
- defer lockman.ReleaseObject(ctx, sg)
- isExec := false
- defer func() {
- if isExec && coolingTime > 0 {
- sg.SetAllowScaleTime(time.Now().Add(time.Duration(coolingTime) * time.Second))
- }
- }()
- if sg.Enabled.IsFalse() {
- return nil
- }
- scalingActivity, err := ScalingActivityManager.CreateScalingActivity(ctx, sg.Id, triggerDesc.TriggerDescription(), api.SA_STATUS_EXEC)
- if err != nil {
- return errors.Wrapf(err, "create ScalingActivity whose ScalingGroup is %s error", sg.Id)
- }
- if action.CheckCoolTime() && !sg.AllowScale() {
- err = scalingActivity.SetReject("",
- fmt.Sprintf("The Cooling Time limit the execution time of the policy to at least: %s",
- sg.AllowScaleTime.In(CoolingTimeLocation).Format("2006-01-02 15:04:05 -0700")))
- return nil
- }
- ret := sg.exec(ctx, action)
- switch ret.code {
- case 0:
- err = scalingActivity.SetResult(ret.actionStr, api.SA_STATUS_SUCCEED, "", ret.intanceNum)
- isExec = true
- case 1:
- err = scalingActivity.SetResult(ret.actionStr, api.SA_STATUS_PART_SUCCEED, ret.reason, ret.intanceNum)
- isExec = true
- case 2:
- err = scalingActivity.SetReject("", ret.reason)
- case 3:
- err = scalingActivity.SetFailed(ret.actionStr, ret.reason)
- }
- if err != nil {
- log.Errorf("ScalingActivity set result failed: %s", err.Error())
- }
- return nil
- }
- func (sgm *SScalingGroupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- q, err := sgm.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = sgm.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = sgm.SVpcResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = sgm.SLoadbalancerBackendgroupResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = sgm.SGroupResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = sgm.SGuestTemplateResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- scalinggroupGuests := ScalingGroupGuestManager.Query("scaling_group_id", "guest_id").SubQuery()
- q = q.LeftJoin(scalinggroupGuests, sqlchemy.Equals(q.Field("id"), scalinggroupGuests.Field("scaling_group_id")))
- q, err = sgm.SGuestResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SScalingGroupManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential, query api.ScalingGroupListInput) (*sqlchemy.SQuery, error) {
- q, err := manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, err
- }
- q, err = manager.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VpcFilterListInput)
- if err != nil {
- return nil, err
- }
- q, err = manager.SLoadbalancerBackendgroupResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.LoadbalancerBackendGroupFilterListInput)
- if err != nil {
- return nil, err
- }
- q, err = manager.SGroupResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.GroupFilterListInput)
- if err != nil {
- return nil, err
- }
- return q, nil
- }
- func (sg *SScalingGroup) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- // attach with networks
- networks, _ := data.Get("networks")
- networkIds := networks.(*jsonutils.JSONArray).GetStringArray()
- for _, netId := range networkIds {
- err := ScalingGroupNetworkManager.Attach(ctx, sg.Id, netId)
- if err != nil {
- reason := fmt.Sprintf("Attach ScalingGroup '%s' with Network '%s' failed: %s", sg.Id, netId, err.Error())
- sg.SetStatus(ctx, userCred, api.SG_STATUS_CREATE_FAILED, reason)
- logclient.AddActionLogWithContext(ctx, sg, logclient.ACT_CREATE, reason, userCred, false)
- return
- }
- }
- now := time.Now()
- db.Update(sg, func() error {
- sg.Status = api.SG_STATUS_READY
- sg.AllowScaleTime = now
- sg.NextCheckTime = now.Add(time.Duration(sg.HealthCheckCycle) * time.Second)
- sg.SetEnabled(true)
- return nil
- })
- logclient.AddActionLogWithContext(ctx, sg, logclient.ACT_CREATE, "", userCred, true)
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: sg,
- Action: notifyclient.ActionCreate,
- })
- }
- func (sg *SScalingGroup) AllowScale() bool {
- return sg.AllowScaleTime.Before(time.Now())
- }
- func (sg *SScalingGroup) SetAllowScaleTime(t time.Time) {
- if sg.AllowScaleTime.After(t) {
- return
- }
- _, err := db.Update(sg, func() error {
- sg.AllowScaleTime = t
- return nil
- })
- if err != nil {
- log.Errorf("Set AllowScaleTime error: %s", err)
- }
- return
- }
- func (sg *SScalingGroup) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
- err := db.EnabledPerformEnable(sg, ctx, userCred, true)
- if err != nil {
- return nil, errors.Wrap(err, "EnabledPerformEnable")
- }
- return nil, nil
- }
- func (sg *SScalingGroup) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
- err := db.EnabledPerformEnable(sg, ctx, userCred, false)
- if err != nil {
- return nil, errors.Wrap(err, "EnabledPerformEnable")
- }
- return nil, nil
- }
- func (s *SGuest) PerformDetachScalingGroup(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input api.SGPerformDetachScalingGroupInput) (jsonutils.JSONObject, error) {
- // check ScalingGroup
- model, err := ScalingGroupManager.FetchByIdOrName(ctx, userCred, input.ScalingGroup)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewInputParameterError("no such ScalingGroup '%s'", input.ScalingGroup)
- }
- return nil, errors.Wrap(err, "ScalingGroupManager.FetchByIdOrName")
- }
- // check ScalingGroupGuest
- sggs, err := ScalingGroupGuestManager.Fetch(model.GetId(), s.Id)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewInputParameterError("Guest '%s' don't belong to ScalingGroup '%s'", s.Id, model.GetId())
- }
- return nil, errors.Wrap(err, "ScalingGroupGuestManager.Fetch")
- }
- if len(sggs) == 0 {
- return nil, httperrors.NewInputParameterError("Guest '%s' don't belong to ScalingGroup '%s'", s.Id, model.GetId())
- }
- sg := model.(*SScalingGroup)
- input.ScalingGroup = sggs[0].ScalingGroupId
- sggs[0].SetGuestStatus(api.SG_GUEST_STATUS_REMOVING)
- taskData := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- taskData.Set("guest", jsonutils.NewString(s.GetId()))
- task, err := taskman.TaskManager.NewTask(ctx, "GuestDetachScalingGroupTask", sg, userCred, taskData, "", "")
- if err != nil {
- return nil, errors.Wrap(err, "Start GuestDetachScalingGroupTask failed")
- }
- task.ScheduleRun(nil)
- return nil, nil
- }
- func (sg *SScalingGroup) Networks() ([]SNetwork, error) {
- nets := make([]SNetwork, 0, 1)
- sgnQuery := ScalingGroupNetworkManager.Query("network_id").Equals("scaling_group_id", sg.Id).SubQuery()
- netQuery := NetworkManager.Query().In("id", sgnQuery)
- err := db.FetchModelObjects(NetworkManager, netQuery, &nets)
- if err != nil {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return nets, nil
- }
- func (sg *SScalingGroup) NetworkIds() ([]string, error) {
- sgnQuery := ScalingGroupNetworkManager.Query("network_id").Equals("scaling_group_id", sg.Id)
- rows, err := sgnQuery.Rows()
- if err != nil {
- return nil, errors.Wrap(err, "SQuery.Rows")
- }
- defer rows.Close()
- nets := make([]string, 0, 1)
- for rows.Next() {
- var net string
- rows.Scan(&net)
- nets = append(nets, net)
- }
- return nets, nil
- }
- func (sg *SScalingGroup) Activities() ([]SScalingActivity, error) {
- q := ScalingActivityManager.Query().Equals("scaling_group_id", sg.Id)
- activities := make([]SScalingActivity, 0, 1)
- err := db.FetchModelObjects(ScalingActivityManager, q, &activities)
- if err != nil {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return activities, nil
- }
- func (manager *SScalingGroupManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
- }
- if keys.ContainsAny(manager.SVpcResourceBaseManager.GetExportKeys()...) {
- // SCloudregionResourceBaseManager
- q, err = manager.SVpcResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.ContainsAny(manager.SLoadbalancerBackendgroupResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SLoadbalancerBackendgroupResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SLoadbalancerBackendgroupResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.ContainsAny(manager.SGroupResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SGroupResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SGroupResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.ContainsAny(manager.SGuestTemplateResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SGuestTemplateResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SGuestTemplateResourceBaseManager.ListItemExportKeys")
- }
- }
- return q, nil
- }
|