mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-31 13:00:41 +08:00
Merge pull request #6691 from rainzm/scheduledtasks/init
Scheduledtasks/init
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package compute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -25,6 +26,20 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type Timer struct {
|
||||
TimingExecTime string `help:"Exectime for 'timing' type trigger, format:'2006-01-02 15:04:05'" json:"exec_time"`
|
||||
}
|
||||
|
||||
type CycleTimer struct {
|
||||
CycleCycleType string `help:"Cycle type for cycle timer" json:"cycle_type" choices:"day|week|month"`
|
||||
CycleMinute int `help:"Minute of cycle timer" json:"minute"`
|
||||
CycleHour int `help:"Hour of cycle timer" json:"hour"`
|
||||
CycleWeekdays []int `help:"Weekdays for cycle timer" json:"weekdays"`
|
||||
CycleMonthDays []int `help:"Month days for cycle timer" json:"month_days"`
|
||||
CycleStartTime string `help:"Start time for cycle timer, format:'2006-01-02 15:04:05'" json:"start_time"`
|
||||
CycleEndTime string `help:"End time for cycle timer, format:'2006-01-02 15:04:05'" json:"end_time"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
type ScalingPolicyListOptions struct {
|
||||
options.BaseListOptions
|
||||
@@ -57,20 +72,6 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type ScalingTimer struct {
|
||||
TimingExecTime time.Time `help:"Exectime for 'timing' type trigger" json:"exec_time"`
|
||||
}
|
||||
|
||||
type ScalingCycleTimer struct {
|
||||
CycleCycleType string `help:"Cycle type for 'cycle' type trigger" json:"cycle_type"`
|
||||
CycleMinute int `help:"Minute of 'cycle' type trigger" json:"minute"`
|
||||
CycleHour int `help:"Hour of 'cycle' type trigger" json:"hour"`
|
||||
CycleWeekdays []int `help:"Weekdays for 'cycle' type trigger" json:"weekdays"`
|
||||
CycleMonthDays []int `help:"Month days for 'cycle' type trigger" json:"month_days"`
|
||||
CycleStartTime time.Time `help:"Start time for 'cycle' type trigger" json:"start_time"`
|
||||
CycleEndTime time.Time `help:"End time for 'cycle' type trigger" json:"end_time"`
|
||||
}
|
||||
|
||||
type ScalingAlarm struct {
|
||||
AlarmCumulate int `help:"Cumulate times alarm will trigger, for 'alarm' trigger" json:"cumulate"`
|
||||
AlarmCycle int `help:"Monitoring cycle for indicators, for 'alarm' trigger" json:"cycle"`
|
||||
@@ -85,8 +86,8 @@ func init() {
|
||||
ScalingGroup string `help:"ScalingGroup ID or Name" json:"scaling_group"`
|
||||
TriggerType string `help:"Trigger type" choices:"alarm|timing|cycle" json:"trigger_type"`
|
||||
|
||||
ScalingTimer
|
||||
ScalingCycleTimer
|
||||
Timer
|
||||
CycleTimer
|
||||
ScalingAlarm
|
||||
|
||||
Action string `help:"Action for scaling policy" choices:"add|remove|set" json:"action"`
|
||||
@@ -97,20 +98,33 @@ func init() {
|
||||
|
||||
R(&ScalingPolicyCreateOptions{}, "scaling-policy-create", "Create Scaling Policy",
|
||||
func(s *mcclient.ClientSession, args *ScalingPolicyCreateOptions) error {
|
||||
formatStr := "2006-01-02 15:04:05"
|
||||
timingExecTime, err := time.Parse(formatStr, args.TimingExecTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'exec_time'")
|
||||
}
|
||||
cycleStarTime, err := time.Parse(formatStr, args.CycleStartTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'start_time'")
|
||||
}
|
||||
cycleEndTime, err := time.Parse(formatStr, args.CycleEndTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'end_time'")
|
||||
}
|
||||
spCreateInput := api.ScalingPolicyCreateInput{
|
||||
ScalingGroup: args.ScalingGroup,
|
||||
TriggerType: args.TriggerType,
|
||||
Timer: api.ScalingTimerCreateInput{
|
||||
ExecTime: args.TimingExecTime,
|
||||
Timer: api.TimerCreateInput{
|
||||
ExecTime: timingExecTime,
|
||||
},
|
||||
CycleTimer: api.ScalingCycleTimerCreateInput{
|
||||
CycleTimer: api.CycleTimerCreateInput{
|
||||
CycleType: args.CycleCycleType,
|
||||
Minute: args.CycleMinute,
|
||||
Hour: args.CycleHour,
|
||||
WeekDays: args.CycleWeekdays,
|
||||
MonthDays: args.CycleMonthDays,
|
||||
StartTime: args.CycleStartTime,
|
||||
EndTime: args.CycleEndTime,
|
||||
StartTime: cycleStarTime,
|
||||
EndTime: cycleEndTime,
|
||||
},
|
||||
Alarm: api.ScalingAlarmCreateInput{
|
||||
Cumulate: args.AlarmCumulate,
|
||||
|
||||
211
cmd/climc/shell/compute/scheduled_task.go
Normal file
211
cmd/climc/shell/compute/scheduled_task.go
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
apis "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ScheduledTaskListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
ScheduledType string `help:"scheduled type" choices:"timing|cycle"`
|
||||
ResourceType string `help:"resource type"`
|
||||
Operation string `help:"operation"`
|
||||
}
|
||||
R(&ScheduledTaskListOptions{}, "scheduledtask-list", "list Scheduled Task", func(s *mcclient.ClientSession, args *ScheduledTaskListOptions) error {
|
||||
params, err := options.ListStructToParams(args)
|
||||
tasks, err := modules.ScheduledTask.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(tasks, modules.ScheduledTask.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
|
||||
type ScheduledTaskShowOptions struct {
|
||||
ID string `help:"ScheduledTask ID or Name"`
|
||||
}
|
||||
R(&ScheduledTaskShowOptions{}, "scheudled-task-show", "Show Scheduled Task", func(s *mcclient.ClientSession,
|
||||
args *ScheduledTaskShowOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("details", jsonutils.JSONTrue)
|
||||
task, err := modules.ScheduledTask.Get(s, args.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(task)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ScheduledTaskCreateOptions struct {
|
||||
NAME string `help:"ScheduledTask Name" json:"name"`
|
||||
ScheduledType string `help:"Scheudled Type" choices:"timing|cycle" json:"scheduled_type"`
|
||||
|
||||
Timer
|
||||
CycleTimer
|
||||
|
||||
ResourceType string `help:"resource type"`
|
||||
Operation string `help:"operation"`
|
||||
LabelType string `help:"label type"`
|
||||
Labels []string `help:"labels"`
|
||||
}
|
||||
R(&ScheduledTaskCreateOptions{}, "scheduledtask-create", "Create Scheduled Task", func(s *mcclient.ClientSession, args *ScheduledTaskCreateOptions) error {
|
||||
formatStr := "2006-01-02 15:04:05"
|
||||
var exectime, starttime, endtime time.Time
|
||||
var err error
|
||||
if len(args.TimingExecTime) > 0 {
|
||||
exectime, err = time.Parse(formatStr, args.TimingExecTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'exec_time'")
|
||||
}
|
||||
}
|
||||
if len(args.CycleStartTime) > 0 {
|
||||
starttime, err = time.Parse(formatStr, args.CycleStartTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'start_time'")
|
||||
}
|
||||
}
|
||||
if len(args.CycleEndTime) > 0 {
|
||||
endtime, err = time.Parse(formatStr, args.CycleEndTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for 'end_time'")
|
||||
}
|
||||
}
|
||||
stCreateInput := apis.ScheduledTaskCreateInput{
|
||||
ScheduledType: args.ScheduledType,
|
||||
Timer: apis.TimerCreateInput{
|
||||
ExecTime: exectime,
|
||||
},
|
||||
CycleTimer: apis.CycleTimerCreateInput{
|
||||
CycleType: args.CycleCycleType,
|
||||
Minute: args.CycleMinute,
|
||||
Hour: args.CycleHour,
|
||||
WeekDays: args.CycleWeekdays,
|
||||
MonthDays: args.CycleMonthDays,
|
||||
StartTime: starttime,
|
||||
EndTime: endtime,
|
||||
},
|
||||
ResourceType: args.ResourceType,
|
||||
Operation: args.Operation,
|
||||
LabelType: args.LabelType,
|
||||
Labels: args.Labels,
|
||||
}
|
||||
stCreateInput.Name = args.NAME
|
||||
ret, err := modules.ScheduledTask.Create(s, jsonutils.Marshal(stCreateInput))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ScheduledTaskEnableOptions struct {
|
||||
ID string `help:"ScheduledTask ID or Name"`
|
||||
}
|
||||
R(&ScheduledTaskEnableOptions{}, "scheduledtask-enable", "Enable ScheduledTask", func(s *mcclient.ClientSession,
|
||||
args *ScheduledTaskEnableOptions) error {
|
||||
ret, err := modules.ScheduledTask.PerformAction(s, args.ID, "enable", jsonutils.NewDict())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
R(&ScheduledTaskEnableOptions{}, "scheduledtask-disable", "Disable ScheduledTask",
|
||||
func(s *mcclient.ClientSession, args *ScheduledTaskEnableOptions) error {
|
||||
ret, err := modules.ScheduledTask.PerformAction(s, args.ID, "disable", jsonutils.NewDict())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
type ScheduledTaskSetLabelsOptions struct {
|
||||
ID string `help:"ScheduledTask ID or Name"`
|
||||
Labels []string `help:"Label"`
|
||||
}
|
||||
R(&ScheduledTaskSetLabelsOptions{}, "scheduledtask-setlabels", "Trigger ScheduledTask's action",
|
||||
func(s *mcclient.ClientSession, args *ScheduledTaskSetLabelsOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("labels", jsonutils.Marshal(args.Labels))
|
||||
ret, err := modules.ScheduledTask.PerformAction(s, args.ID, "set-labels", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
type ScheduledTaskTriggerOptions struct {
|
||||
ID string `help:"ScheduledTask ID or Name"`
|
||||
}
|
||||
R(&ScheduledTaskTriggerOptions{}, "scheduledtask-trigger", "Trigger ScheduledTask",
|
||||
func(s *mcclient.ClientSession, args *ScheduledTaskTriggerOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
ret, err := modules.ScheduledTask.PerformAction(s, args.ID, "trigger", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
type ScheduledTaskDeleteOptions struct {
|
||||
ID string `help:"ScheduledTask ID or Name"`
|
||||
}
|
||||
R(&ScheduledTaskDeleteOptions{}, "scheduledtask-delete", "Delete ScheduledTask",
|
||||
func(s *mcclient.ClientSession, args *ScheduledTaskDeleteOptions) error {
|
||||
ret, err := modules.ScheduledTask.Delete(s, args.ID, jsonutils.NewDict())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
type ScheduledTaskAvtivityListOptions struct {
|
||||
options.BaseListOptions
|
||||
ScheduledTask string `help:"Scheduled Task" json:"scheduled_task"`
|
||||
}
|
||||
R(&ScheduledTaskAvtivityListOptions{}, "scheduledtask-activity-list", "List Scheduled Task Activity",
|
||||
func(s *mcclient.ClientSession, args *ScheduledTaskAvtivityListOptions) error {
|
||||
params, err := options.ListStructToParams(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := modules.ScheduledTaskActivity.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(list, modules.ScheduledTaskActivity.GetColumns(s))
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ type ScalingGroupResourceInfo struct {
|
||||
}
|
||||
|
||||
type ScalingGroupFilterListInput struct {
|
||||
// descirption: 伸缩组 Id or Name
|
||||
// description: 伸缩组 Id or Name
|
||||
// example: sg-1234
|
||||
ScalingGroup string `json:"scaling_group"`
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ type ScalingPolicyDetails struct {
|
||||
ScalingGroupResourceInfo
|
||||
SScalingPolicy
|
||||
// 定时方式触发
|
||||
Timer ScalingTimerDetails `json:"timer"`
|
||||
Timer TimerDetails `json:"timer"`
|
||||
// 周期方式触发
|
||||
CycleTimer ScalingCycleTimerDetails `json:"cycle_timer"`
|
||||
CycleTimer CycleTimerDetails `json:"cycle_timer"`
|
||||
// 告警方式触发
|
||||
Alarm ScalingAlarmDetails `json:"alarm"`
|
||||
}
|
||||
@@ -43,9 +43,9 @@ type ScalingPolicyCreateInput struct {
|
||||
// enum: timing,cycle,alarm
|
||||
TriggerType string `json:"trigger_type"`
|
||||
|
||||
Timer ScalingTimerCreateInput `json:"timer"`
|
||||
CycleTimer ScalingCycleTimerCreateInput `json:"cycle_timer"`
|
||||
Alarm ScalingAlarmCreateInput `json:"alarm"`
|
||||
Timer TimerCreateInput `json:"timer"`
|
||||
CycleTimer CycleTimerCreateInput `json:"cycle_timer"`
|
||||
Alarm ScalingAlarmCreateInput `json:"alarm"`
|
||||
|
||||
// desciption: 伸缩策略的行为(增加还是删除或者调整为)
|
||||
// enum: add,remove,set
|
||||
|
||||
@@ -16,13 +16,13 @@ package compute
|
||||
|
||||
import "time"
|
||||
|
||||
type ScalingTimerCreateInput struct {
|
||||
type TimerCreateInput struct {
|
||||
|
||||
// description: 执行时间
|
||||
ExecTime time.Time
|
||||
}
|
||||
|
||||
type ScalingCycleTimerCreateInput struct {
|
||||
type CycleTimerCreateInput struct {
|
||||
|
||||
// description: 周期类型
|
||||
// enum: day,week,month
|
||||
@@ -81,12 +81,12 @@ type ScalingAlarmCreateInput struct {
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type ScalingTimerDetails struct {
|
||||
type TimerDetails struct {
|
||||
// description: 执行时间
|
||||
ExecTime time.Time `json:"exec_time"`
|
||||
}
|
||||
|
||||
type ScalingCycleTimerDetails struct {
|
||||
type CycleTimerDetails struct {
|
||||
// description: 周期类型:按天/周/月
|
||||
CycleType string `json:"cycle_type"`
|
||||
// description: 分钟
|
||||
|
||||
120
pkg/apis/compute/scheduled_task.go
Normal file
120
pkg/apis/compute/scheduled_task.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
)
|
||||
|
||||
type ScheduledTaskDetails struct {
|
||||
apis.VirtualResourceDetails
|
||||
SScheduledTask
|
||||
// 定时方式触发
|
||||
Timer TimerDetails `json:"timer"`
|
||||
// 周期方式触发
|
||||
CycleTimer CycleTimerDetails `json:"cycle_timer"`
|
||||
// 绑定的所有标示
|
||||
Labels []string `json:"labels,allowempty"`
|
||||
LabelDetails []LabelDetail `json:"label_details,allowempty"`
|
||||
}
|
||||
|
||||
type LabelDetail struct {
|
||||
Label string `json:"label"`
|
||||
IsolatedTime time.Time `json:"isolated_time"`
|
||||
}
|
||||
|
||||
type ScheduledTaskListInput struct {
|
||||
apis.VirtualResourceListInput
|
||||
apis.EnabledResourceBaseListInput
|
||||
|
||||
// description: resource type
|
||||
// example: server
|
||||
// enum: server
|
||||
ResourceType string `json:"resource_type"`
|
||||
|
||||
// description: label type
|
||||
// example: tag
|
||||
LabelType string `json:"label_type"`
|
||||
|
||||
// description: operation
|
||||
// example: stop
|
||||
// enum: start,stop,restart
|
||||
Operation string `json:"operation"`
|
||||
}
|
||||
|
||||
type ScheduledTaskCreateInput struct {
|
||||
apis.VirtualResourceCreateInput
|
||||
apis.EnabledBaseResourceCreateInput
|
||||
|
||||
// description: scheduled type
|
||||
// enum: cycle,timing
|
||||
// example: timing
|
||||
ScheduledType string `json:"scheduled_type"`
|
||||
Timer TimerCreateInput `json:"timer"`
|
||||
CycleTimer CycleTimerCreateInput `json:"cycle_timer"`
|
||||
|
||||
// description: resource type
|
||||
// enum: server
|
||||
// example: server
|
||||
ResourceType string `json:"resource_type"`
|
||||
// description: operation
|
||||
// enum: start,stop,restart
|
||||
// example: stop
|
||||
Operation string `json:"operation"`
|
||||
// description: label type
|
||||
// enum: tag,id
|
||||
// example: id
|
||||
LabelType string `json:"label_type"`
|
||||
// description: labels
|
||||
// example: {g-12345}
|
||||
Labels []string
|
||||
}
|
||||
|
||||
type ScheduledTaskResourceInfo struct {
|
||||
// description: 定时任务名称
|
||||
// example: st-nihao
|
||||
ScheduledTask string `json:"scheduled_task"`
|
||||
|
||||
// description: 定时任务ID
|
||||
// example: 1234
|
||||
ScheduledTaskId string `json:"scheduled_task_id"`
|
||||
}
|
||||
|
||||
type ScheduledTaskFilterListInput struct {
|
||||
// description: 定时任务 Id or Name
|
||||
// example: st-1234
|
||||
ScheduledTask string `json:"scheduled_task"`
|
||||
}
|
||||
|
||||
type ScheduledTaskActivityDetails struct {
|
||||
apis.StatusStandaloneResourceDetails
|
||||
SScheduledTaskActivity
|
||||
}
|
||||
|
||||
type ScheduledTaskActivityListInput struct {
|
||||
apis.StatusStandaloneResourceListInput
|
||||
// description: 定时任务 ID or Name
|
||||
// example: st-11212
|
||||
ScheduledTask string `json:"scheduled_task"`
|
||||
}
|
||||
|
||||
type ScheduledTaskSetLabelsInput struct {
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
type ScheduledTaskTriggerInput struct {
|
||||
}
|
||||
38
pkg/apis/compute/scheduled_task_const.go
Normal file
38
pkg/apis/compute/scheduled_task_const.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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 compute
|
||||
|
||||
const (
|
||||
ST_TYPE_TIMING = "timing" // 定时
|
||||
ST_TYPE_CYCLE = "cycle" // 周期
|
||||
|
||||
ST_STATUS_READY = "ready"
|
||||
ST_STATUS_CREATE_FAILED = "create_failed"
|
||||
|
||||
ST_RESOURCE_SERVER = "server"
|
||||
|
||||
ST_RESOURCE_OPERATION_START = "start"
|
||||
ST_RESOURCE_OPERATION_STOP = "stop"
|
||||
ST_RESOURCE_OPERATION_RESTART = "restart"
|
||||
|
||||
ST_LABEL_ID = "id"
|
||||
ST_LABEL_TAG = "tag"
|
||||
|
||||
ST_ACTIVITY_STATUS_EXEC = "execution" // 执行中
|
||||
ST_ACTIVITY_STATUS_SUCCEED = "succeed" // 成功
|
||||
ST_ACTIVITY_STATUS_PART_SUCCEED = "part_succeed" // 部分成功
|
||||
ST_ACTIVITY_STATUS_FAILED = "failed" // 失败
|
||||
ST_ACTIVITY_STATUS_REJECT = "reject" // 拒绝
|
||||
)
|
||||
@@ -855,7 +855,8 @@ type SGuestTemplate struct {
|
||||
// 计费方式
|
||||
BillingType string `json:"billing_type"`
|
||||
// 其他配置信息
|
||||
Content interface{} `json:"content"`
|
||||
Content interface{} `json:"content"`
|
||||
LastCheckTime time.Time `json:"last_check_time"`
|
||||
}
|
||||
|
||||
// SGuestTemplateResourceBase is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SGuestTemplateResourceBase.
|
||||
@@ -1812,17 +1813,7 @@ type SScalingPolicyBase struct {
|
||||
type SScalingTimer struct {
|
||||
apis.SStandaloneResourceBase
|
||||
SScalingPolicyBase
|
||||
// Timer type
|
||||
Type string `json:"type"`
|
||||
// 0-59
|
||||
Minute int `json:"minute"`
|
||||
// 0-23
|
||||
Hour int `json:"hour"`
|
||||
// 0-7 1 is Monday 0 is unlimited
|
||||
WeekDays byte `json:"week_days"`
|
||||
// 0-31 0 is unlimited
|
||||
MonthDays uint32 `json:"month_days"`
|
||||
IsExpired bool `json:"is_expired"`
|
||||
STimer
|
||||
}
|
||||
|
||||
// SSchedpolicy is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SSchedpolicy.
|
||||
@@ -1855,6 +1846,33 @@ type SSchedtagResourceBase struct {
|
||||
SchedtagId string `json:"schedtag_id"`
|
||||
}
|
||||
|
||||
// SScheduledTask is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SScheduledTask.
|
||||
type SScheduledTask struct {
|
||||
apis.SVirtualResourceBase
|
||||
apis.SEnabledResourceBase
|
||||
ScheduledType string `json:"scheduled_type"`
|
||||
STimer
|
||||
ResourceType string `json:"resource_type"`
|
||||
Operation string `json:"operation"`
|
||||
LabelType string `json:"label_type"`
|
||||
}
|
||||
|
||||
// SScheduledTaskActivity is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SScheduledTaskActivity.
|
||||
type SScheduledTaskActivity struct {
|
||||
apis.SStatusStandaloneResourceBase
|
||||
ScheduledTaskId string `json:"scheduled_task_id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// SScheduledTaskLabel is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SScheduledTaskLabel.
|
||||
type SScheduledTaskLabel struct {
|
||||
apis.SVirtualJointResourceBase
|
||||
ScheduledTaskId string `json:"scheduled_task_id"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
// SSecurityGroup is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SSecurityGroup.
|
||||
type SSecurityGroup struct {
|
||||
apis.SSharableVirtualResourceBase
|
||||
@@ -2071,6 +2089,27 @@ type SSyncableBaseResource struct {
|
||||
LastSyncEndAt time.Time `json:"last_sync_end_at"`
|
||||
}
|
||||
|
||||
// STimer is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.STimer.
|
||||
type STimer struct {
|
||||
// Cycle type
|
||||
Type string `json:"type"`
|
||||
// 0-59
|
||||
Minute int `json:"minute"`
|
||||
// 0-23
|
||||
Hour int `json:"hour"`
|
||||
// 0-7 1 is Monday 0 is unlimited
|
||||
WeekDays byte `json:"week_days"`
|
||||
// 0-31 0 is unlimited
|
||||
MonthDays uint32 `json:"month_days"`
|
||||
// StartTime represent the start time of this timer
|
||||
StartTime time.Time `json:"start_time"`
|
||||
// EndTime represent deadline of this timer
|
||||
EndTime time.Time `json:"end_time"`
|
||||
// NextTime represent the time timer should bell
|
||||
NextTime time.Time `json:"next_time"`
|
||||
IsExpired bool `json:"is_expired"`
|
||||
}
|
||||
|
||||
// SVCenter is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SVCenter.
|
||||
type SVCenter struct {
|
||||
apis.SEnabledStatusStandaloneResourceBase
|
||||
|
||||
@@ -4675,6 +4675,22 @@ func (self *SGuest) PendingDetachScalingGroup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGuest) DetachScheduledTask(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
q := ScheduledTaskLabelManager.Query().Equals("label", self.Id)
|
||||
stls := make([]SScheduledTaskLabel, 0, 1)
|
||||
err := db.FetchModelObjects(ScheduledTaskLabelManager, q, &stls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range stls {
|
||||
err := stls[i].Detach(ctx, userCred)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGuest) DeleteEip(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
eip, err := self.GetEip()
|
||||
if err != nil {
|
||||
|
||||
@@ -56,6 +56,8 @@ func InitDB() error {
|
||||
DynamicschedtagManager,
|
||||
ServerSkuManager,
|
||||
ElasticcacheSkuManager,
|
||||
|
||||
ScheduledTaskActivityManager,
|
||||
} {
|
||||
err := manager.InitializeData()
|
||||
if err != nil {
|
||||
|
||||
@@ -304,20 +304,24 @@ func (sp *SScalingPolicy) Trigger(input *api.ScalingPolicyCreateInput) (IScaling
|
||||
case api.TRIGGER_TIMING:
|
||||
return &SScalingTimer{
|
||||
SScalingPolicyBase: SScalingPolicyBase{sp.GetId()},
|
||||
Type: api.TIMER_TYPE_ONCE,
|
||||
StartTime: input.Timer.ExecTime,
|
||||
EndTime: input.Timer.ExecTime,
|
||||
NextTime: input.Timer.ExecTime,
|
||||
STimer: STimer{
|
||||
Type: api.TIMER_TYPE_ONCE,
|
||||
StartTime: input.Timer.ExecTime,
|
||||
EndTime: input.Timer.ExecTime,
|
||||
NextTime: input.Timer.ExecTime,
|
||||
},
|
||||
}, nil
|
||||
case api.TRIGGER_CYCLE:
|
||||
trigger := &SScalingTimer{
|
||||
SScalingPolicyBase: SScalingPolicyBase{sp.GetId()},
|
||||
Type: input.CycleTimer.CycleType,
|
||||
Minute: input.CycleTimer.Minute,
|
||||
Hour: input.CycleTimer.Hour,
|
||||
StartTime: input.CycleTimer.StartTime,
|
||||
EndTime: input.CycleTimer.EndTime,
|
||||
NextTime: time.Time{},
|
||||
STimer: STimer{
|
||||
Type: input.CycleTimer.CycleType,
|
||||
Minute: input.CycleTimer.Minute,
|
||||
Hour: input.CycleTimer.Hour,
|
||||
StartTime: input.CycleTimer.StartTime,
|
||||
EndTime: input.CycleTimer.EndTime,
|
||||
NextTime: time.Time{},
|
||||
},
|
||||
}
|
||||
log.Debugf("setweekdays")
|
||||
trigger.SetWeekDays(input.CycleTimer.WeekDays)
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -35,7 +34,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
|
||||
"yunion.io/x/onecloud/pkg/util/bitmap"
|
||||
)
|
||||
|
||||
type IScalingTriggerDesc interface {
|
||||
@@ -97,30 +95,7 @@ type SScalingTimer struct {
|
||||
|
||||
SScalingPolicyBase
|
||||
|
||||
// Timer type
|
||||
Type string `width:"8" charset:"ascii"`
|
||||
|
||||
// 0-59
|
||||
Minute int `nullable:"false"`
|
||||
|
||||
// 0-23
|
||||
Hour int `nullable:"false"`
|
||||
|
||||
// 0-7 1 is Monday 0 is unlimited
|
||||
WeekDays uint8 `nullable:"false"`
|
||||
|
||||
// 0-31 0 is unlimited
|
||||
MonthDays uint32 `nullable:"false"`
|
||||
|
||||
// StartTime represent the start time of this timer
|
||||
StartTime time.Time
|
||||
|
||||
// EndTime represent deadline of this timer
|
||||
EndTime time.Time
|
||||
|
||||
// NextTime represent the time timer should bell
|
||||
NextTime time.Time `index:"true"`
|
||||
IsExpired bool
|
||||
STimer
|
||||
}
|
||||
|
||||
type SScalingAlarmManager struct {
|
||||
@@ -181,111 +156,6 @@ func init() {
|
||||
ScalingAlarmManager.SetVirtualObject(ScalingAlarmManager)
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) GetWeekDays() []int {
|
||||
return bitmap.Uint2IntArray(uint32(st.WeekDays))
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) GetMonthDays() []int {
|
||||
return bitmap.Uint2IntArray(st.MonthDays)
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) SetWeekDays(days []int) {
|
||||
st.WeekDays = uint8(bitmap.IntArray2Uint(days))
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) SetMonthDays(days []int) {
|
||||
st.MonthDays = bitmap.IntArray2Uint(days)
|
||||
}
|
||||
|
||||
// Update will update the SScalingTimer
|
||||
func (st *SScalingTimer) Update(now time.Time) {
|
||||
if now.IsZero() {
|
||||
now = time.Now()
|
||||
}
|
||||
if !now.Before(st.EndTime) {
|
||||
st.IsExpired = true
|
||||
return
|
||||
}
|
||||
if now.Before(st.StartTime) {
|
||||
now = st.StartTime
|
||||
}
|
||||
if !st.NextTime.Before(now) {
|
||||
return
|
||||
}
|
||||
|
||||
newNextTime := time.Date(now.Year(), now.Month(), now.Day(), st.Hour, st.Minute, 0, 0, now.Location())
|
||||
if now.After(newNextTime) {
|
||||
newNextTime = newNextTime.AddDate(0, 0, 1)
|
||||
}
|
||||
switch {
|
||||
case st.WeekDays != 0:
|
||||
// week
|
||||
nowDay, weekdays := int(newNextTime.Weekday()), st.GetWeekDays()
|
||||
if nowDay == 0 {
|
||||
nowDay = 7
|
||||
}
|
||||
|
||||
// weekdays[0]+7 is for the case that all time nodes has been missed in this week
|
||||
weekdays = append(weekdays, weekdays[0]+7)
|
||||
index := sort.SearchInts(weekdays, nowDay)
|
||||
newNextTime = newNextTime.AddDate(0, 0, weekdays[index]-nowDay)
|
||||
case st.MonthDays != 0:
|
||||
// month
|
||||
monthdays := st.GetMonthDays()
|
||||
suitTime := newNextTime
|
||||
for {
|
||||
day := suitTime.Day()
|
||||
index := sort.SearchInts(monthdays, day)
|
||||
if index == len(monthdays) || monthdays[index] > st.MonthDaySum(suitTime) {
|
||||
// set suitTime as the first day of next month
|
||||
suitTime = suitTime.AddDate(0, 1, -suitTime.Day()+1)
|
||||
continue
|
||||
}
|
||||
newNextTime = time.Date(suitTime.Year(), suitTime.Month(), monthdays[index], suitTime.Hour(),
|
||||
suitTime.Minute(), 0, 0, suitTime.Location())
|
||||
break
|
||||
}
|
||||
default:
|
||||
// day
|
||||
|
||||
}
|
||||
log.Debugf("The final NextTime: %s", newNextTime)
|
||||
st.NextTime = newNextTime
|
||||
if st.NextTime.After(st.EndTime) {
|
||||
st.IsExpired = true
|
||||
}
|
||||
}
|
||||
|
||||
// MonthDaySum calculate the number of month's days
|
||||
func (st *SScalingTimer) MonthDaySum(t time.Time) int {
|
||||
year, month := t.Year(), t.Month()
|
||||
monthDays := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
if month != 2 {
|
||||
return monthDays[2]
|
||||
}
|
||||
if year%4 != 0 || (year%100 == 0 && year%400 != 0) {
|
||||
return 28
|
||||
}
|
||||
return 29
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) TimerDetails() api.ScalingTimerDetails {
|
||||
return api.ScalingTimerDetails{ExecTime: st.EndTime}
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) CycleTimerDetails() api.ScalingCycleTimerDetails {
|
||||
out := api.ScalingCycleTimerDetails{
|
||||
Minute: st.Minute,
|
||||
Hour: st.Hour,
|
||||
WeekDays: st.GetWeekDays(),
|
||||
MonthDays: st.GetMonthDays(),
|
||||
StartTime: st.StartTime,
|
||||
EndTime: st.EndTime,
|
||||
CycleType: st.Type,
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (sa *SScalingAlarm) AlarmDetails() api.ScalingAlarmDetails {
|
||||
return api.ScalingAlarmDetails{
|
||||
Cumulate: sa.Cumulate,
|
||||
@@ -298,40 +168,13 @@ func (sa *SScalingAlarm) AlarmDetails() api.ScalingAlarmDetails {
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) ValidateCreateData(input api.ScalingPolicyCreateInput) (api.ScalingPolicyCreateInput, error) {
|
||||
now := time.Now()
|
||||
var err error
|
||||
if input.TriggerType == api.TRIGGER_TIMING {
|
||||
if now.After(input.Timer.ExecTime) {
|
||||
return input, fmt.Errorf("exec_time is earlier than now")
|
||||
}
|
||||
return input, nil
|
||||
input.Timer, err = checkTimerCreateInput(input.Timer)
|
||||
return input, err
|
||||
}
|
||||
if input.CycleTimer.Minute < 0 || input.CycleTimer.Minute > 59 {
|
||||
return input, fmt.Errorf("minute should between 0 and 59")
|
||||
}
|
||||
if input.CycleTimer.Hour < 0 || input.CycleTimer.Hour > 23 {
|
||||
return input, fmt.Errorf("hour should between 0 and 23")
|
||||
}
|
||||
switch input.CycleTimer.CycleType {
|
||||
case api.TIMER_TYPE_DAY:
|
||||
input.CycleTimer.WeekDays = []int{}
|
||||
input.CycleTimer.MonthDays = []int{}
|
||||
case api.TIMER_TYPE_WEEK:
|
||||
if len(input.CycleTimer.WeekDays) == 0 {
|
||||
return input, fmt.Errorf("week_days should not be empty")
|
||||
}
|
||||
input.CycleTimer.MonthDays = []int{}
|
||||
case api.TIMER_TYPE_MONTH:
|
||||
if len(input.CycleTimer.MonthDays) == 0 {
|
||||
return input, fmt.Errorf("month_days should not be empty")
|
||||
}
|
||||
input.CycleTimer.WeekDays = []int{}
|
||||
default:
|
||||
return input, fmt.Errorf("unkown cycle type %s", input.CycleTimer.CycleType)
|
||||
}
|
||||
if now.After(input.CycleTimer.EndTime) {
|
||||
return input, fmt.Errorf("end_time is earlier than now")
|
||||
}
|
||||
return input, nil
|
||||
input.CycleTimer, err = checkCycleTimerCreateInput(input.CycleTimer)
|
||||
return input, err
|
||||
}
|
||||
|
||||
func (st *SScalingTimer) Register(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
|
||||
@@ -22,14 +22,14 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/bitmap"
|
||||
)
|
||||
|
||||
func TestSScalingTimer_Update(t *testing.T) {
|
||||
func TestSTimer_Update(t *testing.T) {
|
||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
ins := []struct {
|
||||
Timer *SScalingTimer
|
||||
Timer *STimer
|
||||
UpdateTimes int
|
||||
}{
|
||||
{
|
||||
Timer: &SScalingTimer{
|
||||
Timer: &STimer{
|
||||
Hour: 7,
|
||||
Minute: 21,
|
||||
Type: compute.TIMER_TYPE_DAY,
|
||||
@@ -40,7 +40,7 @@ func TestSScalingTimer_Update(t *testing.T) {
|
||||
UpdateTimes: 2,
|
||||
},
|
||||
{
|
||||
Timer: &SScalingTimer{
|
||||
Timer: &STimer{
|
||||
Hour: 7,
|
||||
Minute: 21,
|
||||
WeekDays: uint8(bitmap.IntArray2Uint([]int{1, 5, 7})),
|
||||
@@ -52,7 +52,7 @@ func TestSScalingTimer_Update(t *testing.T) {
|
||||
UpdateTimes: 4,
|
||||
},
|
||||
{
|
||||
Timer: &SScalingTimer{
|
||||
Timer: &STimer{
|
||||
Hour: 7,
|
||||
Minute: 21,
|
||||
MonthDays: bitmap.IntArray2Uint([]int{1, 10, 30}),
|
||||
@@ -82,7 +82,7 @@ func TestSScalingTimer_Update(t *testing.T) {
|
||||
{},
|
||||
},
|
||||
}
|
||||
wrapper := func(timer *SScalingTimer) time.Time {
|
||||
wrapper := func(timer *STimer) time.Time {
|
||||
fakeNow := timer.NextTime
|
||||
if !fakeNow.IsZero() {
|
||||
fakeNow = fakeNow.Add(time.Minute)
|
||||
|
||||
798
pkg/compute/models/scheduled_tasks.go
Normal file
798
pkg/compute/models/scheduled_tasks.go
Normal file
@@ -0,0 +1,798 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"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"
|
||||
cop "yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
var ScheduledTaskManager *SScheduledTaskManager
|
||||
|
||||
func init() {
|
||||
ScheduledTaskManager = &SScheduledTaskManager{
|
||||
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
||||
SScheduledTask{},
|
||||
"scheduledtasks_tbl",
|
||||
"scheduledtask",
|
||||
"scheduledtasks",
|
||||
),
|
||||
}
|
||||
ScheduledTaskManager.SetVirtualObject(ScheduledTaskManager)
|
||||
}
|
||||
|
||||
// +onecloud:swagger-gen-model-singular=scheduledtask
|
||||
// +onecloud:swagger-gen-model-singular=scheduledtasks
|
||||
type SScheduledTaskManager struct {
|
||||
db.SVirtualResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
type SScheduledTask struct {
|
||||
db.SVirtualResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
ScheduledType string `width:"16" charset:"ascii" create:"required" list:"user" get:"user"`
|
||||
|
||||
STimer
|
||||
|
||||
TimerDesc string `width:"128" charset:"utf8" list:"user" get:"user"`
|
||||
ResourceType string `width:"32" charset:"ascii" create:"required" list:"user" get:"user"`
|
||||
Operation string `width:"32" charset:"ascii" create:"required" list:"user" get:"user"`
|
||||
LabelType string `width:"4" charset:"ascii" create:"required" list:"user" get:"user"`
|
||||
}
|
||||
|
||||
func (stm *SScheduledTaskManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.ScheduledTaskListInput) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = stm.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VirtualResourceListInput)
|
||||
if err != nil {
|
||||
return q, err
|
||||
}
|
||||
q, err = stm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
|
||||
if err != nil {
|
||||
return q, err
|
||||
}
|
||||
if len(input.Operation) > 0 {
|
||||
q = q.Equals("operation", input.Operation)
|
||||
}
|
||||
if len(input.ResourceType) > 0 {
|
||||
q = q.Equals("resource_type", input.ResourceType)
|
||||
}
|
||||
if len(input.LabelType) > 0 {
|
||||
q = q.Equals("label_type", input.LabelType)
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (stm *SScheduledTaskManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential, query api.ScalingPolicyListInput) (*sqlchemy.SQuery, error) {
|
||||
return stm.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
|
||||
}
|
||||
|
||||
func (stm *SScheduledTask) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, isList bool) (api.ScheduledTaskDetails, error) {
|
||||
return api.ScheduledTaskDetails{}, nil
|
||||
}
|
||||
|
||||
func (stm *SScheduledTaskManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.ScheduledTaskDetails {
|
||||
rows := make([]api.ScheduledTaskDetails, len(objs))
|
||||
virRows := stm.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
var err error
|
||||
for i := range rows {
|
||||
rows[i], err = objs[i].(*SScheduledTask).getMoreDetails(ctx, userCred, query, isList)
|
||||
if err != nil {
|
||||
log.Errorf("SScheduledTask.getMoreDetails error: %s", err)
|
||||
}
|
||||
rows[i].VirtualResourceDetails = virRows[i]
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (api.ScheduledTaskDetails, error) {
|
||||
var out api.ScheduledTaskDetails
|
||||
switch st.ScheduledType {
|
||||
case api.ST_TYPE_TIMING:
|
||||
out.Timer = st.STimer.TimerDetails()
|
||||
case api.ST_TYPE_CYCLE:
|
||||
out.CycleTimer = st.STimer.CycleTimerDetails()
|
||||
}
|
||||
// fill label
|
||||
stLabels, err := st.STLabels()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Labels = make([]string, len(stLabels))
|
||||
out.LabelDetails = make([]api.LabelDetail, len(stLabels))
|
||||
for i := range stLabels {
|
||||
out.Labels[i] = stLabels[i].Label
|
||||
out.LabelDetails[i].IsolatedTime = stLabels[i].CreatedAt
|
||||
out.LabelDetails[i].Label = stLabels[i].Label
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (stm *SScheduledTaskManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ScheduledTaskCreateInput) (api.ScheduledTaskCreateInput, error) {
|
||||
var err error
|
||||
input.VirtualResourceCreateInput, err = stm.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
if !utils.IsInStringArray(input.ScheduledType, []string{api.ST_TYPE_TIMING, api.ST_TYPE_CYCLE}) {
|
||||
return input, httperrors.NewInputParameterError("unkown scheduled type '%s'", input.ScheduledType)
|
||||
}
|
||||
if !utils.IsInStringArray(input.ResourceType, []string{api.ST_RESOURCE_SERVER}) {
|
||||
return input, httperrors.NewInputParameterError("unkown resource type '%s'", input.ResourceType)
|
||||
}
|
||||
if !utils.IsInStringArray(input.Operation, []string{api.ST_RESOURCE_OPERATION_RESTART, api.ST_RESOURCE_OPERATION_STOP, api.ST_RESOURCE_OPERATION_START}) {
|
||||
return input, httperrors.NewInputParameterError("unkown resource operation '%s'", input.Operation)
|
||||
}
|
||||
if !utils.IsInStringArray(input.LabelType, []string{api.ST_LABEL_ID, api.ST_LABEL_TAG}) {
|
||||
return input, httperrors.NewInputParameterError("unkown label type '%s'", input.LabelType)
|
||||
}
|
||||
// check timer or cycletimer
|
||||
if input.ScheduledType == api.ST_TYPE_TIMING {
|
||||
input.Timer, err = checkTimerCreateInput(input.Timer)
|
||||
} else {
|
||||
input.CycleTimer, err = checkCycleTimerCreateInput(input.CycleTimer)
|
||||
}
|
||||
return input, err
|
||||
}
|
||||
|
||||
var wdsCN = []string{"", "一", "二", "三", "四", "五", "六", "日"}
|
||||
var zone = time.FixedZone("GMT", 8*3600)
|
||||
|
||||
func (st *SScheduledTask) TimerDescription() string {
|
||||
format := "2006-01-02 15:04:05"
|
||||
var prefix string
|
||||
timer := st.STimer
|
||||
switch timer.Type {
|
||||
case api.TIMER_TYPE_ONCE:
|
||||
return fmt.Sprintf("单次 %s触发", timer.StartTime.In(zone).Format(format))
|
||||
case api.TIMER_TYPE_DAY:
|
||||
prefix = "每天"
|
||||
case api.TIMER_TYPE_WEEK:
|
||||
wds := timer.GetWeekDays()
|
||||
weekDays := make([]string, len(wds))
|
||||
for i := range wds {
|
||||
weekDays[i] = fmt.Sprintf("星期%s", wdsCN[wds[i]])
|
||||
}
|
||||
prefix = fmt.Sprintf("每周 【%s】", strings.Join(weekDays, "|"))
|
||||
case api.TIMER_TYPE_MONTH:
|
||||
mns := timer.GetMonthDays()
|
||||
monthDays := make([]string, len(mns))
|
||||
for i := range mns {
|
||||
monthDays[i] = fmt.Sprintf("%d号", mns[i])
|
||||
}
|
||||
prefix = fmt.Sprintf("每月 【%s】", strings.Join(monthDays, "|"))
|
||||
}
|
||||
return fmt.Sprintf("%s %2d:%2d触发 有效时间为%s至%s", prefix, timer.Hour, timer.Minute, timer.StartTime.In(zone).Format(format), timer.EndTime.In(zone).Format(format))
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, input apis.PerformEnableInput) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(st, ctx, userCred, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, input apis.PerformDisableInput) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
|
||||
input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(st, ctx, userCred, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
st.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
// add label
|
||||
createFailed := func(reason string) {
|
||||
st.SetStatus(userCred, api.ST_STATUS_CREATE_FAILED, reason)
|
||||
logclient.AddActionLogWithContext(ctx, st, logclient.ACT_CREATE, reason, userCred, false)
|
||||
}
|
||||
labels, _ := data.GetArray("labels")
|
||||
for i := range labels {
|
||||
label, _ := labels[i].GetString()
|
||||
err := ScheduledTaskLabelManager.Attach(ctx, st.Id, label)
|
||||
if err != nil {
|
||||
reason := fmt.Sprintf("unable to attach scheduled task '%s' with '%s'", st.Id, label)
|
||||
createFailed(reason)
|
||||
return
|
||||
}
|
||||
}
|
||||
input := api.ScheduledTaskCreateInput{}
|
||||
err := data.Unmarshal(&input)
|
||||
if err != nil {
|
||||
createFailed(err.Error())
|
||||
return
|
||||
}
|
||||
switch st.ScheduledType {
|
||||
case api.ST_TYPE_TIMING:
|
||||
st.STimer = STimer{
|
||||
Type: api.TIMER_TYPE_ONCE,
|
||||
StartTime: input.Timer.ExecTime,
|
||||
EndTime: input.Timer.ExecTime,
|
||||
NextTime: input.Timer.ExecTime,
|
||||
}
|
||||
case api.ST_TYPE_CYCLE:
|
||||
st.STimer = STimer{
|
||||
Type: input.CycleTimer.CycleType,
|
||||
Minute: input.CycleTimer.Minute,
|
||||
Hour: input.CycleTimer.Hour,
|
||||
StartTime: input.CycleTimer.StartTime,
|
||||
EndTime: input.CycleTimer.EndTime,
|
||||
NextTime: time.Time{},
|
||||
}
|
||||
st.SetWeekDays(input.CycleTimer.WeekDays)
|
||||
st.SetMonthDays(input.CycleTimer.MonthDays)
|
||||
}
|
||||
st.Update(time.Time{})
|
||||
st.Status = api.ST_STATUS_READY
|
||||
st.Enabled = tristate.True
|
||||
st.TimerDesc = st.TimerDescription()
|
||||
err = st.GetModelManager().TableSpec().InsertOrUpdate(ctx, st)
|
||||
if err != nil {
|
||||
createFailed("update itself")
|
||||
return
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, st, logclient.ACT_CREATE, "", userCred, true)
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) ValidateDeleteCondition(ctx context.Context) error {
|
||||
err := st.SVirtualResourceBase.ValidateDeleteCondition(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := st.IsExecuted()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
return httperrors.NewForbiddenError("This scheduled task is being executed now, please try later")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) IsExecuted() (bool, error) {
|
||||
q := ScheduledTaskActivityManager.Query().Equals("status", api.ST_ACTIVITY_STATUS_EXEC).Equals("scheduled_task_id", st.Id)
|
||||
n, err := q.CountWithError()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return n > 0, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) Labels() ([]string, error) {
|
||||
stLabels, err := st.STLabels()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
labels := make([]string, len(stLabels))
|
||||
for i := range labels {
|
||||
labels[i] = stLabels[i].Label
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) STLabels() ([]SScheduledTaskLabel, error) {
|
||||
q := ScheduledTaskLabelManager.Query().Equals("scheduled_task_id", st.Id)
|
||||
labels := make([]SScheduledTaskLabel, 0, 1)
|
||||
err := db.FetchModelObjects(ScheduledTaskLabelManager, q, &labels)
|
||||
return labels, err
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) AllowPerformSetLabels(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ScheduledTaskSetLabelsInput) bool {
|
||||
return st.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, st, "set-labels")
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) PerformSetLabels(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ScheduledTaskSetLabelsInput) (jsonutils.JSONObject, error) {
|
||||
nowLabels, err := st.STLabels()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nowLabelMap := make(map[string]*SScheduledTaskLabel, len(nowLabels))
|
||||
for i := range nowLabels {
|
||||
nowLabelMap[nowLabels[i].Label] = &nowLabels[i]
|
||||
}
|
||||
futureLabelSet := sets.NewString(input.Labels...)
|
||||
var attachs []string
|
||||
var detachs []*SScheduledTaskLabel
|
||||
for label := range futureLabelSet {
|
||||
if _, ok := nowLabelMap[label]; !ok {
|
||||
attachs = append(attachs, label)
|
||||
}
|
||||
}
|
||||
for label, stLable := range nowLabelMap {
|
||||
if !futureLabelSet.Has(label) {
|
||||
detachs = append(detachs, stLable)
|
||||
}
|
||||
}
|
||||
// attach
|
||||
for _, label := range attachs {
|
||||
err := ScheduledTaskLabelManager.Attach(ctx, st.Id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// detach
|
||||
for _, stLabel := range detachs {
|
||||
err := stLabel.Detach(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) AllowPerformTrigger(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ScheduledTaskTriggerInput) bool {
|
||||
return st.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, st, "trigger")
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) PerformTrigger(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ScheduledTaskTriggerInput) (jsonutils.JSONObject, error) {
|
||||
go func() {
|
||||
log.Infof("start to execute scheduled task '%s'", st.Id)
|
||||
err := st.Execute(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("fail to execute scheduled task '%s': %s", st.Id, err.Error())
|
||||
} else {
|
||||
log.Infof("execute scheduled task '%s' successfully", st.Id)
|
||||
}
|
||||
}()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
err := st.SVirtualResourceBase.CustomizeDelete(ctx, userCred, query, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
labels, err := st.STLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range labels {
|
||||
err := labels[i].Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("unbale to delete scheduled task label: %s", err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) Action(ctx context.Context, userCred mcclient.TokenCredential) SAction {
|
||||
session := auth.GetSession(ctx, userCred, "", "")
|
||||
return Action.ResourceOperation(st.ResourceOperation()).Session(session)
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) Execute(ctx context.Context, userCred mcclient.TokenCredential) (err error) {
|
||||
exec, err := st.IsExecuted()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to check if scheduled task is executed")
|
||||
}
|
||||
if exec {
|
||||
_, err := st.NewActivity(ctx, true)
|
||||
return err
|
||||
}
|
||||
sa, err := st.NewActivity(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
over := false
|
||||
defer func() {
|
||||
if !over && err != nil {
|
||||
sa.Fail(err.Error())
|
||||
}
|
||||
}()
|
||||
action := st.Action(ctx, userCred)
|
||||
// Get All Resource
|
||||
labels, err := st.Labels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ids []string
|
||||
switch st.LabelType {
|
||||
case api.ST_LABEL_TAG:
|
||||
f := false
|
||||
limit := 1000
|
||||
opts := options.BaseListOptions{
|
||||
Details: &f,
|
||||
Limit: &limit,
|
||||
Scope: "system",
|
||||
Tags: labels,
|
||||
}
|
||||
res, err := action.List(&opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for id := range res {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
case api.ST_LABEL_ID:
|
||||
ids = labels
|
||||
}
|
||||
|
||||
maxLimit := 20
|
||||
type result struct {
|
||||
id string
|
||||
succeed bool
|
||||
reason string
|
||||
}
|
||||
workerQueue := make(chan struct{}, maxLimit)
|
||||
results := make([]result, len(ids))
|
||||
for i, id := range ids {
|
||||
workerQueue <- struct{}{}
|
||||
go func(n int, id string) {
|
||||
ok, reason := action.Apply(id)
|
||||
results[i] = result{id, ok, reason}
|
||||
<-workerQueue
|
||||
}(i, id)
|
||||
}
|
||||
// wait all finish
|
||||
for i := 0; i < maxLimit; i++ {
|
||||
workerQueue <- struct{}{}
|
||||
}
|
||||
failedReasons := make([]string, 0, 1)
|
||||
succeedIds := make([]string, 0, 1)
|
||||
for _, ret := range results {
|
||||
if ret.succeed {
|
||||
succeedIds = append(succeedIds, ret.id)
|
||||
continue
|
||||
}
|
||||
failedReasons = append(failedReasons, fmt.Sprintf("\t%s: %s", ret.id, ret.reason))
|
||||
}
|
||||
if len(failedReasons) == 0 {
|
||||
sa.Succeed()
|
||||
return nil
|
||||
}
|
||||
if len(failedReasons) == len(ids) {
|
||||
reason := fmt.Sprintf("All %ss %s failed:\n%s", st.ResourceType, st.Operation, strings.Join(failedReasons, ";\n"))
|
||||
sa.Fail(reason)
|
||||
return nil
|
||||
}
|
||||
reason := fmt.Sprintf("Some %ss %s successfully:\n\t%s\n\n. Some %ss %s failed:\n%s", st.ResourceType, st.Operation, strings.Join(succeedIds, ";"), st.ResourceType, st.Operation, strings.Join(failedReasons, ";\n"))
|
||||
sa.PartFail(reason)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) NewActivity(ctx context.Context, reject bool) (*SScheduledTaskActivity, error) {
|
||||
now := time.Now()
|
||||
sa := &SScheduledTaskActivity{
|
||||
StartTime: now,
|
||||
}
|
||||
sa.Status = api.ST_ACTIVITY_STATUS_EXEC
|
||||
sa.ScheduledTaskId = st.Id
|
||||
if reject {
|
||||
sa.Status = api.ST_ACTIVITY_STATUS_REJECT
|
||||
sa.EndTime = now
|
||||
sa.Reason = "This Scheduled Task is being executed now"
|
||||
}
|
||||
err := ScheduledTaskActivityManager.TableSpec().Insert(ctx, sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sa.SetModelManager(ScheduledTaskActivityManager, sa)
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
func (st *SScheduledTask) ResourceOperation() ResourceOperation {
|
||||
return ResourceOperationMap[fmt.Sprintf("%s.%s", st.ResourceType, st.Operation)]
|
||||
}
|
||||
|
||||
type STimeScope struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Median time.Time
|
||||
}
|
||||
|
||||
func (stm *SScheduledTaskManager) timeScope(median time.Time, interval time.Duration) STimeScope {
|
||||
ri := interval / 2
|
||||
return STimeScope{
|
||||
Start: median.Add(-ri),
|
||||
End: median.Add(ri),
|
||||
Median: median,
|
||||
}
|
||||
}
|
||||
|
||||
var timerQueue = make(chan struct{}, cop.Options.ScheduledTaskQueueSize)
|
||||
|
||||
func (stm *SScheduledTaskManager) Timer(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
if len(timerQueue) == 0 {
|
||||
timerQueue = make(chan struct{}, cop.Options.ScheduledTaskQueueSize)
|
||||
}
|
||||
// 60 is for fault tolerance
|
||||
interval := 60 + 30
|
||||
timeScope := stm.timeScope(time.Now(), time.Duration(interval)*time.Second)
|
||||
q := stm.Query().Equals("status", api.ST_STATUS_READY).Equals("enabled", true).LT("next_time", timeScope.End).IsFalse("is_expired")
|
||||
sts := make([]SScheduledTask, 0, 5)
|
||||
err := db.FetchModelObjects(stm, q, &sts)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjects error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
log.Debugf("timeScope: start: %s, end: %s", timeScope.Start, timeScope.End)
|
||||
for i := range sts {
|
||||
st := sts[i]
|
||||
timerQueue <- struct{}{}
|
||||
go func(ctx context.Context) {
|
||||
defer func() {
|
||||
<-timerQueue
|
||||
}()
|
||||
if st.NextTime.Before(timeScope.Start) {
|
||||
// For unknown reasons, the scalingTimer did not execute at the specified time
|
||||
st.Update(timeScope.Start)
|
||||
// scalingTimer should not exec for now.
|
||||
if st.NextTime.After(timeScope.End) || st.IsExpired {
|
||||
err = stm.TableSpec().InsertOrUpdate(ctx, &st)
|
||||
if err != nil {
|
||||
log.Errorf("update Scheduled task whose id is %s error: %s", st.Id, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
err := st.Execute(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("unable to execute scheduled task '%s'", st.Id)
|
||||
}
|
||||
st.Update(timeScope.End)
|
||||
err = stm.TableSpec().InsertOrUpdate(ctx, &st)
|
||||
if err != nil {
|
||||
log.Errorf("update Scheduled task whose id is %s error: %s", st.Id, err.Error())
|
||||
}
|
||||
}(ctx)
|
||||
}
|
||||
// wait all finish
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(ResourceServer, modules.Servers.ResourceManager)
|
||||
}
|
||||
|
||||
// Modules describe the correspondence between Resource and modulebase.ResourceManager,
|
||||
// which is equivalent to onecloud resource client.
|
||||
var Modules = make(map[Resource]modulebase.ResourceManager)
|
||||
|
||||
// Every Resource should call Register to register their modulebase.ResourceManager.
|
||||
func Register(resource Resource, manager modulebase.ResourceManager) {
|
||||
Modules[resource] = manager
|
||||
}
|
||||
|
||||
// Resoruce describe a onecloud resource, such as:
|
||||
type Resource string
|
||||
|
||||
const (
|
||||
ResourceServer Resource = api.ST_RESOURCE_SERVER
|
||||
)
|
||||
|
||||
// ResourceOperation describe the operation for onecloud resource like create, update, delete and so on.
|
||||
type ResourceOperation struct {
|
||||
Resource Resource
|
||||
Operation string
|
||||
StatusSuccess []string
|
||||
Fail []ResourceOperationFail
|
||||
}
|
||||
|
||||
type ResourceOperationFail struct {
|
||||
Status string
|
||||
LogEvent string
|
||||
}
|
||||
|
||||
// It is clearer to write each ResourceOperation as a constant
|
||||
var (
|
||||
ServerStart = ResourceOperation{
|
||||
Resource: ResourceServer,
|
||||
Operation: api.ST_RESOURCE_OPERATION_START,
|
||||
StatusSuccess: []string{api.VM_RUNNING},
|
||||
Fail: []ResourceOperationFail{
|
||||
{api.VM_START_FAILED, db.ACT_START_FAIL},
|
||||
},
|
||||
}
|
||||
ServerStop = ResourceOperation{
|
||||
Resource: ResourceServer,
|
||||
Operation: api.ST_RESOURCE_OPERATION_STOP,
|
||||
StatusSuccess: []string{api.VM_READY},
|
||||
Fail: []ResourceOperationFail{
|
||||
{api.VM_STOP_FAILED, db.ACT_STOP_FAIL},
|
||||
},
|
||||
}
|
||||
ServerRestart = ResourceOperation{
|
||||
Resource: ResourceServer,
|
||||
Operation: api.ST_RESOURCE_OPERATION_RESTART,
|
||||
StatusSuccess: []string{api.VM_RUNNING},
|
||||
Fail: []ResourceOperationFail{
|
||||
{api.VM_START_FAILED, db.ACT_START_FAIL},
|
||||
{api.VM_STOP_FAILED, db.ACT_STOP_FAIL},
|
||||
},
|
||||
}
|
||||
ResourceOperationMap = map[string]ResourceOperation{
|
||||
fmt.Sprintf("%s.%s", ResourceServer, api.ST_RESOURCE_OPERATION_START): ServerStart,
|
||||
fmt.Sprintf("%s.%s", ResourceServer, api.ST_RESOURCE_OPERATION_STOP): ServerStop,
|
||||
fmt.Sprintf("%s.%s", ResourceServer, api.ST_RESOURCE_OPERATION_RESTART): ServerRestart,
|
||||
}
|
||||
)
|
||||
|
||||
// Action itself is meaningless, a meaningful Action is generated by
|
||||
// calling Resource, Operation, Session and DefaultParams.
|
||||
// A example:
|
||||
// Action.ResourceOperation(ServerStart).Session(...).Apply(...)
|
||||
var Action = SAction{timeout: 5 * time.Minute}
|
||||
|
||||
// SAction encapsulates action to for onecloud resources
|
||||
type SAction struct {
|
||||
operation ResourceOperation
|
||||
session *mcclient.ClientSession
|
||||
defautParams *jsonutils.JSONDict
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (r SAction) ResourceOperation(oper ResourceOperation) SAction {
|
||||
r.operation = oper
|
||||
return r
|
||||
}
|
||||
|
||||
func (r SAction) Session(session *mcclient.ClientSession) SAction {
|
||||
r.session = session
|
||||
return r
|
||||
}
|
||||
|
||||
func (r SAction) Timeout(time time.Duration) SAction {
|
||||
r.timeout = time
|
||||
return r
|
||||
}
|
||||
|
||||
func (r SAction) DefaultParams(dict *jsonutils.JSONDict) SAction {
|
||||
r.defautParams = dict
|
||||
return r
|
||||
}
|
||||
|
||||
func (r SAction) List(opts *options.BaseListOptions) (map[string]string, error) {
|
||||
resourceManager, ok := Modules[r.operation.Resource]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("no such resource '%s' in Modules", r.operation.Resource)
|
||||
}
|
||||
params, err := options.ListStructToParams(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret, err := resourceManager.List(r.session, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]string, len(ret.Data))
|
||||
for i := range ret.Data {
|
||||
id, _ := ret.Data[i].GetString("id")
|
||||
name, _ := ret.Data[i].GetString("name")
|
||||
out[id] = name
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r SAction) Apply(id string) (success bool, failReason string) {
|
||||
success = true
|
||||
resourceManager, ok := Modules[r.operation.Resource]
|
||||
if !ok {
|
||||
return false, fmt.Sprintf("no such resource '%s' in Modules", r.operation.Resource)
|
||||
}
|
||||
var requestFunc func(session *mcclient.ClientSession, id string, params *jsonutils.JSONDict) error
|
||||
|
||||
action := utils.CamelSplit(r.operation.Operation, "-")
|
||||
requestFunc = func(session *mcclient.ClientSession, id string, params *jsonutils.JSONDict) error {
|
||||
if params == nil {
|
||||
params = jsonutils.NewDict()
|
||||
}
|
||||
_, err := resourceManager.PerformAction(session, id, action, params)
|
||||
return err
|
||||
}
|
||||
err := requestFunc(r.session, id, r.defautParams)
|
||||
if err != nil {
|
||||
clientErr, _ := err.(*httputils.JSONClientError)
|
||||
return false, clientErr.Details
|
||||
}
|
||||
if len(r.operation.StatusSuccess) == 0 {
|
||||
return true, ""
|
||||
}
|
||||
// wait for status
|
||||
timer := time.NewTimer(r.timeout)
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
timer.Stop()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
default:
|
||||
ret, e := resourceManager.GetSpecific(r.session, id, "status", nil)
|
||||
if e != nil {
|
||||
log.Errorf("fail to exec resouce(%s.%s).GetStatus: %s", r.operation.Resource, id, e.Error())
|
||||
<-ticker.C
|
||||
continue
|
||||
}
|
||||
status, _ := ret.GetString("status")
|
||||
if utils.IsInStringArray(status, r.operation.StatusSuccess) {
|
||||
return
|
||||
}
|
||||
for _, fail := range r.operation.Fail {
|
||||
if status != fail.Status {
|
||||
continue
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(id), "obj_id")
|
||||
params.Add(jsonutils.NewStringArray([]string{fail.LogEvent}), "action")
|
||||
params.Add(jsonutils.NewInt(1), "limit")
|
||||
events, err := modules.Logs.List(r.session, params)
|
||||
if err != nil {
|
||||
log.Errorf("Logs.List failed: %s", err.Error())
|
||||
<-ticker.C
|
||||
continue
|
||||
}
|
||||
if len(events.Data) == 0 {
|
||||
log.Errorf("These is no opslog about action '%s' for %s.%s: %s", fail.LogEvent, r.operation.Resource, id, err.Error())
|
||||
<-ticker.C
|
||||
continue
|
||||
}
|
||||
reason, _ := events.Data[0].GetString("notes")
|
||||
return false, reason
|
||||
}
|
||||
<-ticker.C
|
||||
case <-timer.C:
|
||||
log.Errorf("timeout(%s) to exec resource(%s.%s).%s", r.timeout.String(), r.operation.Resource, id, r.operation.Operation)
|
||||
return false, "timeout"
|
||||
}
|
||||
}
|
||||
}
|
||||
173
pkg/compute/models/scheduledtask_activity.go
Normal file
173
pkg/compute/models/scheduledtask_activity.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
var ScheduledTaskActivityManager *SScheduledTaskActivityManager
|
||||
|
||||
func init() {
|
||||
ScheduledTaskActivityManager = &SScheduledTaskActivityManager{
|
||||
SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
|
||||
SScheduledTaskActivity{},
|
||||
"scheduledtaskactivities_tbl",
|
||||
"scheduledtaskactivity",
|
||||
"scheduledtaskactivities",
|
||||
),
|
||||
}
|
||||
ScheduledTaskActivityManager.SetVirtualObject(ScheduledTaskActivityManager)
|
||||
}
|
||||
|
||||
// +onecloud:swagger-gen-model-singular=scheduledtaskactivity
|
||||
// +onecloud:swagger-gen-model-singular=scheduledtaskactivities
|
||||
type SScheduledTaskActivityManager struct {
|
||||
db.SStatusStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
type SScheduledTaskActivity struct {
|
||||
db.SStatusStandaloneResourceBase
|
||||
ScheduledTaskId string `width:"36" charset:"ascii" nullable:"true" list:"user" index:"true"`
|
||||
StartTime time.Time `list:"user"`
|
||||
EndTime time.Time `list:"user"`
|
||||
Reason string `charset:"utf8" list:"user"`
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) InitializeData() error {
|
||||
sas := make([]SScheduledTaskActivity, 0, 10)
|
||||
q := ScheduledTaskActivityManager.Query().Equals("status", api.ST_ACTIVITY_STATUS_EXEC)
|
||||
err := db.FetchModelObjects(ScheduledTaskActivityManager, q, &sas)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range sas {
|
||||
err := sas[i].Fail("As the service restarts, the status becomes unknown")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
||||
return sam.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ScheduledTaskActivityListInput) (*sqlchemy.SQuery, error) {
|
||||
return sam.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput)
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.ScheduledTaskActivityDetails {
|
||||
rows := make([]api.ScheduledTaskActivityDetails, len(objs))
|
||||
statusRows := sam.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
for i := range rows {
|
||||
rows[i].StatusStandaloneResourceDetails = statusRows[i]
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (api.ScheduledTaskActivityDetails, error) {
|
||||
return api.ScheduledTaskActivityDetails{}, nil
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.ScheduledTaskActivityListInput) (*sqlchemy.SQuery, error) {
|
||||
q, err := sam.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StatusStandaloneResourceListInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Desc("start_time").Desc("end_time")
|
||||
if len(input.ScheduledTask) == 0 {
|
||||
return nil, httperrors.NewInputParameterError("need scheduled task")
|
||||
}
|
||||
task, err := ScheduledTaskManager.FetchByIdOrName(userCred, input.ScheduledTask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Equals("scheduled_task_id", task.GetId())
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) NamespaceScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeProject
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) ResourceScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeProject
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) FileterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
|
||||
if owner != nil {
|
||||
switch scope {
|
||||
case rbacutils.ScopeProject, rbacutils.ScopeDomain:
|
||||
scheduledTaskQ := ScheduledTaskManager.Query("id", "domain_id").SubQuery()
|
||||
q = q.Join(scheduledTaskQ, sqlchemy.Equals(q.Field("scheduled_task_id"), scheduledTaskQ.Field("id")))
|
||||
q = q.Filter(sqlchemy.Equals(scheduledTaskQ.Field("domain_id"), owner.GetProjectDomainId()))
|
||||
}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (sam *SScheduledTaskActivityManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
|
||||
return db.FetchDomainInfo(ctx, data)
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) GetOwnerId() mcclient.IIdentityProvider {
|
||||
obj, _ := ScheduledTaskManager.FetchById(sa.ScheduledTaskId)
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
return obj.GetOwnerId()
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) SetResult(status, reason string) error {
|
||||
_, err := db.Update(sa, func() error {
|
||||
sa.Status = status
|
||||
sa.Reason = reason
|
||||
sa.EndTime = time.Now()
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) Fail(reason string) error {
|
||||
return sa.SetResult(api.ST_ACTIVITY_STATUS_FAILED, reason)
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) Succeed() error {
|
||||
return sa.SetResult(api.ST_ACTIVITY_STATUS_SUCCEED, "")
|
||||
}
|
||||
|
||||
func (sa *SScheduledTaskActivity) PartFail(reason string) error {
|
||||
return sa.SetResult(api.ST_ACTIVITY_STATUS_PART_SUCCEED, reason)
|
||||
}
|
||||
61
pkg/compute/models/scheduledtask_label.go
Normal file
61
pkg/compute/models/scheduledtask_label.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
var ScheduledTaskLabelManager *SScheduledTaskLabelManager
|
||||
|
||||
func init() {
|
||||
db.InitManager(func() {
|
||||
ScheduledTaskLabelManager = &SScheduledTaskLabelManager{
|
||||
SVirtualJointResourceBaseManager: db.NewVirtualJointResourceBaseManager(
|
||||
SScheduledTaskLabel{},
|
||||
"scheduledtasklabels_tbl",
|
||||
"scheduledtasklabel",
|
||||
"scheduledtasklabels",
|
||||
ScheduledTaskManager,
|
||||
GuestManager,
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type SScheduledTaskLabelManager struct {
|
||||
db.SVirtualJointResourceBaseManager
|
||||
}
|
||||
|
||||
type SScheduledTaskLabel struct {
|
||||
db.SVirtualJointResourceBase
|
||||
ScheduledTaskId string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
Label string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
}
|
||||
|
||||
func (slm *SScheduledTaskLabelManager) Attach(ctx context.Context, taskId, label string) error {
|
||||
sl := &SScheduledTaskLabel{
|
||||
ScheduledTaskId: taskId,
|
||||
Label: label,
|
||||
}
|
||||
return slm.TableSpec().Insert(ctx, sl)
|
||||
}
|
||||
|
||||
func (sl *SScheduledTaskLabel) Detach(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return db.DetachJoint(ctx, userCred, sl)
|
||||
}
|
||||
191
pkg/compute/models/timer.go
Normal file
191
pkg/compute/models/timer.go
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/bitmap"
|
||||
)
|
||||
|
||||
type STimer struct {
|
||||
// Cycle type
|
||||
Type string `width:"8" charset:"ascii"`
|
||||
// 0-59
|
||||
Minute int `nullable:"false"`
|
||||
// 0-23
|
||||
Hour int `nullable:"false"`
|
||||
// 0-7 1 is Monday 0 is unlimited
|
||||
WeekDays uint8 `nullable:"false"`
|
||||
// 0-31 0 is unlimited
|
||||
MonthDays uint32 `nullable:"false"`
|
||||
|
||||
// StartTime represent the start time of this timer
|
||||
StartTime time.Time
|
||||
// EndTime represent deadline of this timer
|
||||
EndTime time.Time
|
||||
// NextTime represent the time timer should bell
|
||||
NextTime time.Time `index:"true"`
|
||||
IsExpired bool
|
||||
}
|
||||
|
||||
// Update will update the SScalingTimer
|
||||
func (st *STimer) Update(now time.Time) {
|
||||
if now.IsZero() {
|
||||
now = time.Now()
|
||||
}
|
||||
if !now.Before(st.EndTime) {
|
||||
st.IsExpired = true
|
||||
return
|
||||
}
|
||||
if now.Before(st.StartTime) {
|
||||
now = st.StartTime
|
||||
}
|
||||
if !st.NextTime.Before(now) {
|
||||
return
|
||||
}
|
||||
|
||||
newNextTime := time.Date(now.Year(), now.Month(), now.Day(), st.Hour, st.Minute, 0, 0, now.Location())
|
||||
if now.After(newNextTime) {
|
||||
newNextTime = newNextTime.AddDate(0, 0, 1)
|
||||
}
|
||||
switch {
|
||||
case st.WeekDays != 0:
|
||||
// week
|
||||
nowDay, weekdays := int(newNextTime.Weekday()), st.GetWeekDays()
|
||||
if nowDay == 0 {
|
||||
nowDay = 7
|
||||
}
|
||||
|
||||
// weekdays[0]+7 is for the case that all time nodes has been missed in this week
|
||||
weekdays = append(weekdays, weekdays[0]+7)
|
||||
index := sort.SearchInts(weekdays, nowDay)
|
||||
newNextTime = newNextTime.AddDate(0, 0, weekdays[index]-nowDay)
|
||||
case st.MonthDays != 0:
|
||||
// month
|
||||
monthdays := st.GetMonthDays()
|
||||
suitTime := newNextTime
|
||||
for {
|
||||
day := suitTime.Day()
|
||||
index := sort.SearchInts(monthdays, day)
|
||||
if index == len(monthdays) || monthdays[index] > st.MonthDaySum(suitTime) {
|
||||
// set suitTime as the first day of next month
|
||||
suitTime = suitTime.AddDate(0, 1, -suitTime.Day()+1)
|
||||
continue
|
||||
}
|
||||
newNextTime = time.Date(suitTime.Year(), suitTime.Month(), monthdays[index], suitTime.Hour(),
|
||||
suitTime.Minute(), 0, 0, suitTime.Location())
|
||||
break
|
||||
}
|
||||
default:
|
||||
// day
|
||||
|
||||
}
|
||||
log.Debugf("The final NextTime: %s", newNextTime)
|
||||
st.NextTime = newNextTime
|
||||
if st.NextTime.After(st.EndTime) {
|
||||
st.IsExpired = true
|
||||
}
|
||||
}
|
||||
|
||||
// MonthDaySum calculate the number of month's days
|
||||
func (st *STimer) MonthDaySum(t time.Time) int {
|
||||
year, month := t.Year(), t.Month()
|
||||
monthDays := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
if month != 2 {
|
||||
return monthDays[2]
|
||||
}
|
||||
if year%4 != 0 || (year%100 == 0 && year%400 != 0) {
|
||||
return 28
|
||||
}
|
||||
return 29
|
||||
}
|
||||
|
||||
func (st *STimer) GetWeekDays() []int {
|
||||
return bitmap.Uint2IntArray(uint32(st.WeekDays))
|
||||
}
|
||||
|
||||
func (st *STimer) GetMonthDays() []int {
|
||||
return bitmap.Uint2IntArray(st.MonthDays)
|
||||
}
|
||||
|
||||
func (st *STimer) SetWeekDays(days []int) {
|
||||
st.WeekDays = uint8(bitmap.IntArray2Uint(days))
|
||||
}
|
||||
|
||||
func (st *STimer) SetMonthDays(days []int) {
|
||||
st.MonthDays = bitmap.IntArray2Uint(days)
|
||||
}
|
||||
|
||||
func (st *STimer) TimerDetails() api.TimerDetails {
|
||||
return api.TimerDetails{ExecTime: st.EndTime}
|
||||
}
|
||||
|
||||
func (st *STimer) CycleTimerDetails() api.CycleTimerDetails {
|
||||
out := api.CycleTimerDetails{
|
||||
Minute: st.Minute,
|
||||
Hour: st.Hour,
|
||||
WeekDays: st.GetWeekDays(),
|
||||
MonthDays: st.GetMonthDays(),
|
||||
StartTime: st.StartTime,
|
||||
EndTime: st.EndTime,
|
||||
CycleType: st.Type,
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func checkTimerCreateInput(in api.TimerCreateInput) (api.TimerCreateInput, error) {
|
||||
now := time.Now()
|
||||
if now.After(in.ExecTime) {
|
||||
return in, fmt.Errorf("exec_time is earlier than now")
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
|
||||
func checkCycleTimerCreateInput(in api.CycleTimerCreateInput) (api.CycleTimerCreateInput, error) {
|
||||
now := time.Now()
|
||||
if in.Minute < 0 || in.Minute > 59 {
|
||||
return in, fmt.Errorf("minute should between 0 and 59")
|
||||
}
|
||||
if in.Hour < 0 || in.Hour > 23 {
|
||||
return in, fmt.Errorf("hour should between 0 and 23")
|
||||
}
|
||||
switch in.CycleType {
|
||||
case api.TIMER_TYPE_DAY:
|
||||
in.WeekDays = []int{}
|
||||
in.MonthDays = []int{}
|
||||
case api.TIMER_TYPE_WEEK:
|
||||
if len(in.WeekDays) == 0 {
|
||||
return in, fmt.Errorf("week_days should not be empty")
|
||||
}
|
||||
in.MonthDays = []int{}
|
||||
case api.TIMER_TYPE_MONTH:
|
||||
if len(in.MonthDays) == 0 {
|
||||
return in, fmt.Errorf("month_days should not be empty")
|
||||
}
|
||||
in.WeekDays = []int{}
|
||||
default:
|
||||
return in, fmt.Errorf("unkown cycle type %s", in.CycleType)
|
||||
}
|
||||
if now.After(in.EndTime) {
|
||||
return in, fmt.Errorf("end_time is earlier than now")
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
@@ -136,6 +136,8 @@ type ComputeOptions struct {
|
||||
|
||||
GuestTemplateCheckInterval int `help:"interval between two consecutive inspections of Guest Template in hour unit" default:"12"`
|
||||
|
||||
ScheduledTaskQueueSize int `help:"the maximum number of scheduled tasks that are being executed simultaneously" default:"100"`
|
||||
|
||||
SCapabilityOptions
|
||||
SASControllerOptions
|
||||
common_options.CommonOptions
|
||||
|
||||
@@ -88,6 +88,8 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.ScalingAlarmManager,
|
||||
models.ScalingGroupGuestManager,
|
||||
models.ScalingGroupNetworkManager,
|
||||
|
||||
models.ScheduledTaskLabelManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
@@ -181,6 +183,9 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.ScalingActivityManager,
|
||||
models.PolicyDefinitionManager,
|
||||
models.PolicyAssignmentManager,
|
||||
|
||||
models.ScheduledTaskManager,
|
||||
models.ScheduledTaskActivityManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -145,6 +145,8 @@ func StartService() {
|
||||
cron.AddJobEveryFewDays("StorageSnapshotsRecycle", 1, 2, 0, 0, models.StorageManager.StorageSnapshotsRecycle, false)
|
||||
|
||||
cron.AddJobEveryFewHour("InspectAllTemplate", 1, 0, 0, models.GuestTemplateManager.InspectAllTemplate, true)
|
||||
|
||||
cron.AddJobAtIntervalsWithStartRun("ScheduledTaskCheck", time.Duration(60)*time.Second, models.ScheduledTaskManager.Timer, true)
|
||||
go cron.Start2(ctx, electObj)
|
||||
|
||||
// init auto scaling controller
|
||||
|
||||
@@ -194,6 +194,7 @@ func (self *GuestDeleteTask) OnSyncConfigComplete(ctx context.Context, obj db.IS
|
||||
log.Debugf("XXXXXXX Do guest pending delete... XXXXXXX")
|
||||
// pending detach
|
||||
guest.PendingDetachScalingGroup()
|
||||
guest.DetachScheduledTask(ctx, self.UserCred)
|
||||
guestStatus, _ := self.Params.GetString("guest_status")
|
||||
if !utils.IsInStringArray(guestStatus, []string{
|
||||
api.VM_SCHEDULE_FAILED, api.VM_NETWORK_FAILED, api.VM_DISK_FAILED,
|
||||
|
||||
@@ -586,6 +586,7 @@ func (asc *SASController) checkAllServer(session *mcclient.ClientSession, guestI
|
||||
ret, e := modules.Servers.GetSpecific(session, id, "status", nil)
|
||||
if e != nil {
|
||||
log.Errorf("Servers.GetSpecific failed: %s", e)
|
||||
<-ticker.C
|
||||
continue
|
||||
}
|
||||
log.Debugf("ret from GetSpecific: %s", ret.String())
|
||||
|
||||
33
pkg/mcclient/modules/mod_scheduledtask.go
Normal file
33
pkg/mcclient/modules/mod_scheduledtask.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 modules
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
|
||||
var (
|
||||
ScheduledTask modulebase.ResourceManager
|
||||
ScheduledTaskActivity modulebase.ResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
ScheduledTask = NewComputeManager("scheduledtask", "scheduledtasks",
|
||||
[]string{"ID", "Name", "Scheduled_Type", "Timer", "Cycle_Timer", "Resource_Type", "Operation", "Label_Type", "Labels", "Timer_Desc"}, []string{},
|
||||
)
|
||||
ScheduledTaskActivity = NewComputeManager("scheudledtaskactivity", "scheduledtaskactivities",
|
||||
[]string{"ID", "Status", "Scheduled_Task_Id", "Start_Time", "End_Time", "Reason"}, []string{},
|
||||
)
|
||||
registerCompute(&ScheduledTask)
|
||||
registerCompute(&ScheduledTaskActivity)
|
||||
}
|
||||
Reference in New Issue
Block a user