| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // 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 autoscaling
- import (
- "context"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/util/sets"
- "yunion.io/x/sqlchemy"
- apis "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/compute/models"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
- )
- var UnhealthStatus = []string{
- apis.VM_UNKNOWN, apis.VM_SCHEDULE_FAILED, apis.VM_NETWORK_FAILED, apis.VM_DEVICE_FAILED, apis.VM_DISK_FAILED,
- apis.VM_DEPLOY_FAILED, apis.VM_READY, apis.VM_START_FAILED,
- }
- type sUnnormalGuest struct {
- Id string `json:"id"`
- Status string `json:"status"`
- ScalngGroupId string `json:"scaling_group_id"`
- CreateCompleteTime time.Time `json:"create_complete_time"`
- }
- func (asc *SASController) HealthCheckSql() *sqlchemy.SQuery {
- now := time.Now()
- sgSubQ := models.ScalingGroupManager.Query("id").IsTrue("enabled").LT("next_check_time", now).SubQuery()
- sggQ := models.ScalingGroupGuestManager.Query("guest_id", "scaling_group_id", "updated_at").Equals("guest_status", apis.SG_GUEST_STATUS_READY)
- sggSubQ := sggQ.Join(sgSubQ, sqlchemy.Equals(sgSubQ.Field("id"), sggQ.Field("scaling_group_id"))).SubQuery()
- q := models.GuestManager.Query("id", "status").In("status", UnhealthStatus)
- q = q.Join(sggSubQ, sqlchemy.Equals(q.Field("id"), sggSubQ.Field("guest_id")))
- q = q.AppendField(sggSubQ.Field("scaling_group_id"), sggSubQ.Field("updated_at", "create_complete_time"))
- return q
- }
- func (asc *SASController) CheckInstanceHealth(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- // Fetch all unhealth status instace
- unnormalGuests := make([]sUnnormalGuest, 0, 5)
- scalingGroupIdSet := sets.NewString()
- rows, err := asc.HealthCheckSql().Rows()
- if err != nil {
- log.Errorf("GuestManager's SQuery.Rows: %s", err.Error())
- return
- }
- for rows.Next() {
- var ug sUnnormalGuest
- rows.Scan(&ug.Id, &ug.Status, &ug.ScalngGroupId, &ug.CreateCompleteTime)
- scalingGroupIdSet.Insert(ug.ScalngGroupId)
- unnormalGuests = append(unnormalGuests, ug)
- }
- rows.Close()
- // fetch all ScalingGroup
- scalingGroups := make([]models.SScalingGroup, 0, scalingGroupIdSet.Len())
- q := models.ScalingGroupManager.Query().In("id", scalingGroupIdSet.UnsortedList())
- err = db.FetchModelObjects(models.ScalingGroupManager, q, &scalingGroups)
- if err != nil {
- log.Errorf("unable to fetch ScalingGroup")
- return
- }
- scalingGroupMap := make(map[string]*models.SScalingGroup, len(scalingGroups))
- for i := range scalingGroups {
- scalingGroupMap[scalingGroups[i].GetId()] = &scalingGroups[i]
- }
- // update NextCheckTime for ScalingGroup
- now := time.Now()
- for i := range scalingGroups {
- sg := &scalingGroups[i]
- _, err := db.Update(sg, func() error {
- sg.NextCheckTime = now.Add(time.Duration(sg.HealthCheckCycle) * time.Second)
- return nil
- })
- if err != nil {
- log.Errorf("unable to update NextCheckTime for ScalingGroup '%s'", sg.GetId())
- }
- }
- // request to detach
- readyGuestList := make([]string, 0, 5)
- readyGuestMap := make(map[string]string, 5)
- removeParams := jsonutils.NewDict()
- removeParams.Set("delete_server", jsonutils.JSONTrue)
- removeParams.Set("auto", jsonutils.JSONTrue)
- session := auth.GetSession(ctx, userCred, "")
- for i := range unnormalGuests {
- ug := unnormalGuests[i]
- if ug.CreateCompleteTime.Add(time.Duration(scalingGroupMap[ug.ScalngGroupId].HealthCheckGov) * time.Second).After(now) {
- continue
- }
- if ug.Status == apis.VM_READY {
- readyGuestList = append(readyGuestList, ug.Id)
- readyGuestMap[ug.Id] = ug.ScalngGroupId
- continue
- }
- removeParams.Set("scaling_group", jsonutils.NewString(ug.ScalngGroupId))
- _, err := compute.Servers.PerformAction(session, ug.Id, "detach-scaling-group", removeParams)
- if err != nil {
- log.Errorf("Request Detach Scaling Group failed: %s", err.Error())
- }
- }
- // check NextCheckTime for ScalngGroup
- if len(readyGuestList) > 0 {
- go func() {
- time.Sleep(2 * time.Minute)
- q := models.GuestManager.Query("id").In("id", readyGuestList).Equals("status", apis.VM_READY)
- rows, err := q.Rows()
- if err != nil {
- log.Errorf("GuestManager's SQuery.Rows: %s", err.Error())
- }
- removeGuestList := make([]string, 0, len(readyGuestList)/2)
- for rows.Next() {
- var g string
- rows.Scan(&g)
- removeGuestList = append(removeGuestList, g)
- }
- rows.Close()
- for _, id := range removeGuestList {
- removeParams.Set("scaling_group", jsonutils.NewString(readyGuestMap[id]))
- _, err := compute.Servers.PerformAction(session, id, "detach-scaling-group", removeParams)
- if err != nil {
- log.Errorf("Request Detach Scaling Group failed: %s", err.Error())
- }
- }
- }()
- }
- }
|