mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #11424 from rainzm/notify/domain_subs
Notify managed by domain admin
This commit is contained in:
@@ -3,4 +3,7 @@
|
||||
{{- if $d.brand -}}
|
||||
{{ $d.brand }}平台
|
||||
{{- end -}}
|
||||
{{ .resource_type_display }}{{ $d.name }}已经成功{{ .action_display }}
|
||||
{{ .resource_type_display }}{{ $d.name }}已经{{ .action_display }}{{.result_display}}
|
||||
{{- if eq .result "failed" -}}
|
||||
,请尽快前往控制台进行处理
|
||||
{{- end -}}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{{- $d := .resource_details -}}
|
||||
您在{{ $d.project }}项目的
|
||||
{{- if $d.brand -}}
|
||||
{{ $d.brand }}平台
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.private_dns -}}
|
||||
,内网地址为{{ $d.private_dns }}:{{ $d.private_connect_port }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.public_dns -}}
|
||||
,外网地址为{{ $d.public_dns }}:{{ $d.public_connect_port }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.ips -}}
|
||||
,IP地址为{{ $d.ips }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.ip_addr -}}
|
||||
,IP地址为{{ $d.ip_addr }}
|
||||
{{- end -}}
|
||||
{{ .resource_type_display }}{{ $d.name }}已经删除成功。
|
||||
@@ -1,2 +1,5 @@
|
||||
{{- $d := .resource_details -}}
|
||||
Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been {{ .action_display }} successfully
|
||||
Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been {{ .action_display }} {{ .result_display }}
|
||||
{{- if eq .result "failed" -}}
|
||||
. And please go to the console as soon as possible to process.
|
||||
{{- end -}}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{{- $d := .resource_details -}}
|
||||
Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }}
|
||||
{{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been deleted successfully
|
||||
{{- if $d.private_dns -}}
|
||||
, the intranet address is {{ $d.private_dns }}:{{ $d.private_connect_port }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.public_dns -}}
|
||||
, the internet address is {{ $d.public_dns }}:{{ $d.public_connect_port }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if and $d.address_type $d.address -}}
|
||||
, the service address is {{ $d.address_type }}{{ $d.address }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.ips -}}
|
||||
, the IP address is {{ $d.ips }}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $d.ip_addr -}}
|
||||
, the IP address is {{ $d.ip_addr }}
|
||||
{{- end -}}
|
||||
@@ -2,4 +2,4 @@
|
||||
{{- if $d.project -}}
|
||||
{{ $d.project }}项目的
|
||||
{{- end -}}
|
||||
{{ .resource_type_display }}{{ $d.name }}{{ .action_display }}成功
|
||||
{{ .resource_type_display }}{{ $d.name }}{{ .action_display }}{{ .result_display }}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
{{- $d := .resource_details -}}
|
||||
The {{ .resource_type_display }} {{ .Name }} {{ if $d.project }} in poject {{ $d.project }} {{ end -}} {{ .action_display }} successfully
|
||||
The {{ .resource_type_display }} {{ .Name }} {{ if $d.project }} in poject {{ $d.project }} {{ end -}} {{ .action_display }} {{ .result_display }}
|
||||
|
||||
@@ -15,121 +15,16 @@
|
||||
package notifyv2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/notify"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ConfigListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
R(&ConfigListOptions{}, "notify-config-list", "List notify config", func(s *mcclient.ClientSession, args *ConfigListOptions) error {
|
||||
params, err := options.ListStructToParams(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.NotifyConfig.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.NotifyConfig.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
type ConfigCreateOptions struct {
|
||||
TYPE string `help:"Type contact config"`
|
||||
Configs []string `help:"Config content, format: 'key:value'"`
|
||||
}
|
||||
R(&ConfigCreateOptions{}, "notify-config-create", "Create notify config", func(s *mcclient.ClientSession, args *ConfigCreateOptions) error {
|
||||
configs := jsonutils.NewDict()
|
||||
for _, kv := range args.Configs {
|
||||
index := strings.IndexByte(kv, ':')
|
||||
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("type", jsonutils.NewString(args.TYPE))
|
||||
params.Set("content", configs)
|
||||
ret, err := modules.NotifyConfig.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
R(&ConfigCreateOptions{}, "notify-config-update", "Update notify config", func(s *mcclient.ClientSession, args *ConfigCreateOptions) error {
|
||||
configs := jsonutils.NewDict()
|
||||
for _, kv := range args.Configs {
|
||||
index := strings.IndexByte(kv, ':')
|
||||
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("content", configs)
|
||||
|
||||
id, err := configIdFromType(s, args.TYPE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := modules.NotifyConfig.Update(s, id, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
type ConfigOptions struct {
|
||||
TYPE string `help:"Type contact config"`
|
||||
}
|
||||
R(&ConfigOptions{}, "notify-config-delete", "Delete notify config", func(s *mcclient.ClientSession, args *ConfigOptions) error {
|
||||
id, err := configIdFromType(s, args.TYPE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := modules.NotifyConfig.Delete(s, id, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
R(&ConfigOptions{}, "notify-config-show", "Show notify config", func(s *mcclient.ClientSession, args *ConfigOptions) error {
|
||||
listParams := jsonutils.NewDict()
|
||||
listParams.Set("type", jsonutils.NewString(args.TYPE))
|
||||
list, err := modules.NotifyConfig.List(s, listParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := list.Data[0]
|
||||
printObject(data)
|
||||
return nil
|
||||
})
|
||||
type ConfigGetTypesOptions struct {
|
||||
Robot string `json:"robot" choices:"yes|no|only"`
|
||||
}
|
||||
R(&ConfigGetTypesOptions{}, "notify-config-get-types", "Get all Config types", func(s *mcclient.ClientSession, args *ConfigGetTypesOptions) error {
|
||||
param := jsonutils.Marshal(args)
|
||||
result, err := modules.NotifyReceiver.PerformClassAction(s, "get-types", param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func configIdFromType(s *mcclient.ClientSession, t string) (string, error) {
|
||||
listParams := jsonutils.NewDict()
|
||||
listParams.Set("type", jsonutils.NewString(t))
|
||||
list, err := modules.NotifyConfig.List(s, listParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
id, err := list.Data[0].GetString("id")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
cmd := shell.NewResourceCmd(&modules.NotifyConfig).WithKeyword("notify-config")
|
||||
cmd.List(new(options.ConfigListOptions))
|
||||
cmd.Create(new(options.ConfigCreateOptions))
|
||||
cmd.Update(new(options.ConfigUpdateOptions))
|
||||
cmd.Show(new(options.ConfigOptions))
|
||||
cmd.Delete(new(options.ConfigOptions))
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
func init() {
|
||||
type NotificationCreateInput struct {
|
||||
Receivers []string `help:"ID or Name of Receiver"`
|
||||
Robots []string `help:"ID or Name of Robot"`
|
||||
ContactType string `help:"Contact type of receiver"`
|
||||
TOPIC string `help:"Topic"`
|
||||
Priority string `help:"Priority"`
|
||||
@@ -41,6 +42,7 @@ func init() {
|
||||
if args.Oldsdk {
|
||||
msg := notify.SNotifyMessage{
|
||||
Uid: args.Receivers,
|
||||
Robots: args.Robots,
|
||||
ContactType: notify.TNotifyChannel(args.ContactType),
|
||||
Topic: args.TOPIC,
|
||||
Priority: notify.TNotifyPriority(args.Priority),
|
||||
@@ -55,6 +57,7 @@ func init() {
|
||||
} else {
|
||||
input := api.NotificationCreateInput{
|
||||
Receivers: args.Receivers,
|
||||
Robots: args.Robots,
|
||||
ContactType: args.ContactType,
|
||||
Topic: args.TOPIC,
|
||||
Priority: args.Priority,
|
||||
|
||||
@@ -31,5 +31,7 @@ func init() {
|
||||
cmd.Perform("disable", new(options.ReceiverOptions))
|
||||
cmd.Perform("trigger-verify", new(options.ReceiverTriggerVerifyOptions))
|
||||
cmd.Perform("verify", new(options.ReceiverVerifyOptions))
|
||||
cmd.Perform("enable-contact-type", new(options.ReceiverEnableContactTypeInput))
|
||||
cmd.PerformClass("intellij-get", new(options.ReceiverIntellijGetOptions))
|
||||
cmd.PerformClass("get-types", new(options.ReceiverGetTypeOptions))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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 notifyv2
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/notify"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.NotifyRobot).WithKeyword("notify-robot")
|
||||
cmd.List(new(options.RobotListOptions))
|
||||
cmd.Create(new(options.RobotCreateOptions))
|
||||
cmd.Update(new(options.RobotUpdateOptions))
|
||||
cmd.Show(new(options.RobotOptions))
|
||||
cmd.Delete(new(options.RobotOptions))
|
||||
cmd.Perform("enable", new(options.RobotOptions))
|
||||
cmd.Perform("disable", new(options.RobotOptions))
|
||||
}
|
||||
@@ -21,10 +21,15 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.NotifySubscription).WithKeyword("notify-subscription")
|
||||
cmd.List(new(notify.SubscriptionListOptions))
|
||||
cmd.Show(new(notify.SubscriptionOptions))
|
||||
cmd.Perform("set-receiver", new(notify.SubscriptionSetReceiverOptions))
|
||||
cmd.Perform("set-robot", new(notify.SubscriptionSetRobotOptions))
|
||||
cmd.Perform("set-webhook", new(notify.SubscriptionSetWebhookOptions))
|
||||
cmd := shell.NewResourceCmd(&modules.NotifyTopic).WithKeyword("notify-topic")
|
||||
cmd.List(new(notify.TopicListOptions))
|
||||
|
||||
cmd1 := shell.NewResourceCmd(&modules.NotifySubscriber).WithKeyword("notify-subscriber")
|
||||
cmd1.List(new(notify.SubscriberListOptions))
|
||||
cmd1.Create(new(notify.SubscriberCreateOptions))
|
||||
cmd1.Show(new(notify.SubscriberOptions))
|
||||
cmd1.Delete(new(notify.SubscriberOptions))
|
||||
cmd1.Perform("change", new(notify.SubscriberChangeOptions))
|
||||
cmd1.Perform("enable", new(notify.SubscriberOptions))
|
||||
cmd1.Perform("disable", new(notify.SubscriberOptions))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
type ConfigCreateInput struct {
|
||||
apis.StandaloneResourceCreateInput
|
||||
apis.DomainizedResourceInput
|
||||
|
||||
// description: config type
|
||||
// required: true
|
||||
@@ -32,6 +33,12 @@ type ConfigCreateInput struct {
|
||||
// required: true
|
||||
// example: {"app_id": "123456", "app_secret": "feishu_nihao"}
|
||||
Content jsonutils.JSONObject `json:"content"`
|
||||
|
||||
// description: attribution
|
||||
// required: true
|
||||
// enum: system,domain
|
||||
// example: system
|
||||
Attribution string `json:"attribution"`
|
||||
}
|
||||
|
||||
type ConfigUpdateInput struct {
|
||||
@@ -43,13 +50,16 @@ type ConfigUpdateInput struct {
|
||||
|
||||
type ConfigDetails struct {
|
||||
apis.StandaloneResourceDetails
|
||||
apis.DomainizedResourceInfo
|
||||
|
||||
SConfig
|
||||
}
|
||||
|
||||
type ConfigListInput struct {
|
||||
apis.StandaloneResourceListInput
|
||||
Type string `json:"type"`
|
||||
apis.DomainizedResourceListInput
|
||||
Type string `json:"type"`
|
||||
Attribution string `json:"attribution"`
|
||||
}
|
||||
|
||||
type ConfigValidateInput struct {
|
||||
@@ -70,10 +80,9 @@ type ConfigValidateOutput struct {
|
||||
}
|
||||
|
||||
type ConfigManagerGetTypesInput struct {
|
||||
// description: Filter about robot
|
||||
// enum: no,yes,only
|
||||
// example: yes
|
||||
Robot string `json:"robot"`
|
||||
// description: domain where in config
|
||||
// example: default
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type ConfigManagerGetTypesOutput struct {
|
||||
|
||||
+42
-25
@@ -70,31 +70,48 @@ const (
|
||||
CTYPE_ROBOT_YES = "yes"
|
||||
CTYPE_ROBOT_ONLY = "only"
|
||||
|
||||
SUBSCRIPTION_RESOURCE_CREATE_DELETE = "resource create delete"
|
||||
SUBSCRIPTION_RESOURCE_CHANGECONFIG = "resource change config"
|
||||
SUBSCRIPTION_RESOURCE_UPDATE = "resource udpate"
|
||||
SUBSCRIPTION_RESOURCE_EXPIRED_RELEASE = "resource expired release"
|
||||
SUBSCRIPTION_AUTOMATED_PROCESS_EXECUTION = "automated process execution"
|
||||
CONFIG_ATTRIBUTION_SYSTEM = "system"
|
||||
CONFIG_ATTRIBUTION_DOMAIN = "domain"
|
||||
|
||||
SUBSCRIPTION_TYPE_RESOURCE = "resource"
|
||||
SUBSCRIPTION_TYPE_AUTOMATED_PROCESS = "automated_process"
|
||||
ROBOT_TYPE_FEISHU = "feishu"
|
||||
ROBOT_TYPE_DINGTALK = "dingtalk"
|
||||
ROBOT_TYPE_WORKWX = "workwx"
|
||||
ROBOT_TYPE_WEBHOOK = "webhook"
|
||||
|
||||
SUBSCRIPTION_RESOURCE_SERVER = "server"
|
||||
SUBSCRIPTION_RESOURCE_SCALINGGROUP = "scalinggroup"
|
||||
SUBSCRIPTION_RESOURCE_SCALINGPOLICY = "scalingpolicy"
|
||||
SUBSCRIPTION_RESOURCE_IMAGE = "image"
|
||||
SUBSCRIPTION_RESOURCE_DISK = "disk"
|
||||
SUBSCRIPTION_RESOURCE_SNAPSHOT = "snapshot"
|
||||
SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT = "instance_snapshot"
|
||||
SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY = "snapshotpolicy"
|
||||
SUBSCRIPTION_RESOURCE_NETWORK = "network"
|
||||
SUBSCRIPTION_RESOURCE_EIP = "eip"
|
||||
SUBSCRIPTION_RESOURCE_SECGROUP = "secgroup"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCER = "loadbalancer"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCERACL = "loadbalanceracl"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE = "loadbalancercertificate"
|
||||
SUBSCRIPTION_RESOURCE_BUCKET = "bucket"
|
||||
SUBSCRIPTION_RESOURCE_DBINSTANCE = "dbinstance"
|
||||
SUBSCRIPTION_RESOURCE_ELASTICCACHE = "elasticcache"
|
||||
SUBSCRIPTION_RESOURCE_SCHEDULEDTASK = "scheduledtask"
|
||||
ROBOT_STATUS_READY = "ready"
|
||||
|
||||
RECEIVER_TYPE_USER = "user"
|
||||
RECEIVER_TYPE_CONTACT = "contact"
|
||||
RECEIVER_TYPE_ROBOT = "robot"
|
||||
|
||||
TOPIC_TYPE_RESOURCE = "resource"
|
||||
TOPIC_TYPE_AUTOMATED_PROCESS = "automated_process"
|
||||
|
||||
TOPIC_RESOURCE_SERVER = "server"
|
||||
TOPIC_RESOURCE_SCALINGGROUP = "scalinggroup"
|
||||
TOPIC_RESOURCE_SCALINGPOLICY = "scalingpolicy"
|
||||
TOPIC_RESOURCE_IMAGE = "image"
|
||||
TOPIC_RESOURCE_DISK = "disk"
|
||||
TOPIC_RESOURCE_SNAPSHOT = "snapshot"
|
||||
TOPIC_RESOURCE_INSTANCESNAPSHOT = "instance_snapshot"
|
||||
TOPIC_RESOURCE_SNAPSHOTPOLICY = "snapshotpolicy"
|
||||
TOPIC_RESOURCE_NETWORK = "network"
|
||||
TOPIC_RESOURCE_EIP = "eip"
|
||||
TOPIC_RESOURCE_SECGROUP = "secgroup"
|
||||
TOPIC_RESOURCE_LOADBALANCER = "loadbalancer"
|
||||
TOPIC_RESOURCE_LOADBALANCERACL = "loadbalanceracl"
|
||||
TOPIC_RESOURCE_LOADBALANCERCERTIFICATE = "loadbalancercertificate"
|
||||
TOPIC_RESOURCE_BUCKET = "bucket"
|
||||
TOPIC_RESOURCE_DBINSTANCE = "dbinstance"
|
||||
TOPIC_RESOURCE_ELASTICCACHE = "elasticcache"
|
||||
TOPIC_RESOURCE_SCHEDULEDTASK = "scheduledtask"
|
||||
TOPIC_RESOURCE_BAREMETAL = "baremetal"
|
||||
|
||||
SUBSCRIBER_TYPE_ROLE = "role"
|
||||
SUBSCRIBER_TYPE_ROBOT = "robot"
|
||||
SUBSCRIBER_TYPE_RECEIVER = "receiver"
|
||||
|
||||
SUBSCRIBER_SCOPE_SYSTEM = "system"
|
||||
SUBSCRIBER_SCOPE_DOMAIN = "domain"
|
||||
SUBSCRIBER_SCOPE_PROJECT = "project"
|
||||
)
|
||||
|
||||
@@ -32,6 +32,16 @@ var (
|
||||
ActionResize SAction = "resize"
|
||||
ActionExpiredRelease SAction = "expired_release"
|
||||
ActionExecute SAction = "execute"
|
||||
ActionChangeIpaddr SAction = "change_ipaddr"
|
||||
ActionSyncStatus SAction = "sync_status"
|
||||
ActionCleanData SAction = "clean_data"
|
||||
ActionMigrate SAction = "migrate"
|
||||
|
||||
ActionCreateBackupServer SAction = "add_backup_server"
|
||||
ActionDelBackupServer SAction = "delete_backup_server"
|
||||
|
||||
ResultFailed SResult = "failed"
|
||||
ResultSucceed SResult = "succeed"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,9 +50,12 @@ const (
|
||||
|
||||
type SAction string
|
||||
|
||||
type SResult string
|
||||
|
||||
type SEvent struct {
|
||||
resourceType string
|
||||
action SAction
|
||||
result SResult
|
||||
}
|
||||
|
||||
func (se SEvent) WithResourceType(rt string) SEvent {
|
||||
@@ -55,6 +68,11 @@ func (se SEvent) WithAction(a SAction) SEvent {
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) WithResult(r SResult) SEvent {
|
||||
se.result = r
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) ResourceType() string {
|
||||
return se.resourceType
|
||||
}
|
||||
@@ -63,6 +81,29 @@ func (se SEvent) Action() SAction {
|
||||
return se.action
|
||||
}
|
||||
|
||||
func (se SEvent) ActionWithResult(delimiter string) string {
|
||||
ar := string(se.action)
|
||||
if len(se.result) > 0 {
|
||||
ar += delimiter + string(se.result)
|
||||
}
|
||||
return strings.ToUpper(ar)
|
||||
}
|
||||
|
||||
func (se SEvent) Result() SResult {
|
||||
if se.result == "" {
|
||||
return ResultSucceed
|
||||
}
|
||||
return se.result
|
||||
}
|
||||
|
||||
func (se SEvent) String() string {
|
||||
return strings.ToUpper(fmt.Sprintf("%s%s%s", se.ResourceType(), DelimiterInEvent, se.Action()))
|
||||
return se.StringWithDeli(DelimiterInEvent)
|
||||
}
|
||||
|
||||
func (se SEvent) StringWithDeli(delimiter string) string {
|
||||
str := strings.ToUpper(fmt.Sprintf("%s%s%s", se.ResourceType(), delimiter, se.Action()))
|
||||
if se.result != "" {
|
||||
str += delimiter + strings.ToUpper(string(se.result))
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ type NotificationCreateInput struct {
|
||||
// description: direct contact, admin privileges required
|
||||
// required: false
|
||||
Contacts []string `json:"contacts"`
|
||||
// description: robots
|
||||
// example: feishu robot
|
||||
Robots []string `json:"robots"`
|
||||
// description: contact type
|
||||
// required: ture
|
||||
// example: email
|
||||
@@ -84,14 +87,19 @@ type NotificationListInput struct {
|
||||
Tag string
|
||||
}
|
||||
|
||||
type SContact struct {
|
||||
Contact string `json:"contact"`
|
||||
ContactType string `json:"contact_type"`
|
||||
}
|
||||
|
||||
type NotificationManagerEventNotifyInput struct {
|
||||
// description: ids or names of receiver
|
||||
// required: false
|
||||
// example: {"adfb720ccdd34c638346ea4fa7a713a8", "zhangsan"}
|
||||
Receivers []string `json:"receivers"`
|
||||
// example: {"adfb720ccdd34c638346ea4fa7a713a8"}
|
||||
ReceiverIds []string `json:"receiver_ids"`
|
||||
// description: direct contact, admin privileges required
|
||||
// required: false
|
||||
Contacts []string `json:"contacts"`
|
||||
Contacts []SContact `json:"contacts"`
|
||||
// description: contact types
|
||||
// required: false
|
||||
// example: email
|
||||
|
||||
@@ -162,3 +162,7 @@ type ReceiverIntellijGetInput struct {
|
||||
// required: true
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type ReceiverEnableContactTypeInput struct {
|
||||
EnabledContactTypes []string `json:"enabled_contact_types"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type RobotCreateInput struct {
|
||||
apis.SharableVirtualResourceCreateInput
|
||||
// description: robot type
|
||||
// enum: feishu,dingtalk,workwx,webhook
|
||||
// example: webhook
|
||||
Type string `json:"type"`
|
||||
// description: address
|
||||
// example: http://helloworld.io/test/webhook
|
||||
Address string `json:"address"`
|
||||
// description: Language preference
|
||||
// example: zh_CN
|
||||
Lang string `json:"lang"`
|
||||
}
|
||||
|
||||
type RobotDetails struct {
|
||||
apis.SharableVirtualResourceDetails
|
||||
}
|
||||
|
||||
type RobotListInput struct {
|
||||
apis.SharableVirtualResourceListInput
|
||||
apis.EnabledResourceBaseListInput
|
||||
// description: robot type
|
||||
// enum: feishu,dingtalk,workwx,webhook
|
||||
// example: webhook
|
||||
Type string `json:"type"`
|
||||
// description: Language preference
|
||||
// example: en
|
||||
Lang string `json:"lang"`
|
||||
}
|
||||
|
||||
type RobotUpdateInput struct {
|
||||
apis.SharableVirtualResourceBaseUpdateInput
|
||||
// description: address
|
||||
// example: http://helloworld.io/test/webhook
|
||||
Address string `json:"address"`
|
||||
// description: Language preference
|
||||
// example: en
|
||||
Lang string `json:"lang"`
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// 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 notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type SubscriberCreateInput struct {
|
||||
apis.StandaloneAnonResourceCreateInput
|
||||
|
||||
// description: Id of Topic
|
||||
// required
|
||||
TopicID string
|
||||
|
||||
// description: scope of resource
|
||||
// enum: system,domain,project
|
||||
ResourceScope string
|
||||
|
||||
// description: project id or domain id of resource
|
||||
// example: 1e3824756bac4ac084e784ed297ec652
|
||||
ResourceAttributionId string
|
||||
|
||||
// description: domain id of resource
|
||||
// example: 1e3824756bac4ac084e784ed297ec652
|
||||
DomainId string
|
||||
|
||||
// description: Type of subscriber
|
||||
// enum: receiver,robot,role
|
||||
Type string
|
||||
|
||||
// description: receivers which is required when the type is 'receiver' will Subscribe TopicID
|
||||
Receivers []string
|
||||
|
||||
// description: Role(Id or Name) which is required when the type is 'role' will Subscribe TopicID
|
||||
Role string
|
||||
|
||||
// description: The scope of role subscribers
|
||||
// enum: system,domain,project
|
||||
RoleScope string
|
||||
|
||||
// description: Robot(Id or Name) which is required when the type is 'robot' will Subscribe TopicID
|
||||
Robot string
|
||||
|
||||
// description: scope
|
||||
// enum: system,domain
|
||||
Scope string
|
||||
}
|
||||
|
||||
type SubscriberChangeInput struct {
|
||||
// description: receivers which is required when the type is 'receiver' will Subscribe TopicID
|
||||
Receivers []string
|
||||
|
||||
// description: Role(Id or Name) which is required when the type is 'role' will Subscribe TopicID
|
||||
Role string
|
||||
|
||||
// description: The scope of role subscribers
|
||||
// enum: system,domain,project
|
||||
RoleScope string
|
||||
|
||||
// description: Robot(Id or Name) which is required when the type is 'robot' will Subscribe TopicID
|
||||
Robot string
|
||||
}
|
||||
|
||||
type SubscriberListInput struct {
|
||||
apis.StandaloneAnonResourceListInput
|
||||
apis.EnabledResourceBaseListInput
|
||||
|
||||
// description: topic id
|
||||
TopicID string
|
||||
|
||||
// description: scope of resource
|
||||
// enum: system,domain,project
|
||||
ResourceScope string
|
||||
|
||||
// description: type
|
||||
// enum: receiver,robot,role
|
||||
Type string
|
||||
|
||||
// description: scope
|
||||
// enum: system,domain
|
||||
Scope string
|
||||
}
|
||||
|
||||
type Identification struct {
|
||||
// example: 036fed49483b412888a760c2bc995caa
|
||||
ID string `json:"id"`
|
||||
// example: test
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type SubscriberDetails struct {
|
||||
apis.StandaloneAnonResourceDetails
|
||||
SSubscriber
|
||||
|
||||
// description: receivers
|
||||
Receivers []Identification
|
||||
|
||||
// description: role
|
||||
Role Identification
|
||||
|
||||
// description: robot
|
||||
Robot Identification
|
||||
}
|
||||
|
||||
type SubscriberSetReceiverInput struct {
|
||||
Receivers []string
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// 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 notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type SubscriptionListInput struct {
|
||||
}
|
||||
|
||||
type SubscriptionDetails struct {
|
||||
apis.StatusStandaloneResourceDetails
|
||||
// description: resources managed
|
||||
// example: ["server", "eip", "disk"]
|
||||
Resources []string `json:"resource_types"`
|
||||
// description: receivers of the message sent
|
||||
Receivers SubscriptionReceiver `json:"receivers"`
|
||||
// description: type of robot send message
|
||||
// example: dingtalk_robot
|
||||
Robot string `json:"robot"`
|
||||
// description: webhook send message
|
||||
// example: webhook
|
||||
Webhook string `json:"webhook"`
|
||||
}
|
||||
|
||||
type IDAndName struct {
|
||||
// example: 036fed49483b412888a760c2bc995caa
|
||||
ID string `json:"id"`
|
||||
// example: test
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ReceivingRoleIDAndName struct {
|
||||
IDAndName
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionReceiver struct {
|
||||
Receivers []IDAndName `json:"receivers"`
|
||||
ReceivingRoles []ReceivingRoleIDAndName `json:"roles"`
|
||||
}
|
||||
|
||||
type SubscriptionSetReceiverInput struct {
|
||||
ReceivingRoles []ReceivingRole `json:"roles"`
|
||||
Receivers []string `json:"receivers"`
|
||||
}
|
||||
|
||||
type ReceivingRole struct {
|
||||
// description: id or name of role
|
||||
Role string `json:"role"`
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionSetRobotInput struct {
|
||||
// description: robot
|
||||
// enum: feishu-robot,dingtalk-robot,workwx-robot
|
||||
Robot string
|
||||
}
|
||||
|
||||
type SubscriptionSetWebhookInput struct {
|
||||
Webhook string
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type TopicListInput struct {
|
||||
apis.StandaloneResourceListInput
|
||||
}
|
||||
|
||||
type TopicDetails struct {
|
||||
apis.StandaloneResourceDetails
|
||||
STopic
|
||||
|
||||
// description: resources managed
|
||||
// example: ["server", "eip", "disk"]
|
||||
Resources []string `json:"resource_types"`
|
||||
}
|
||||
|
||||
type PerformEnableInput struct {
|
||||
}
|
||||
|
||||
type PerformDisableInput struct {
|
||||
}
|
||||
@@ -23,8 +23,10 @@ import (
|
||||
// SConfig is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SConfig.
|
||||
type SConfig struct {
|
||||
apis.SStandaloneResourceBase
|
||||
Type string `json:"type"`
|
||||
Content interface{} `json:"content"`
|
||||
apis.SDomainizedResourceBase
|
||||
Type string `json:"type"`
|
||||
Content interface{} `json:"content"`
|
||||
Attribution string `json:"attribution"`
|
||||
}
|
||||
|
||||
// SNotification is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SNotification.
|
||||
@@ -69,11 +71,21 @@ type SReceiverNotification struct {
|
||||
NotificationID string `json:"notification_id"`
|
||||
// ignore if ReceiverID is not empty or default
|
||||
Contact string `json:"contact"`
|
||||
ReceiverType string `json:"receiver_type"`
|
||||
SendBy string `json:"send_by"`
|
||||
Status string `json:"status"`
|
||||
FailedReason string `json:"failed_reason"`
|
||||
}
|
||||
|
||||
// SRobot is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SRobot.
|
||||
type SRobot struct {
|
||||
apis.SSharableVirtualResourceBase
|
||||
apis.SEnabledResourceBase
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Lang string `json:"lang"`
|
||||
}
|
||||
|
||||
// SSubContact is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubContact.
|
||||
type SSubContact struct {
|
||||
apis.SStandaloneResourceBase
|
||||
@@ -87,33 +99,31 @@ type SSubContact struct {
|
||||
VerifiedNote string `json:"verified_note"`
|
||||
}
|
||||
|
||||
// SSubscription is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscription.
|
||||
type SSubscription struct {
|
||||
apis.SStandaloneResourceBase
|
||||
// SSubscriber is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscriber.
|
||||
type SSubscriber struct {
|
||||
apis.SVirtualResourceBase
|
||||
apis.SEnabledResourceBase
|
||||
Type string `json:"type"`
|
||||
Resources uint64 `json:"resources"`
|
||||
Actions uint32 `json:"actions"`
|
||||
AdvanceDays int `json:"advance_days"`
|
||||
TopicID string `json:"topic_id"`
|
||||
Type string `json:"type"`
|
||||
Identification string `json:"identification"`
|
||||
RoleScope string `json:"role_scope"`
|
||||
ResourceScope string `json:"resource_scope"`
|
||||
}
|
||||
|
||||
// SSubscriptionReceiver is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscriptionReceiver.
|
||||
type SSubscriptionReceiver struct {
|
||||
apis.SStandaloneResourceBase
|
||||
SubscriptionID string `json:"subscription_id"`
|
||||
// role id or receiver id or other and the value type is determined by the ReceiverType
|
||||
Receiver string `json:"receiver"`
|
||||
ReceiverType string `json:"receiver_type"`
|
||||
RoleScope string `json:"role_scope"`
|
||||
}
|
||||
|
||||
// SSubscriptionReceiverDis is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscriptionReceiverDis.
|
||||
type SSubscriptionReceiverDis struct {
|
||||
SSubscriptionReceiver
|
||||
// SSubscriberDis is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscriberDis.
|
||||
type SSubscriberDis struct {
|
||||
SSubscriber
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
// SSubscriberReceiver is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubscriberReceiver.
|
||||
type SSubscriberReceiver struct {
|
||||
apis.SJointResourceBase
|
||||
SubscriberId string `json:"subscriber_id"`
|
||||
ReceiverId string `json:"receiver_id"`
|
||||
}
|
||||
|
||||
// STemplate is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.STemplate.
|
||||
type STemplate struct {
|
||||
apis.SStandaloneResourceBase
|
||||
@@ -126,6 +136,16 @@ type STemplate struct {
|
||||
Example string `json:"example"`
|
||||
}
|
||||
|
||||
// STopic is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.STopic.
|
||||
type STopic struct {
|
||||
apis.SStandaloneResourceBase
|
||||
apis.SEnabledResourceBase
|
||||
Type string `json:"type"`
|
||||
Resources uint64 `json:"resources"`
|
||||
Actions uint32 `json:"actions"`
|
||||
AdvanceDays int `json:"advance_days"`
|
||||
}
|
||||
|
||||
// SVerification is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SVerification.
|
||||
type SVerification struct {
|
||||
apis.SStandaloneResourceBase
|
||||
|
||||
@@ -197,6 +197,7 @@ func mergeQueryParams(params map[string]string, query jsonutils.JSONObject, excl
|
||||
}
|
||||
|
||||
func listHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
log.Infof("start list handler")
|
||||
manager, params, query, _ := fetchEnv(ctx, w, r)
|
||||
handleList(ctx, w, manager, nil, mergeQueryParams(params, query))
|
||||
}
|
||||
|
||||
@@ -1155,6 +1155,9 @@ func FetchModelObjects(modelManager IModelManager, query *sqlchemy.SQuery, targe
|
||||
|
||||
targetsValue := reflect.Indirect(reflect.ValueOf(targets))
|
||||
for rows.Next() {
|
||||
if modelManager.Keyword() == "server" {
|
||||
log.Infof("one guest")
|
||||
}
|
||||
m, err := NewModelObject(modelManager)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -52,6 +52,11 @@ var (
|
||||
ActionExpiredRelease = api.ActionExpiredRelease
|
||||
ActionExecute = api.ActionExecute
|
||||
|
||||
ActionMigrate = api.ActionMigrate
|
||||
ActionCreateBackupServer = api.ActionCreateBackupServer
|
||||
ActionDelBackupServer = api.ActionDelBackupServer
|
||||
ActionSyncStatus = api.ActionSyncStatus
|
||||
|
||||
ActionPendingDelete = api.ActionPendingDelete
|
||||
)
|
||||
|
||||
|
||||
@@ -119,22 +119,29 @@ func NotifyCriticalWithCtx(ctx context.Context, recipientId []string, isGroup bo
|
||||
|
||||
// NotifyAllWithoutRobot will send messages via all contacnt type from exclude robot contact type such as dingtalk-robot.
|
||||
func NotifyAllWithoutRobot(recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
return notifyRobot(context.Background(), "no", recipientId, isGroup, priority, event, data)
|
||||
return notifyAll(context.Background(), recipientId, isGroup, priority, event, data)
|
||||
}
|
||||
|
||||
// NotifyAllWithoutRobot will send messages via all contacnt type from exclude robot contact type such as dingtalk-robot.
|
||||
func NotifyAllWithoutRobotWithCtx(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
return notifyRobot(ctx, "no", recipientId, isGroup, priority, event, data)
|
||||
return notifyAll(ctx, recipientId, isGroup, priority, event, data)
|
||||
}
|
||||
|
||||
// NotifyRobot will send messages via all robot contact type such as dingtalk-robot.
|
||||
func NotifyRobot(recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
return notifyRobot(context.Background(), "only", recipientId, isGroup, priority, event, data)
|
||||
func NotifyRobot(robotIds []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
return NotifyRobotWithCtx(context.Background(), robotIds, priority, event, data)
|
||||
}
|
||||
|
||||
// NotifyRobot will send messages via all robot contact type such as dingtalk-robot.
|
||||
func NotifyRobotWithCtx(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
return notifyRobot(ctx, "only", recipientId, isGroup, priority, event, data)
|
||||
func NotifyRobotWithCtx(ctx context.Context, robotIds []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
rawNotify(context.Background(), sNotifyParams{
|
||||
robots: robotIds,
|
||||
channel: npk.NotifyByRobot,
|
||||
priority: priority,
|
||||
event: event,
|
||||
data: data,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func SystemNotify(priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
|
||||
@@ -214,7 +221,9 @@ type SEventMessage struct {
|
||||
|
||||
type SEventNotifyParam struct {
|
||||
Obj db.IModel
|
||||
ResourceType string
|
||||
Action api.SAction
|
||||
IsFail bool
|
||||
ObjDetailsDecorator func(context.Context, *jsonutils.JSONDict)
|
||||
AdvanceDays int
|
||||
}
|
||||
@@ -240,9 +249,6 @@ func (t *eventTask) Run() {
|
||||
}
|
||||
|
||||
func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEventNotifyParam) {
|
||||
// disable EventNotify for now
|
||||
return
|
||||
|
||||
ret, err := db.FetchCustomizeColumns(ep.Obj.GetModelManager(), ctx, userCred, jsonutils.NewDict(), []interface{}{ep.Obj}, stringutils2.SSortedStrings{}, false)
|
||||
if err != nil {
|
||||
log.Errorf("unable to FetchCustomizeColumns: %v", err)
|
||||
@@ -256,7 +262,14 @@ func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEve
|
||||
if ep.ObjDetailsDecorator != nil {
|
||||
ep.ObjDetailsDecorator(ctx, objDetails)
|
||||
}
|
||||
event := api.Event.WithAction(ep.Action).WithResourceType(ep.Obj.GetModelManager().Keyword())
|
||||
rt := ep.ResourceType
|
||||
if len(rt) == 0 {
|
||||
rt = ep.Obj.GetModelManager().Keyword()
|
||||
}
|
||||
event := api.Event.WithAction(ep.Action).WithResourceType(rt)
|
||||
if ep.IsFail {
|
||||
event = event.WithResult(api.ResultFailed)
|
||||
}
|
||||
var (
|
||||
projectId string
|
||||
projectDomainId string
|
||||
@@ -267,6 +280,7 @@ func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEve
|
||||
projectDomainId = ownerId.GetProjectDomainId()
|
||||
}
|
||||
params := api.NotificationManagerEventNotifyInput{
|
||||
ReceiverIds: []string{userCred.GetUserId()},
|
||||
ResourceDetails: objDetails,
|
||||
Event: event.String(),
|
||||
AdvanceDays: ep.AdvanceDays,
|
||||
|
||||
@@ -57,13 +57,12 @@ func systemNotify(ctx context.Context, priority npk.TNotifyPriority, event strin
|
||||
notify(ctx, notifyAdminGroups, true, priority, event, data)
|
||||
}
|
||||
|
||||
func notifyRobot(ctx context.Context, robot string, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
func notifyAll(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
|
||||
s, err := AdminSessionGenerator(ctx, consts.GetRegion(), "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("robot", jsonutils.NewString(robot))
|
||||
result, err := modules.NotifyReceiver.PerformClassAction(s, "get-types", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -85,6 +84,30 @@ func notifyRobot(ctx context.Context, robot string, recipientId []string, isGrou
|
||||
type sTarget struct {
|
||||
reIds []string
|
||||
contacts []string
|
||||
robots []string
|
||||
}
|
||||
|
||||
func langRobot(ctx context.Context, robots []string) (map[language.Tag]*sTarget, error) {
|
||||
contextLang := i18n.Lang(ctx)
|
||||
robotLang, err := getRobotLang(robots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make(map[language.Tag]*sTarget)
|
||||
for robot, langStr := range robotLang {
|
||||
lang, err := language.Parse(langStr)
|
||||
if err != nil {
|
||||
log.Errorf("can't parse %s to language.Tag: %v", langStr, err)
|
||||
lang = contextLang
|
||||
}
|
||||
t := ret[lang]
|
||||
if t == nil {
|
||||
ret[lang] = &sTarget{}
|
||||
t = ret[lang]
|
||||
}
|
||||
t.robots = append(t.robots, robot)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func lang(ctx context.Context, contactType npk.TNotifyChannel, reIds []string, contacts []string) (map[language.Tag]*sTarget, error) {
|
||||
@@ -175,6 +198,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
|
||||
msg := npk.SNotifyMessage{}
|
||||
msg.Uid = reIds
|
||||
msg.Priority = p.priority
|
||||
msg.Robots = p.robots
|
||||
msg.Contacts = p.contacts
|
||||
msg.ContactType = p.channel
|
||||
msg.Topic = p.event
|
||||
@@ -185,9 +209,15 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
|
||||
return []npk.SNotifyMessage{msg}, nil
|
||||
}
|
||||
|
||||
langMap, err := lang(ctx, p.channel, reIds, p.contacts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var langMap map[language.Tag]*sTarget
|
||||
|
||||
if p.channel == npk.NotifyByRobot {
|
||||
langMap, err = langRobot(ctx, p.robots)
|
||||
} else {
|
||||
langMap, err = lang(ctx, p.channel, reIds, p.contacts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
msgs := make([]npk.SNotifyMessage, 0, len(langMap))
|
||||
@@ -196,6 +226,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
|
||||
msg := npk.SNotifyMessage{}
|
||||
msg.Uid = t.reIds
|
||||
msg.Priority = p.priority
|
||||
msg.Robots = p.robots
|
||||
msg.Contacts = t.contacts
|
||||
msg.ContactType = p.channel
|
||||
topic, _ := getContent(langSuffix, p.event, "title", p.channel, p.data)
|
||||
@@ -218,6 +249,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
|
||||
|
||||
type sNotifyParams struct {
|
||||
recipientId []string
|
||||
robots []string
|
||||
isGroup bool
|
||||
contacts []string
|
||||
channel npk.TNotifyChannel
|
||||
|
||||
@@ -64,6 +64,29 @@ func getUserLang(uids []string) (map[string]string, error) {
|
||||
return uidLang, nil
|
||||
}
|
||||
|
||||
func getRobotLang(robots []string) (map[string]string, error) {
|
||||
s, err := AdminSessionGenerator(context.Background(), consts.GetRegion(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
robotLang := make(map[string]string)
|
||||
if len(robots) > 0 {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("filter", jsonutils.NewString(fmt.Sprintf("id.in(%s)", strings.Join(robots, ","))))
|
||||
params.Set("scope", jsonutils.NewString("system"))
|
||||
ret, err := modules.NotifyRobot.List(s, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range ret.Data {
|
||||
id, _ := ret.Data[i].GetString("id")
|
||||
langStr, _ := ret.Data[i].GetString("lang")
|
||||
robotLang[id] = langStr
|
||||
}
|
||||
}
|
||||
return robotLang, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
templatesTableLock = &sync.Mutex{}
|
||||
templatesTable = make(map[string]*template.Template)
|
||||
|
||||
@@ -16,13 +16,17 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -204,3 +208,83 @@ func ParseBillingCycleInput(billingBase *SBillingResourceBase, input apis.Postpa
|
||||
|
||||
return &bc, nil
|
||||
}
|
||||
|
||||
type SBillingResourceCheckManager struct {
|
||||
db.SResourceBaseManager
|
||||
}
|
||||
|
||||
type SBillingResourceCheck struct {
|
||||
db.SResourceBase
|
||||
ResourceId string `width:"128" charset:"ascii" index:"true"`
|
||||
ResourceType string `width:"36" charset:"ascii" index:"true"`
|
||||
AdvanceDays int
|
||||
LastCheck time.Time
|
||||
}
|
||||
|
||||
var BillingResourceCheckManager *SBillingResourceCheckManager
|
||||
|
||||
func init() {
|
||||
BillingResourceCheckManager = &SBillingResourceCheckManager{
|
||||
SResourceBaseManager: db.NewResourceBaseManager(
|
||||
SBillingResourceCheck{},
|
||||
"billingresourcecheck_tbl",
|
||||
"billingresourcecheck",
|
||||
"billingresourcechecks",
|
||||
),
|
||||
}
|
||||
BillingResourceCheckManager.SetVirtualObject(BillingResourceCheckManager)
|
||||
}
|
||||
|
||||
func (bm *SBillingResourceCheckManager) Create(ctx context.Context, resourceId, resourceType string, advanceDays int) error {
|
||||
bc := SBillingResourceCheck{
|
||||
ResourceId: resourceId,
|
||||
ResourceType: resourceType,
|
||||
AdvanceDays: advanceDays,
|
||||
LastCheck: time.Now(),
|
||||
}
|
||||
return bm.TableSpec().Insert(ctx, &bc)
|
||||
}
|
||||
|
||||
var advanceDays []int = []int{1, 3}
|
||||
|
||||
func CheckBillingResourceExpireAt(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
billingResourceManagers := []db.IModelManager{
|
||||
GuestManager,
|
||||
DBInstanceManager,
|
||||
ElasticcacheManager,
|
||||
}
|
||||
for _, advanceDay := range advanceDays {
|
||||
for _, manager := range billingResourceManagers {
|
||||
upLimit := time.Now().AddDate(0, 0, advanceDay)
|
||||
downLimit := time.Now().AddDate(0, 0, advanceDay-1)
|
||||
v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0)
|
||||
q := manager.Query().LE("expired_at", upLimit).GE("expired_at", downLimit)
|
||||
|
||||
bq := BillingResourceCheckManager.Query("resource_id").Equals("resource_type", manager.Keyword()).Equals("advance_days", advanceDay).SubQuery()
|
||||
q = q.LeftJoin(bq, sqlchemy.Equals(q.Field("id"), bq.Field("resource_id")))
|
||||
q = q.IsNull("resource_id")
|
||||
|
||||
vp := reflect.New(v.Type())
|
||||
vp.Elem().Set(v)
|
||||
err := db.FetchModelObjects(manager, q, vp.Interface())
|
||||
if err != nil {
|
||||
log.Errorf("unable to list %s: %v", manager.KeywordPlural(), err)
|
||||
}
|
||||
|
||||
v = vp.Elem()
|
||||
log.Debugf("%s length of v: %d", manager.Alias(), v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
m := v.Index(i).Addr().Interface().(db.IModel)
|
||||
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
|
||||
Obj: m,
|
||||
Action: notifyclient.ActionExpiredRelease,
|
||||
AdvanceDays: advanceDay,
|
||||
})
|
||||
err := BillingResourceCheckManager.Create(ctx, m.GetId(), manager.Keyword(), advanceDay)
|
||||
if err != nil {
|
||||
log.Errorf("unable to create billingresourcecheck for resource %s %s", manager.Keyword(), m.GetId())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,8 +973,13 @@ func (self *SGuest) EventNotify(ctx context.Context, userCred mcclient.TokenCred
|
||||
}
|
||||
}
|
||||
}
|
||||
var resourceType string
|
||||
if self.Hypervisor == api.HYPERVISOR_BAREMETAL {
|
||||
resourceType = noapi.TOPIC_RESOURCE_BAREMETAL
|
||||
}
|
||||
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
|
||||
Obj: self,
|
||||
ResourceType: resourceType,
|
||||
Action: action,
|
||||
ObjDetailsDecorator: detailsDecro,
|
||||
AdvanceDays: 0,
|
||||
|
||||
@@ -47,7 +47,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/userdata"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -91,6 +90,7 @@ func init() {
|
||||
"servers",
|
||||
),
|
||||
}
|
||||
log.Infof("init GuestManager")
|
||||
GuestManager.SetVirtualObject(GuestManager)
|
||||
GuestManager.SetAlias("guest", "guests")
|
||||
}
|
||||
@@ -1681,8 +1681,6 @@ func (self *SGuest) PostUpdate(ctx context.Context, userCred mcclient.TokenCrede
|
||||
log.Errorf("StartRemoteUpdateTask fail: %s", err)
|
||||
}
|
||||
}
|
||||
// notify webhook
|
||||
notifyclient.NotifyWebhook(ctx, userCred, self, notifyclient.ActionUpdate)
|
||||
}
|
||||
|
||||
func (manager *SGuestManager) checkCreateQuota(
|
||||
|
||||
@@ -95,6 +95,7 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.CloudimageManager,
|
||||
|
||||
models.WafRuleStatementManager,
|
||||
models.BillingResourceCheckManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
|
||||
@@ -165,6 +165,8 @@ func StartService() {
|
||||
cron.AddJobEveryFewHour("InspectAllTemplate", 1, 0, 0, models.GuestTemplateManager.InspectAllTemplate, true)
|
||||
|
||||
cron.AddJobAtIntervalsWithStartRun("ScheduledTaskCheck", time.Duration(60)*time.Second, models.ScheduledTaskManager.Timer, true)
|
||||
|
||||
cron.AddJobEveryFewHour("CheckBillingResourceExpireAt", 1, 0, 0, models.CheckBillingResourceExpireAt, true)
|
||||
go cron.Start2(ctx, electObj)
|
||||
|
||||
// init auto scaling controller
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -53,6 +54,10 @@ func (task *BucketCreateTask) OnInit(ctx context.Context, obj db.IStandaloneMode
|
||||
}
|
||||
|
||||
bucket.SetStatus(task.UserCred, api.BUCKET_STATUS_READY, "BucketCreateTask")
|
||||
notifyclient.EventNotify(ctx, task.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: bucket,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
logclient.AddActionLogWithStartable(task, bucket, logclient.ACT_ALLOCATE, nil, task.UserCred, true)
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -52,6 +53,10 @@ func (task *BucketDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneMode
|
||||
return
|
||||
}
|
||||
|
||||
notifyclient.EventNotify(ctx, task.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: bucket,
|
||||
Action: notifyclient.ActionDelete,
|
||||
})
|
||||
logclient.AddActionLogWithStartable(task, bucket, logclient.ACT_DELETE, nil, task.UserCred, true)
|
||||
task.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -73,7 +74,20 @@ func (self *DBInstanceAccountResetPasswordTask) OnInit(ctx context.Context, obj
|
||||
}
|
||||
}
|
||||
|
||||
self.resetPasswdNotify(ctx, instance, account, password)
|
||||
account.SetStatus(self.UserCred, api.DBINSTANCE_USER_AVAILABLE, "")
|
||||
logclient.AddActionLogWithStartable(self, account, logclient.ACT_RESET_PASSWORD, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *DBInstanceAccountResetPasswordTask) resetPasswdNotify(ctx context.Context, rds *models.SDBInstance, account *models.SDBInstanceAccount, password string) {
|
||||
detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
|
||||
details.Set("account", jsonutils.NewString(account.GetName()))
|
||||
details.Set("password", jsonutils.NewString(password))
|
||||
}
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: rds,
|
||||
Action: notifyclient.ActionResetPassword,
|
||||
ObjDetailsDecorator: detailsDecro,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ func (self *DBInstanceChangeConfigTask) taskFailed(ctx context.Context, dbinstan
|
||||
dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_CHANGE_CONFIG_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(dbinstance, db.ACT_CHANGE_CONFIG, err, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CHANGE_CONFIG, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionChangeConfig,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
@@ -64,7 +69,10 @@ func (self *DBInstanceChangeConfigTask) OnInit(ctx context.Context, obj db.IStan
|
||||
func (self *DBInstanceChangeConfigTask) OnDBInstanceChangeConfigComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
dbinstance := obj.(*models.SDBInstance)
|
||||
logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CHANGE_CONFIG, nil, self.UserCred, true)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionChangeConfig)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionChangeConfig,
|
||||
})
|
||||
self.SetStage("OnSyncDBInstanceStatusComplete", nil)
|
||||
models.StartResourceSyncStatusTask(ctx, self.UserCred, dbinstance, "DBInstanceSyncStatusTask", self.GetTaskId())
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ func (self *DBInstanceCreateTask) taskFailed(ctx context.Context, dbinstance *mo
|
||||
dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_CREATE_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(dbinstance, db.ACT_CREATE, err, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CREATE, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
@@ -77,7 +82,11 @@ func (self *DBInstanceCreateTask) OnCreateDBInstanceCompleteFailed(ctx context.C
|
||||
|
||||
func (self *DBInstanceCreateTask) OnSyncDBInstanceStatusComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
dbinstance := obj.(*models.SDBInstance)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionCreate)
|
||||
//notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionCreate)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ func (self *DBInstanceDeleteTask) taskFailed(ctx context.Context, dbinstance *mo
|
||||
dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_DELETE_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(dbinstance, db.ACT_DELETE, err, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_DELETE, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
@@ -83,13 +88,21 @@ func (self *DBInstanceDeleteTask) DeleteDBInstanceComplete(ctx context.Context,
|
||||
self.taskFailed(ctx, dbinstance, errors.Wrap(err, "dbinstance.Purge"))
|
||||
return
|
||||
}
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete)
|
||||
//notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionDelete,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
self.DeleteBackups(ctx, dbinstance, nil)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete)
|
||||
//notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionDelete,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *DBInstanceDeleteTask) DeleteBackups(ctx context.Context, instance *models.SDBInstance, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -39,6 +40,11 @@ func (self *DBInstanceSyncStatusTask) taskFailed(ctx context.Context, dbinstance
|
||||
dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_UNKNOWN, err.Error())
|
||||
db.OpsLog.LogEvent(dbinstance, db.ACT_SYNC_STATUS, err, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_SYNC_STATUS, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: dbinstance,
|
||||
Action: notifyclient.ActionSyncStatus,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,11 @@ func init() {
|
||||
func (self *EipAllocateTask) onFailed(ctx context.Context, eip *models.SElasticip, reason jsonutils.JSONObject) {
|
||||
eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, reason.String())
|
||||
self.setGuestAllocateEipFailed(eip, reason)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: eip,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
@@ -159,6 +164,10 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel
|
||||
}
|
||||
} else {
|
||||
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: eip,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
}
|
||||
@@ -166,10 +175,6 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel
|
||||
func (self *EipAllocateTask) OnEipAssociateComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
eip := obj.(*models.SElasticip)
|
||||
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: eip,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,11 @@ func (self *EipDeallocateTask) taskFail(ctx context.Context, eip *models.SElasti
|
||||
eip.SetStatus(self.UserCred, api.EIP_STATUS_DEALLOCATE_FAIL, msg.String())
|
||||
db.OpsLog.LogEvent(eip, db.ACT_DELOCATE, msg, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_DELETE, msg, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: eip,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, msg)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,21 @@ func (self *ElasticcacheAccountResetPasswordTask) OnInit(ctx context.Context, ob
|
||||
ec.(*models.SElasticcache).SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RUNNING, "")
|
||||
logclient.AddActionLogWithStartable(self, ec, logclient.ACT_RESET_PASSWORD, "", self.UserCred, true)
|
||||
}
|
||||
password, _ := self.GetParams().GetString("password")
|
||||
self.resetPasswordNotify(ctx, ec.(*models.SElasticcache), ea, password)
|
||||
logclient.AddActionLogWithStartable(self, ea, logclient.ACT_RESET_PASSWORD, "", self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ElasticcacheAccountResetPasswordTask) resetPasswordNotify(ctx context.Context, ec *models.SElasticcache, account *models.SElasticcacheAccount, password string) {
|
||||
detailDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
|
||||
details.Set("account", jsonutils.NewString(account.GetName()))
|
||||
details.Set("password", jsonutils.NewString(password))
|
||||
}
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: ec,
|
||||
Action: notifyclient.ActionResetPassword,
|
||||
ObjDetailsDecorator: detailDecro,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ func (self *ElasticcacheChangeSpecTask) taskFail(ctx context.Context, ec *models
|
||||
ec.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CHANGE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(ec, db.ACT_CHANGE_FLAVOR, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, ec, logclient.ACT_VM_CHANGE_FLAVOR, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, ec.Id, ec.Name, api.ELASTIC_CACHE_STATUS_CHANGE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: ec,
|
||||
Action: notifyclient.ActionChangeConfig,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ func (self *ElasticcacheCreateTask) taskFail(ctx context.Context, elasticcache *
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CREATE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(elasticcache, db.ACT_ALLOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, elasticcache.Id, elasticcache.Name, api.ELASTIC_CACHE_STATUS_CREATE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: elasticcache,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
@@ -95,7 +99,11 @@ func (self *ElasticcacheCreateTask) OnSyncSecurityGroupCompleteFailed(ctx contex
|
||||
func (self *ElasticcacheCreateTask) OnElasticcacheCreateComplete(ctx context.Context, elasticcache *models.SElasticcache, data jsonutils.JSONObject) {
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RUNNING, "")
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, "", self.UserCred, true)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, elasticcache, notifyclient.ActionCreate)
|
||||
// notifyclient.NotifyWebhook(ctx, self.UserCred, elasticcache, notifyclient.ActionCreate)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: elasticcache,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ func (self *ElasticcacheDeleteTask) taskFail(ctx context.Context, elasticcache *
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RELEASE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(elasticcache, db.ACT_DELOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_DELETE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, elasticcache.Id, elasticcache.Name, api.ELASTIC_CACHE_STATUS_RELEASE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: elasticcache,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
@@ -62,7 +66,10 @@ func (self *ElasticcacheDeleteTask) OnInit(ctx context.Context, obj db.IStandalo
|
||||
ec.DeleteSubResources(ctx, self.UserCred)
|
||||
ec.SVirtualResourceBase.Delete(ctx, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, ec, logclient.ACT_DELETE, "", self.UserCred, true)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, ec, notifyclient.ActionDelete)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: ec,
|
||||
Action: notifyclient.ActionDelete,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -40,6 +41,11 @@ func (self *ElasticcacheSyncstatusTask) taskFailed(ctx context.Context, cache *m
|
||||
self.SetStageFailed(ctx, err)
|
||||
db.OpsLog.LogEvent(cache, db.ACT_SYNC_STATUS, cache.GetShortDesc(ctx), self.GetUserCred())
|
||||
logclient.AddActionLogWithContext(ctx, cache, logclient.ACT_SYNC_STATUS, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: cache,
|
||||
Action: notifyclient.ActionSyncStatus,
|
||||
IsFail: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *ElasticcacheSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -296,7 +296,11 @@ func (self *GuestBatchCreateTask) SaveScheduleResult(ctx context.Context, obj IS
|
||||
self.clearPendingUsage(ctx, guest)
|
||||
db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, err, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, obj, logclient.ACT_ALLOCATE, err, self.GetUserCred(), false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_CREATE_FAILED, err.Error())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionCreateBackupServer,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +335,6 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecCompleteFailed(ctx con
|
||||
func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Context, guest *models.SGuest) {
|
||||
models.HostManager.ClearSchedDescCache(guest.HostId)
|
||||
self.SetStage("OnSyncConfigComplete", nil)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionChangeConfig)
|
||||
err := guest.StartSyncTaskWithoutSyncstatus(ctx, self.UserCred, false, self.GetTaskId())
|
||||
if err != nil {
|
||||
self.markStageFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("StartSyncstatus fail %s", err)))
|
||||
@@ -383,7 +382,11 @@ func (self *GuestChangeConfigTask) markStageFailed(ctx context.Context, guest *m
|
||||
guest.SetStatus(self.UserCred, api.VM_CHANGE_FLAVOR_FAIL, reason.String())
|
||||
db.OpsLog.LogEvent(guest, db.ACT_CHANGE_FLAVOR_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_FLAVOR, reason, self.UserCred, false)
|
||||
notifyclient.NotifyError(ctx, self.UserCred, guest.GetId(), guest.GetName(), logclient.ACT_VM_CHANGE_FLAVOR, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionChangeConfig,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -70,7 +69,12 @@ func (self *GuestCreateTask) OnDiskPreparedFailed(ctx context.Context, obj db.IS
|
||||
guest.SetStatus(self.UserCred, api.VM_DISK_FAILED, "allocation failed")
|
||||
db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DISK_FAILED, data.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
|
||||
self.SetStageFailed(ctx, data)
|
||||
}
|
||||
|
||||
@@ -99,7 +103,11 @@ func (self *GuestCreateTask) OnCdromPreparedFailed(ctx context.Context, obj db.I
|
||||
guest.SetStatus(self.UserCred, api.VM_DISK_FAILED, "")
|
||||
db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DISK_FAILED, fmt.Sprintf("cdrom_failed %s", data))
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, data)
|
||||
}
|
||||
|
||||
@@ -160,21 +168,18 @@ func (self *GuestCreateTask) OnDeployGuestDescComplete(ctx context.Context, obj
|
||||
}
|
||||
|
||||
func (self *GuestCreateTask) notifyServerCreated(ctx context.Context, guest *models.SGuest) {
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionCreate)
|
||||
guest.NotifyServerEvent(
|
||||
ctx, self.UserCred, notifyclient.SERVER_CREATED,
|
||||
notify.NotifyPriorityImportant, true, nil, false,
|
||||
)
|
||||
guest.NotifyAdminServerEvent(ctx, notifyclient.SERVER_CREATED_ADMIN, notify.NotifyPriorityImportant)
|
||||
// guest.EventNotify(ctx, self.UserCred, notifyclient.ActionCreate)
|
||||
guest.EventNotify(ctx, self.UserCred, notifyclient.ActionCreate)
|
||||
}
|
||||
|
||||
func (self *GuestCreateTask) OnDeployGuestDescCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
guest := obj.(*models.SGuest)
|
||||
guest.SetStatus(self.UserCred, api.VM_DEPLOY_FAILED, "deploy_failed")
|
||||
db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DEPLOY_FAILED, data.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, data)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
@@ -40,6 +41,11 @@ func (self *GuestDeleteBackupTask) OnFail(ctx context.Context, guest *models.SGu
|
||||
logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_DELETE_BACKUP, reason, self.UserCred, false)
|
||||
db.OpsLog.LogEvent(guest, db.ACT_DELETE_BACKUP_FAILED, reason, self.UserCred)
|
||||
guest.SetStatus(self.UserCred, compute.VM_BACKUP_DELETE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionDelBackupServer,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
@@ -307,6 +306,11 @@ func (self *GuestDeleteTask) OnFailed(ctx context.Context, guest *models.SGuest,
|
||||
guest.SetStatus(self.UserCred, api.VM_DELETE_FAIL, err.String())
|
||||
db.OpsLog.LogEvent(guest, db.ACT_DELOCATE_FAIL, err, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_DELOCATE, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, err)
|
||||
}
|
||||
|
||||
@@ -354,14 +358,5 @@ func (self *GuestDeleteTask) DeleteGuest(ctx context.Context, guest *models.SGue
|
||||
}
|
||||
|
||||
func (self *GuestDeleteTask) NotifyServerDeleted(ctx context.Context, guest *models.SGuest) {
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionPendingDelete)
|
||||
guest.NotifyServerEvent(
|
||||
ctx,
|
||||
self.UserCred,
|
||||
notifyclient.SERVER_DELETED,
|
||||
notify.NotifyPriorityImportant,
|
||||
false, nil, false,
|
||||
)
|
||||
guest.NotifyAdminServerEvent(ctx, notifyclient.SERVER_DELETED_ADMIN, notify.NotifyPriorityImportant)
|
||||
// guest.EventNotify(ctx, self.UserCred, notifyclient.ActionPendingDelete)
|
||||
guest.EventNotify(ctx, self.UserCred, notifyclient.ActionPendingDelete)
|
||||
}
|
||||
|
||||
@@ -470,6 +470,11 @@ func (self *GuestMigrateTask) TaskFailed(ctx context.Context, guest *models.SGue
|
||||
logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_MIGRATE, reason, self.UserCred, false)
|
||||
self.SetStageFailed(ctx, reason)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_MIGRATE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionMigrate,
|
||||
IsFail: true,
|
||||
})
|
||||
}
|
||||
|
||||
//ManagedGuestMigrateTask
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
@@ -59,6 +58,11 @@ func (self *GuestRebuildRootTask) markFailed(ctx context.Context, guest *models.
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_REBUILD, reason, self.UserCred, false)
|
||||
guest.SetStatus(self.GetUserCred(), api.VM_REBUILD_ROOT_FAIL, reason.String())
|
||||
self.SGuestBaseTask.SetStageFailed(ctx, reason)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: guest,
|
||||
Action: notifyclient.ActionRebuildRoot,
|
||||
IsFail: true,
|
||||
})
|
||||
gds := guest.CategorizeDisks()
|
||||
imageId, _ := self.Params.GetString("origin_image_id")
|
||||
_, err := db.Update(gds.Root, func() error {
|
||||
@@ -176,15 +180,7 @@ func (self *GuestRebuildRootTask) OnRebuildAllDisksComplete(ctx context.Context,
|
||||
}
|
||||
}
|
||||
db.OpsLog.LogEvent(guest, db.ACT_REBUILD_ROOT, "", self.UserCred)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionRebuildRoot)
|
||||
guest.NotifyServerEvent(
|
||||
ctx,
|
||||
self.UserCred,
|
||||
notifyclient.SERVER_REBUILD_ROOT,
|
||||
notify.NotifyPriorityImportant,
|
||||
true, nil, false,
|
||||
)
|
||||
// guest.EventNotify(ctx, self.UserCred, notifyclient.ActionRebuildRoot)
|
||||
guest.EventNotify(ctx, self.UserCred, notifyclient.ActionRebuildRoot)
|
||||
self.SetStage("OnSyncStatusComplete", nil)
|
||||
guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ func (self *LoadbalancerCreateTask) taskFail(ctx context.Context, lb *models.SLo
|
||||
db.OpsLog.LogEvent(lb, db.ACT_ALLOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, lb, logclient.ACT_CREATE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, lb.Id, lb.Name, api.LB_CREATE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: lb,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
@@ -71,7 +76,10 @@ func (self *LoadbalancerCreateTask) OnLoadbalancerCreateCompleteFailed(ctx conte
|
||||
|
||||
func (self *LoadbalancerCreateTask) OnLoadbalancerStartComplete(ctx context.Context, lb *models.SLoadbalancer, data jsonutils.JSONObject) {
|
||||
lb.SetStatus(self.GetUserCred(), api.LB_STATUS_ENABLED, "")
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionCreate)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: lb,
|
||||
Action: notifyclient.ActionCreate,
|
||||
})
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ func (self *LoadbalancerDeleteTask) taskFail(ctx context.Context, lb *models.SLo
|
||||
lb.SetStatus(self.GetUserCred(), api.LB_STATUS_DELETE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(lb, db.ACT_DELOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, lb, logclient.ACT_DELOCATE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, lb.Id, lb.Name, api.LB_STATUS_DELETE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: lb,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
@@ -60,7 +64,11 @@ func (self *LoadbalancerDeleteTask) OnInit(ctx context.Context, obj db.IStandalo
|
||||
func (self *LoadbalancerDeleteTask) OnLoadbalancerDeleteComplete(ctx context.Context, lb *models.SLoadbalancer, data jsonutils.JSONObject) {
|
||||
db.OpsLog.LogEvent(lb, db.ACT_DELETE, lb.GetShortDesc(ctx), self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, lb, logclient.ACT_DELOCATE, nil, self.UserCred, true)
|
||||
notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionDelete)
|
||||
// notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionDelete)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: lb,
|
||||
Action: notifyclient.ActionDelete,
|
||||
})
|
||||
lb.DeleteEip(ctx, self.UserCred)
|
||||
lb.LBPendingDelete(ctx, self.GetUserCred())
|
||||
self.SetStageComplete(ctx, nil)
|
||||
|
||||
@@ -159,7 +159,11 @@ func (self *LoadbalancerListenerCreateTask) taskFail(ctx context.Context, lblis
|
||||
lblis.SetStatus(self.GetUserCred(), api.LB_CREATE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(lblis, db.ACT_ALLOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, lblis, logclient.ACT_CREATE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, lblis.Id, lblis.Name, api.LB_CREATE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: lblis,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ func (self *LoadbalancerListenerDeleteTask) taskFail(ctx context.Context, lblis
|
||||
lblis.SetStatus(self.GetUserCred(), api.LB_STATUS_DELETE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(lblis, db.ACT_DELOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, lblis, logclient.ACT_DELOCATE, reason, self.UserCred, false)
|
||||
notifyclient.NotifySystemErrorWithCtx(ctx, lblis.Id, lblis.Name, api.LB_STATUS_DELETE_FAILED, reason.String())
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: lblis,
|
||||
Action: notifyclient.ActionDelete,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,14 @@ type ConfigsManager struct {
|
||||
}
|
||||
|
||||
var (
|
||||
NotifyReceiver modulebase.ResourceManager
|
||||
NotifyConfig modulebase.ResourceManager
|
||||
Notification modulebase.ResourceManager
|
||||
NotifyTemplate modulebase.ResourceManager
|
||||
NotifySubscription modulebase.ResourceManager
|
||||
Configs ConfigsManager
|
||||
NotifyReceiver modulebase.ResourceManager
|
||||
NotifyConfig modulebase.ResourceManager
|
||||
NotifyRobot modulebase.ResourceManager
|
||||
Notification modulebase.ResourceManager
|
||||
NotifyTemplate modulebase.ResourceManager
|
||||
NotifyTopic modulebase.ResourceManager
|
||||
NotifySubscriber modulebase.ResourceManager
|
||||
Configs ConfigsManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -41,11 +43,19 @@ func init() {
|
||||
NotifyConfig = NewNotifyv2Manager(
|
||||
"notifyconfig",
|
||||
"notifyconfigs",
|
||||
[]string{"Type", "Content"},
|
||||
[]string{"Name", "Type", "Content", "Attribution", "Project_Domain"},
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifyConfig)
|
||||
|
||||
NotifyRobot = NewNotifyv2Manager(
|
||||
"robot",
|
||||
"robots",
|
||||
[]string{"ID", "Name", "Type", "Address", "Lang"},
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifyRobot)
|
||||
|
||||
Notification = NewNotifyv2Manager(
|
||||
"notification",
|
||||
"notifications",
|
||||
@@ -62,11 +72,19 @@ func init() {
|
||||
)
|
||||
register(&NotifyTemplate)
|
||||
|
||||
NotifySubscription = NewNotifyv2Manager(
|
||||
"subscription",
|
||||
"subscriptions",
|
||||
[]string{"ID", "Name", "Type", "Resource_Types", "Receivers", "Robot", "Webhook"},
|
||||
NotifyTopic = NewNotifyv2Manager(
|
||||
"topic",
|
||||
"topics",
|
||||
[]string{"ID", "Name", "Type", "Enabled", "Resources"},
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifySubscription)
|
||||
register(&NotifyTopic)
|
||||
|
||||
NotifySubscriber = NewNotifyv2Manager(
|
||||
"subscriber",
|
||||
"subscribers",
|
||||
[]string{"ID", "Name", "Topic_Id", "Type", "Resource_Scope", "Role_Scope", "Receivers", "Role", "Robot"},
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifySubscriber)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
NotifyByWebConsole = TNotifyChannel("webconsole")
|
||||
NotifyByFeishu = TNotifyChannel("feishu")
|
||||
NotifyByWorkwx = TNotifyChannel("workwx")
|
||||
NotifyByRobot = TNotifyChannel("robot")
|
||||
|
||||
NotifyFeishuRobot = TNotifyChannel("feishu-robot")
|
||||
NotifyByDingTalkRobot = TNotifyChannel("dingtalk-robot")
|
||||
|
||||
@@ -31,6 +31,7 @@ var (
|
||||
type SNotifyMessage struct {
|
||||
Uid []string `json:"uid,omitempty"`
|
||||
Gid []string `json:"gid,omitempty"`
|
||||
Robots []string `json:"robots,omitempty"`
|
||||
ContactType TNotifyChannel `json:"contact_type,omitempty"`
|
||||
Contacts []string `json:"contracts"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
@@ -46,6 +47,7 @@ type SNotifyMessage struct {
|
||||
type SNotifyV2Message struct {
|
||||
Receivers []string `json:"receivers"`
|
||||
Contacts []string `json:"contacts"`
|
||||
Robots []string `json:"robots"`
|
||||
ContactType string `json:"contact_type"`
|
||||
Topic string `json:"topic"`
|
||||
Priority string `json:"priority"`
|
||||
@@ -83,6 +85,7 @@ func (manager *NotificationManager) Send(s *mcclient.ClientSession, msg SNotifyM
|
||||
v2msg := SNotifyV2Message{
|
||||
Receivers: receiverIds,
|
||||
Contacts: msg.Contacts,
|
||||
Robots: msg.Robots,
|
||||
ContactType: string(msg.ContactType),
|
||||
Topic: msg.Topic,
|
||||
Priority: string(msg.Priority),
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 notify
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type ConfigListOptions struct {
|
||||
options.BaseListOptions
|
||||
Type string `json:"type"`
|
||||
Attribution string `json:"attribution"`
|
||||
}
|
||||
|
||||
func (cl *ConfigListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(cl)
|
||||
}
|
||||
|
||||
type ConfigCreateOptions struct {
|
||||
NAME string `help:"Name of Config"`
|
||||
Domain string `help:"which domain create for, required if attribution is 'domain'"`
|
||||
Type string `help:"The type of config"`
|
||||
Configs []string `help:"Config content, format: 'key:value'"`
|
||||
Attribution string `help:"Attribution" choices:"system|domain"`
|
||||
}
|
||||
|
||||
func (cc *ConfigCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
jo := jsonutils.Marshal(cc)
|
||||
d := jo.(*jsonutils.JSONDict)
|
||||
d.Remove("configs")
|
||||
configs := jsonutils.NewDict()
|
||||
for _, kv := range cc.Configs {
|
||||
index := strings.IndexByte(kv, ':')
|
||||
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
|
||||
}
|
||||
d.Set("content", configs)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
type ConfigOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (c *ConfigOptions) GetId() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
func (c *ConfigOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ConfigUpdateOptions struct {
|
||||
ConfigOptions
|
||||
Configs []string
|
||||
}
|
||||
|
||||
func (cu *ConfigUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
configs := jsonutils.NewDict()
|
||||
for _, kv := range cu.Configs {
|
||||
index := strings.IndexByte(kv, ':')
|
||||
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
|
||||
}
|
||||
d := jsonutils.NewDict()
|
||||
d.Set("content", configs)
|
||||
return d, nil
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func (rc *ReceiverCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
d := jsonutils.NewDict()
|
||||
d.Set("uid", jsonutils.NewString(rc.UID))
|
||||
d.Set("email", jsonutils.NewString(rc.Email))
|
||||
d.Set("enabled_contact_type", jsonutils.NewStringArray(rc.EnabledContactTypes))
|
||||
d.Set("enabled_contact_types", jsonutils.NewStringArray(rc.EnabledContactTypes))
|
||||
d.Add(jsonutils.NewString(rc.Mobile), "international_mobile", "mobile")
|
||||
d.Add(jsonutils.NewString(rc.MobileAreaCode), "international_mobile", "area_code")
|
||||
return d, nil
|
||||
@@ -101,6 +101,19 @@ func (rv *ReceiverVerifyOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(rv.SreceiverVerifyOptions), nil
|
||||
}
|
||||
|
||||
type ReceiverEnableContactTypeInput struct {
|
||||
ReceiverOptions
|
||||
SreceiverEnableContactTypeInput
|
||||
}
|
||||
|
||||
func (re *ReceiverEnableContactTypeInput) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(re.SreceiverEnableContactTypeInput), nil
|
||||
}
|
||||
|
||||
type SreceiverEnableContactTypeInput struct {
|
||||
EnabledContactTypes []string `help:"Enabled contact types"`
|
||||
}
|
||||
|
||||
type ReceiverIntellijGetOptions struct {
|
||||
USERID string `help:"user id in keystone" json:"user_id"`
|
||||
CreateIfNo *bool `help:"create if receiver with UserId does not exist"`
|
||||
@@ -110,3 +123,11 @@ type ReceiverIntellijGetOptions struct {
|
||||
func (ri *ReceiverIntellijGetOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(ri), nil
|
||||
}
|
||||
|
||||
type ReceiverGetTypeOptions struct {
|
||||
Domain string `help:"Domain under where available contact methods"`
|
||||
}
|
||||
|
||||
func (rg *ReceiverGetTypeOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(rg), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 notify
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type RobotListOptions struct {
|
||||
options.BaseListOptions
|
||||
Lang string
|
||||
Type string `choices:"feishu|dingtalk|workwx|webhook"`
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func (rl *RobotListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(rl)
|
||||
}
|
||||
|
||||
type RobotCreateOptions struct {
|
||||
NAME string
|
||||
Type string `choices:"feishu|dingtalk|workwx|webhook"`
|
||||
Address string
|
||||
Lang string
|
||||
}
|
||||
|
||||
func (rc *RobotCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(rc), nil
|
||||
}
|
||||
|
||||
type RobotOptions struct {
|
||||
ID string `help:"Id or Name of robot"`
|
||||
}
|
||||
|
||||
func (r *RobotOptions) GetId() string {
|
||||
return r.ID
|
||||
}
|
||||
|
||||
func (r *RobotOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type RobotUpdateOptions struct {
|
||||
RobotOptions
|
||||
SrobotUpdateOptions
|
||||
}
|
||||
|
||||
type SrobotUpdateOptions struct {
|
||||
Address string
|
||||
Lang string
|
||||
}
|
||||
|
||||
func (ru *RobotUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(ru.SrobotUpdateOptions), nil
|
||||
}
|
||||
@@ -14,84 +14,81 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type SubscriptionListOptions struct {
|
||||
type TopicListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
func (opts *SubscriptionListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
func (opts *TopicListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(opts)
|
||||
}
|
||||
|
||||
type SubscriptionOptions struct {
|
||||
ID string `help:"Id or Name of subscription"`
|
||||
type TopicOptions struct {
|
||||
ID string `help:"Id or Name of topic"`
|
||||
}
|
||||
|
||||
func (so *SubscriptionOptions) GetId() string {
|
||||
func (so *TopicOptions) GetId() string {
|
||||
return so.ID
|
||||
}
|
||||
|
||||
func (so *SubscriptionOptions) Params() (jsonutils.JSONObject, error) {
|
||||
func (so *TopicOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type SubscriptionSetReceiverOptions struct {
|
||||
SubscriptionOptions
|
||||
Receivers []string `json:"receivers"`
|
||||
RoleAndScopes []string `json:"role_and_scopes" help:"role and scope, separated by a colon, example: admin:system"`
|
||||
type SubscriberCreateOptions struct {
|
||||
TopicId string `positional:"true"`
|
||||
ResourceScope string `positional:"true" choices:"system|domain|project"`
|
||||
ResourceAttributionId string `help:"project id or domain id of resource"`
|
||||
Type string `positional:"true" choices:"receiver|robot|role"`
|
||||
Receivers []string `help:"required if type is 'receiver'"`
|
||||
Role string `help:"required if type is 'role'"`
|
||||
RoleScope string `help:"required if type is 'role'"`
|
||||
Robot string `help:"required if type is 'robot'"`
|
||||
Scope string `positional:"true"`
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetReceiverOptions) Params() (jsonutils.JSONObject, error) {
|
||||
d := jsonutils.NewDict()
|
||||
if len(opts.Receivers) > 0 {
|
||||
d.Set("receivers", jsonutils.NewStringArray(opts.Receivers))
|
||||
}
|
||||
if len(opts.RoleAndScopes) > 0 {
|
||||
ra := jsonutils.NewArray()
|
||||
for _, rs := range opts.RoleAndScopes {
|
||||
index := strings.Index(rs, ":")
|
||||
if index <= 0 {
|
||||
return nil, fmt.Errorf("invalid role_and_scope %q, example: %s", rs, "admin:system")
|
||||
}
|
||||
rd := jsonutils.NewDict()
|
||||
rd.Set("role", jsonutils.NewString(rs[:index]))
|
||||
rd.Set("scope", jsonutils.NewString(rs[index+1:]))
|
||||
ra.Add(rd)
|
||||
}
|
||||
d.Set("roles", ra)
|
||||
}
|
||||
return d, nil
|
||||
func (sc *SubscriberCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(sc), nil
|
||||
}
|
||||
|
||||
type SsubscriptionSetRobotOptions struct {
|
||||
ROBOT string `choices:"feishu-robot|dingtalk-robot|workwx-robot"`
|
||||
type SubscriberListOptions struct {
|
||||
options.BaseListOptions
|
||||
TopicId string
|
||||
ResourceScope string `choices:"system|domain|project"`
|
||||
Type string `choices:"receiver|robot|role"`
|
||||
SCOPE string `choices:"system|domain"`
|
||||
}
|
||||
|
||||
type SubscriptionSetRobotOptions struct {
|
||||
SubscriptionOptions
|
||||
SsubscriptionSetRobotOptions
|
||||
func (sl *SubscriberListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(sl)
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetRobotOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts.SsubscriptionSetRobotOptions), nil
|
||||
type SubscriberOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
type SsubscriptionSetWebhookOptions struct {
|
||||
WEBHOOK string `choices:"webhook"`
|
||||
func (s *SubscriberOptions) GetId() string {
|
||||
return s.ID
|
||||
}
|
||||
|
||||
type SubscriptionSetWebhookOptions struct {
|
||||
SubscriptionOptions
|
||||
SsubscriptionSetWebhookOptions
|
||||
func (s *SubscriberOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetWebhookOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts.SsubscriptionSetWebhookOptions), nil
|
||||
type SubscriberChangeOptions struct {
|
||||
SubscriberOptions
|
||||
Receivers []string
|
||||
Role string
|
||||
RoleScope string
|
||||
Robot string
|
||||
}
|
||||
|
||||
func (ssr *SubscriberChangeOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params := jsonutils.Marshal(ssr)
|
||||
params.(*jsonutils.JSONDict).Remove("id")
|
||||
return params, nil
|
||||
}
|
||||
|
||||
+16
-6
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
notify_apis "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
)
|
||||
@@ -27,10 +28,13 @@ type INotifyService interface {
|
||||
InitAll() error
|
||||
StopAll()
|
||||
UpdateServices(ctx context.Context, userCred mcclient.TokenCredential, isStart bool)
|
||||
RestartService(ctx context.Context, config SConfig, serviceName string)
|
||||
UpdateConfig(ctx context.Context, service string, config SConfig) error
|
||||
Send(ctx context.Context, contactType string, args apis.SendParams) error
|
||||
ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error)
|
||||
ContactByMobile(ctx context.Context, mobile, serviceName, domainId string) (string, error)
|
||||
BatchSend(ctx context.Context, contactType string, args apis.BatchSendParams) ([]*apis.FailedRecord, error)
|
||||
SendRobotMessage(ctx context.Context, rType string, receivers []*apis.SReceiver, title string, message string) ([]*apis.FailedRecord, error)
|
||||
AddConfig(ctx context.Context, service string, config SConfig) error
|
||||
DeleteConfig(ctx context.Context, service, domainId string) error
|
||||
ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error)
|
||||
}
|
||||
|
||||
@@ -53,15 +57,18 @@ type SBatchSendParams struct {
|
||||
}
|
||||
|
||||
type IServiceConfigStore interface {
|
||||
GetConfig(serviceName string) (SConfig, error)
|
||||
SetConfig(serviceName string, config SConfig) error
|
||||
GetConfigs(service string) ([]SConfig, error)
|
||||
GetConfig(service, domainId string) (SConfig, error)
|
||||
SetConfig(service string, config SConfig) error
|
||||
HasSystemConfig(service string) (bool, error)
|
||||
BatchCheckConfig(service string, domainIds []string) ([]bool, error)
|
||||
}
|
||||
|
||||
type SNotification struct {
|
||||
ContactType string
|
||||
Topic string
|
||||
Message string
|
||||
Event string
|
||||
Event notify_apis.SEvent
|
||||
AdvanceDays int
|
||||
}
|
||||
|
||||
@@ -70,7 +77,10 @@ type ITemplateStore interface {
|
||||
FillWithTemplate(ctx context.Context, lang string, notification SNotification) (params apis.SendParams, err error)
|
||||
}
|
||||
|
||||
type SConfig map[string]string
|
||||
type SConfig struct {
|
||||
Config map[string]string
|
||||
DomainId string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNoSuchMobile = errors.Error("no such mobile")
|
||||
|
||||
+204
-29
@@ -26,8 +26,10 @@ import (
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
@@ -40,6 +42,7 @@ import (
|
||||
|
||||
type SConfigManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
db.SDomainizedResourceBaseManager
|
||||
}
|
||||
|
||||
var ConfigManager *SConfigManager
|
||||
@@ -58,9 +61,11 @@ func init() {
|
||||
|
||||
type SConfig struct {
|
||||
db.SStandaloneResourceBase
|
||||
db.SDomainizedResourceBase
|
||||
|
||||
Type string `width:"15" nullable:"false" create:"required" get:"admin" list:"admin"`
|
||||
Content jsonutils.JSONObject `nullable:"false" create:"required" update:"admin" get:"admin" list:"admin"`
|
||||
Type string `width:"15" nullable:"false" create:"required" get:"domain" list:"domain"`
|
||||
Content jsonutils.JSONObject `nullable:"false" create:"required" update:"domain" get:"domain" list:"domain"`
|
||||
Attribution string `width:"8" nullable:"false" default:"system" get:"domain" list:"domain"`
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ConfigCreateInput) (api.ConfigCreateInput, error) {
|
||||
@@ -69,13 +74,28 @@ func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mccli
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
if !utils.IsInStringArray(input.Type, []string{api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WEBCONSOLE, api.WORKWX, api.FEISHU_ROBOT, api.DINGTALK_ROBOT, api.WORKWX_ROBOT, api.WEBHOOK}) {
|
||||
if len(input.ProjectDomainId) > 0 {
|
||||
_, input.DomainizedResourceInput, err = db.ValidateDomainizedResourceInput(ctx, input.DomainizedResourceInput)
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
}
|
||||
if !utils.IsInStringArray(input.Type, []string{api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WEBCONSOLE, api.WORKWX}) {
|
||||
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
|
||||
}
|
||||
if !utils.IsInStringArray(input.Attribution, []string{api.CONFIG_ATTRIBUTION_SYSTEM, api.CONFIG_ATTRIBUTION_DOMAIN}) {
|
||||
return input, httperrors.NewInputParameterError("invalid attribution, need %q or %q", api.CONFIG_ATTRIBUTION_SYSTEM, api.CONFIG_ATTRIBUTION_DOMAIN)
|
||||
}
|
||||
if input.Attribution == api.CONFIG_ATTRIBUTION_SYSTEM {
|
||||
allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), ConfigManager.KeywordPlural(), policy.PolicyActionCreate)
|
||||
if allowScope != rbacutils.ScopeSystem {
|
||||
return input, httperrors.NewInputParameterError("No permission to set %q attribution", api.CONFIG_ATTRIBUTION_SYSTEM)
|
||||
}
|
||||
}
|
||||
if input.Content == nil {
|
||||
return input, httperrors.NewMissingParameterError("content")
|
||||
}
|
||||
config, err := cm.GetConfigByType(input.Type)
|
||||
config, err := cm.Config(input.Type, input.ProjectDomainId)
|
||||
if err == nil && config != nil {
|
||||
return input, httperrors.NewDuplicateResourceError("duplicate type %q", input.Type)
|
||||
}
|
||||
@@ -104,6 +124,21 @@ func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mccli
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (c *SConfig) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
err := c.SStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Attribution == api.CONFIG_ATTRIBUTION_DOMAIN || c.Attribution == "" {
|
||||
c.Attribution = api.CONFIG_ATTRIBUTION_DOMAIN
|
||||
c.DomainId, _ = data.GetString("project_domain_id")
|
||||
if c.DomainId == "" {
|
||||
c.DomainId = userCred.GetProjectDomainId()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SConfig) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigUpdateInput) (api.ConfigUpdateInput, error) {
|
||||
// validate
|
||||
configs := make(map[string]string)
|
||||
@@ -129,7 +164,18 @@ func (c *SConfig) ValidateUpdateData(ctx context.Context, userCred mcclient.Toke
|
||||
}
|
||||
|
||||
func (c *SConfig) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
err := c.StartRepullSubcontactTask(ctx, userCred)
|
||||
c.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
configMap := make(map[string]string)
|
||||
err := c.Content.Unmarshal(&configMap)
|
||||
if err != nil {
|
||||
log.Errorf("unable to unmarshal: %v", err)
|
||||
return
|
||||
}
|
||||
NotifyService.AddConfig(ctx, c.Type, notifyv2.SConfig{
|
||||
Config: configMap,
|
||||
DomainId: c.DomainId,
|
||||
})
|
||||
err = c.StartRepullSubcontactTask(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
|
||||
}
|
||||
@@ -143,13 +189,27 @@ func (c *SConfig) PostUpdate(ctx context.Context, userCred mcclient.TokenCredent
|
||||
log.Errorf("unable to unmarshal: %v", err)
|
||||
return
|
||||
}
|
||||
NotifyService.RestartService(ctx, configMap, c.Type)
|
||||
NotifyService.UpdateConfig(ctx, c.Type, notifyv2.SConfig{
|
||||
Config: configMap,
|
||||
DomainId: c.DomainId,
|
||||
})
|
||||
err = c.StartRepullSubcontactTask(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SConfig) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
c.SStandaloneResourceBase.PreDelete(ctx, userCred)
|
||||
NotifyService.DeleteConfig(ctx, c.Type, c.DomainId)
|
||||
}
|
||||
func (c *SConfig) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
err := c.StartRepullSubcontactTask(ctx, userCred)
|
||||
if err != nil {
|
||||
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SConfig) StartRepullSubcontactTask(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "RepullSuncontactTask", c, userCred, nil, "", "")
|
||||
if err != nil {
|
||||
@@ -159,19 +219,8 @@ func (c *SConfig) StartRepullSubcontactTask(ctx context.Context, userCred mcclie
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) filterContactType(cTypes []string, robot string) []string {
|
||||
switch robot {
|
||||
case api.CTYPE_ROBOT_ONLY:
|
||||
return intersection(cTypes, RobotContactTypes)
|
||||
case api.CTYPE_ROBOT_YES:
|
||||
return cTypes
|
||||
default:
|
||||
return difference(cTypes, RobotContactTypes)
|
||||
}
|
||||
}
|
||||
|
||||
var sortedCTypes = []string{
|
||||
api.WEBCONSOLE, api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WORKWX, api.DINGTALK_ROBOT, api.FEISHU_ROBOT, api.WORKWX_ROBOT,
|
||||
api.WEBCONSOLE, api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WORKWX,
|
||||
}
|
||||
|
||||
func sortContactType(ctypes []string) []string {
|
||||
@@ -185,6 +234,25 @@ func sortContactType(ctypes []string) []string {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) availableContactTypes(domainId string) ([]string, error) {
|
||||
q := cm.Query("type")
|
||||
q = q.Filter(sqlchemy.OR(sqlchemy.AND(sqlchemy.Equals(q.Field("attribution"), api.CONFIG_ATTRIBUTION_DOMAIN), sqlchemy.Equals(q.Field("domain_id"), domainId)), sqlchemy.Equals(q.Field("attribution"), api.CONFIG_ATTRIBUTION_SYSTEM)))
|
||||
|
||||
allTypes := make([]struct {
|
||||
Type string
|
||||
}, 0, 3)
|
||||
err := q.All(&allTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]string, len(allTypes))
|
||||
for i := range ret {
|
||||
ret[i] = allTypes[i].Type
|
||||
}
|
||||
// De-duplication
|
||||
return sets.NewString(ret...).UnsortedList(), nil
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) allContactType() ([]string, error) {
|
||||
q := cm.Query("type")
|
||||
allTypes := make([]struct {
|
||||
@@ -206,10 +274,18 @@ func (self *SConfigManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQue
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q, err = self.SDomainizedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.DomainizedResourceListInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.NotEquals("type", api.WEBCONSOLE)
|
||||
if len(input.Type) > 0 {
|
||||
q.Filter(sqlchemy.Equals(q.Field("type"), input.Type))
|
||||
}
|
||||
if len(input.Attribution) > 0 {
|
||||
q = q.Equals("attribution", input.Attribution)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -226,9 +302,11 @@ func (cm *SConfigManager) FetchCustomizeColumns(
|
||||
isList bool,
|
||||
) []api.ConfigDetails {
|
||||
sRows := cm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
dRows := cm.SDomainizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]api.ConfigDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneResourceDetails = sRows[i]
|
||||
rows[i].DomainizedResourceInfo = dRows[i]
|
||||
}
|
||||
return rows
|
||||
}
|
||||
@@ -238,6 +316,10 @@ func (cm *SConfigManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field stri
|
||||
if err != nil {
|
||||
return q, nil
|
||||
}
|
||||
q, err = cm.SDomainizedResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err != nil {
|
||||
return q, nil
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -246,6 +328,10 @@ func (cm *SConfigManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q, err = cm.SDomainizedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DomainizedResourceListInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -378,7 +464,18 @@ func (self *SConfigManager) InitializeData() error {
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) ResourceScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeSystem
|
||||
return rbacutils.ScopeDomain
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
|
||||
switch scope {
|
||||
case rbacutils.ScopeDomain, rbacutils.ScopeProject:
|
||||
q = q.Equals("attribution", api.CONFIG_ATTRIBUTION_DOMAIN)
|
||||
if owner != nil {
|
||||
q = q.Equals("domain_id", owner.GetProjectDomainId())
|
||||
}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (cm *SConfigManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
@@ -398,36 +495,114 @@ func (c *SConfig) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCr
|
||||
}
|
||||
|
||||
// Fetch all SConfig struct which type is contactType.
|
||||
func (self *SConfigManager) GetConfigByType(contactType string) (*SConfig, error) {
|
||||
var config SConfig
|
||||
func (self *SConfigManager) Configs(contactType string) ([]SConfig, error) {
|
||||
var configs = make([]SConfig, 0, 2)
|
||||
q := self.Query()
|
||||
q.Filter(sqlchemy.Equals(q.Field("type"), contactType))
|
||||
err := q.All(&configs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fail to fetch SConfigs by type %s", contactType)
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) Config(contactType, domainId string) (*SConfig, error) {
|
||||
q := self.Query()
|
||||
q = q.Equals("type", contactType)
|
||||
if len(domainId) == 0 {
|
||||
q = q.Equals("attribution", api.CONFIG_ATTRIBUTION_SYSTEM)
|
||||
} else {
|
||||
q = q.Equals("domain_id", domainId).Equals("attribution", api.CONFIG_ATTRIBUTION_DOMAIN)
|
||||
}
|
||||
var config SConfig
|
||||
err := q.First(&config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to fetch SConfigs by type")
|
||||
return nil, errors.Wrapf(err, "fail to fetch SConfig by type %s and domain %s", contactType, domainId)
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) GetConfig(contactType string) (notifyv2.SConfig, error) {
|
||||
config, err := self.GetConfigByType(contactType)
|
||||
func (self *SConfigManager) HasSystemConfig(contactType string) (bool, error) {
|
||||
q := self.Query().Equals("type", contactType).Equals("attribution", "system")
|
||||
c, err := q.CountWithError()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return false, err
|
||||
}
|
||||
ret := make(map[string]string)
|
||||
err = config.Content.Unmarshal(&ret)
|
||||
return c > 0, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) BatchCheckConfig(contactType string, domainIds []string) ([]bool, error) {
|
||||
domainIdSet := sets.NewString(domainIds...)
|
||||
var configs = make([]SConfig, 0, 2)
|
||||
q := self.Query().Equals("type", contactType).Equals("attribution", "domain").In("domain_id", domainIdSet.UnsortedList())
|
||||
err := q.All(&configs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail unmarshal config content")
|
||||
return nil, errors.Wrapf(err, "fail to fetch SConfigs by type %s", contactType)
|
||||
}
|
||||
for i := range configs {
|
||||
if domainIdSet.Has(configs[i].DomainId) {
|
||||
domainIdSet.Delete(configs[i].DomainId)
|
||||
}
|
||||
}
|
||||
ret := make([]bool, len(domainIds))
|
||||
for i := range domainIds {
|
||||
if domainIdSet.Has(domainIds[i]) {
|
||||
// no config of domainId, use default one
|
||||
ret[i] = false
|
||||
}
|
||||
ret[i] = true
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) GetConfigs(contactType string) ([]notifyv2.SConfig, error) {
|
||||
configs, err := self.Configs(contactType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]notifyv2.SConfig, 0, len(configs))
|
||||
for i := range configs {
|
||||
c := make(map[string]string)
|
||||
err := configs[i].Content.Unmarshal(&c)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail unmarshal config content")
|
||||
}
|
||||
ret = append(ret, notifyv2.SConfig{
|
||||
Config: c,
|
||||
DomainId: configs[i].DomainId,
|
||||
})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) GetConfig(contactType, domainId string) (notifyv2.SConfig, error) {
|
||||
config, err := self.Config(contactType, domainId)
|
||||
if err != nil {
|
||||
return notifyv2.SConfig{}, err
|
||||
}
|
||||
ret := make(map[string]string)
|
||||
err = config.Content.Unmarshal(&ret)
|
||||
if err != nil {
|
||||
return notifyv2.SConfig{}, errors.Wrap(err, "fail unmarshal config content")
|
||||
}
|
||||
return notifyv2.SConfig{
|
||||
Config: ret,
|
||||
DomainId: config.DomainId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *SConfigManager) SetConfig(contactType string, config notifyv2.SConfig) error {
|
||||
content := jsonutils.Marshal(config)
|
||||
content := jsonutils.Marshal(config.Config)
|
||||
sConfig := &SConfig{
|
||||
Type: contactType,
|
||||
Content: content,
|
||||
}
|
||||
sConfig.DomainId = config.DomainId
|
||||
if sConfig.DomainId == "" {
|
||||
sConfig.Attribution = api.CONFIG_ATTRIBUTION_SYSTEM
|
||||
} else {
|
||||
sConfig.Attribution = api.CONFIG_ATTRIBUTION_DOMAIN
|
||||
}
|
||||
return self.TableSpec().InsertOrUpdate(context.Background(), sConfig)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
type SEventManager struct {
|
||||
db.SStandaloneAnonResourceBaseManager
|
||||
}
|
||||
|
||||
var EventManager *SEventManager
|
||||
|
||||
func init() {
|
||||
EventManager = &SEventManager{
|
||||
SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager(
|
||||
SEvent{},
|
||||
"events_tbl",
|
||||
"notifyevent",
|
||||
"notifyevents",
|
||||
),
|
||||
}
|
||||
EventManager.SetVirtualObject(EventManager)
|
||||
}
|
||||
|
||||
type SEvent struct {
|
||||
db.SStandaloneAnonResourceBase
|
||||
|
||||
Message string
|
||||
Event string `width:"32" nullable:"true"`
|
||||
AdvanceDays int
|
||||
}
|
||||
|
||||
func (e *SEventManager) CreateEvent(ctx context.Context, event, message string, advanceDays int) (*SEvent, error) {
|
||||
eve := &SEvent{
|
||||
Message: message,
|
||||
Event: event,
|
||||
AdvanceDays: advanceDays,
|
||||
}
|
||||
err := e.TableSpec().Insert(ctx, eve)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eve, nil
|
||||
}
|
||||
|
||||
func (e *SEventManager) GetEvent(id string) (*SEvent, error) {
|
||||
model, err := e.FetchById(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.(*SEvent), nil
|
||||
}
|
||||
@@ -79,12 +79,8 @@ func (lt *SLocalTemplateManager) detailsDisplay(resourceType string, details *js
|
||||
}
|
||||
|
||||
func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang string, no notifyv2.SNotification) (params rpcapi.SendParams, err error) {
|
||||
out := rpcapi.SendParams{}
|
||||
event, err := parseEvent(no.Event)
|
||||
if err != nil {
|
||||
return out, errors.Wrapf(err, "unable to parse event %q", no.Event)
|
||||
}
|
||||
rtStr, aStr := event.ResourceType(), string(event.Action())
|
||||
out, event := rpcapi.SendParams{}, no.Event
|
||||
rtStr, aStr, resultStr := event.ResourceType(), string(event.Action()), string(event.Result())
|
||||
dict, err := jsonutils.ParseString(no.Message)
|
||||
if err != nil {
|
||||
return out, errors.Wrapf(err, "unable to parse json from %q", no.Message)
|
||||
@@ -92,10 +88,11 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri
|
||||
webhookMsg := jsonutils.NewDict()
|
||||
webhookMsg.Set("resource_type", jsonutils.NewString(rtStr))
|
||||
webhookMsg.Set("action", jsonutils.NewString(aStr))
|
||||
webhookMsg.Set("result", jsonutils.NewString(resultStr))
|
||||
webhookMsg.Set("resource_details", dict)
|
||||
if no.ContactType == api.WEBHOOK {
|
||||
return rpcapi.SendParams{
|
||||
Title: no.Topic,
|
||||
Title: no.Event.StringWithDeli("_"),
|
||||
Message: webhookMsg.String(),
|
||||
}, nil
|
||||
}
|
||||
@@ -113,6 +110,10 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri
|
||||
if len(aDis) == 0 {
|
||||
aDis = aStr
|
||||
}
|
||||
resultDis := notifyclientI18nTable.LookupByLang(tag, resultStr)
|
||||
if len(resultDis) == 0 {
|
||||
resultDis = resultStr
|
||||
}
|
||||
|
||||
lt.detailsDisplay(rtStr, dict.(*jsonutils.JSONDict), tag)
|
||||
|
||||
@@ -120,6 +121,7 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri
|
||||
templateParams.Set("advance_days", jsonutils.NewInt(int64(no.AdvanceDays)))
|
||||
templateParams.Set("resource_type_display", jsonutils.NewString(rtDis))
|
||||
templateParams.Set("action_display", jsonutils.NewString(aDis))
|
||||
templateParams.Set("result_display", jsonutils.NewString(resultDis))
|
||||
|
||||
// get title
|
||||
title, err := lt.fillWithTemplate(ctx, "title", no.ContactType, lang, event, templateParams)
|
||||
@@ -148,9 +150,23 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri
|
||||
|
||||
var action2Topic = make(map[string]string, 0)
|
||||
|
||||
func specTopic(event api.SEvent) string {
|
||||
switch event.Action() {
|
||||
case api.ActionRebuildRoot, api.ActionChangeIpaddr, api.ActionResetPassword:
|
||||
return string(api.ActionUpdate)
|
||||
case api.ActionDelete:
|
||||
switch event.ResourceType() {
|
||||
case api.TOPIC_RESOURCE_BAREMETAL, api.TOPIC_RESOURCE_SERVER, api.TOPIC_RESOURCE_LOADBALANCER, api.TOPIC_RESOURCE_DBINSTANCE, api.TOPIC_RESOURCE_ELASTICCACHE:
|
||||
return "DELETE_WITH_IP"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
action2Topic[string(api.ActionRebuildRoot)] = string(api.ActionUpdate)
|
||||
action2Topic[string(api.ActionResetPassword)] = string(api.ActionUpdate)
|
||||
action2Topic[string(api.ActionChangeIpaddr)] = string(api.ActionUpdate)
|
||||
}
|
||||
|
||||
func (lt *SLocalTemplateManager) fillWithTemplate(ctx context.Context, titleOrContent string, contactType string, lang string, event api.SEvent, dis jsonutils.JSONObject) (string, error) {
|
||||
@@ -158,8 +174,11 @@ func (lt *SLocalTemplateManager) fillWithTemplate(ctx context.Context, titleOrCo
|
||||
tmpl *template.Template
|
||||
err error
|
||||
)
|
||||
actionStr := string(event.Action())
|
||||
for _, topic := range []string{event.String(), actionStr, action2Topic[actionStr], "common"} {
|
||||
actionResultStr := event.ActionWithResult("_")
|
||||
for _, topic := range []string{specTopic(event), event.StringWithDeli("_"), actionResultStr, "common"} {
|
||||
if topic == "" {
|
||||
continue
|
||||
}
|
||||
tmpl, err = lt.getTemplate(ctx, titleOrContent, contactType, topic, lang)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
continue
|
||||
@@ -182,12 +201,12 @@ func (lt *SLocalTemplateManager) fillWithTemplate(ctx context.Context, titleOrCo
|
||||
}
|
||||
|
||||
var specFields = map[string][]string{
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY: {
|
||||
notify.TOPIC_RESOURCE_SCALINGPOLICY: {
|
||||
"trigger_type",
|
||||
"action",
|
||||
"unit",
|
||||
},
|
||||
notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK: {
|
||||
notify.TOPIC_RESOURCE_SCHEDULEDTASK: {
|
||||
"resource_type",
|
||||
"operation",
|
||||
},
|
||||
@@ -212,8 +231,8 @@ func init() {
|
||||
stI18nTable.Set(comapi.ST_RESOURCE_OPERATION_STOP, i18n.NewTableEntry().EN("stop").CN("关机"))
|
||||
stI18nTable.Set(comapi.ST_RESOURCE_OPERATION_START, i18n.NewTableEntry().EN("start").CN("开机"))
|
||||
|
||||
specFieldTrans[notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY] = spI18nTable
|
||||
specFieldTrans[notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK] = stI18nTable
|
||||
specFieldTrans[notify.TOPIC_RESOURCE_SCALINGPOLICY] = spI18nTable
|
||||
specFieldTrans[notify.TOPIC_RESOURCE_SCHEDULEDTASK] = stI18nTable
|
||||
}
|
||||
|
||||
func (lt *SLocalTemplateManager) getTemplate(ctx context.Context, titleOrContent string, contactType string, topic string, lang string) (*template.Template, error) {
|
||||
@@ -307,90 +326,95 @@ func init() {
|
||||
api.TEMPLATE_LANG_CN,
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
api.TOPIC_RESOURCE_SERVER,
|
||||
"virtual machine",
|
||||
"虚拟机",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
api.TOPIC_RESOURCE_SCALINGGROUP,
|
||||
"scaling group",
|
||||
"弹性伸缩组",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SCALINGPOLICY,
|
||||
api.TOPIC_RESOURCE_SCALINGPOLICY,
|
||||
"scaling policy",
|
||||
"弹性伸缩策略",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
api.TOPIC_RESOURCE_IMAGE,
|
||||
"image",
|
||||
"系统镜像",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_DISK,
|
||||
api.TOPIC_RESOURCE_DISK,
|
||||
"disk",
|
||||
"硬盘",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
api.TOPIC_RESOURCE_SNAPSHOT,
|
||||
"snapshot",
|
||||
"硬盘快照",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
api.TOPIC_RESOURCE_INSTANCESNAPSHOT,
|
||||
"instance snapshot",
|
||||
"主机快照",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
api.TOPIC_RESOURCE_NETWORK,
|
||||
"network",
|
||||
"IP子网",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_EIP,
|
||||
api.TOPIC_RESOURCE_EIP,
|
||||
"EIP",
|
||||
"弹性公网IP",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SECGROUP,
|
||||
api.TOPIC_RESOURCE_SECGROUP,
|
||||
"security group",
|
||||
"安全组",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
api.TOPIC_RESOURCE_LOADBALANCER,
|
||||
"loadbalancer instance",
|
||||
"负载均衡实例",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
api.TOPIC_RESOURCE_LOADBALANCERACL,
|
||||
"loadbalancer ACL",
|
||||
"负载均衡访问控制",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
api.TOPIC_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
"loadbalancer certificate",
|
||||
"负载均衡证书",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
api.TOPIC_RESOURCE_BUCKET,
|
||||
"object storage bucket",
|
||||
"对象存储桶",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
api.TOPIC_RESOURCE_DBINSTANCE,
|
||||
"RDS instance",
|
||||
"RDS实例",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
api.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
"Redis instance",
|
||||
"Redis实例",
|
||||
},
|
||||
sI18nElme{
|
||||
api.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK,
|
||||
api.TOPIC_RESOURCE_SCHEDULEDTASK,
|
||||
"scheduled task",
|
||||
"定时任务",
|
||||
},
|
||||
sI18nElme{
|
||||
api.TOPIC_RESOURCE_BAREMETAL,
|
||||
"baremetal",
|
||||
"裸金属",
|
||||
},
|
||||
sI18nElme{
|
||||
string(api.ActionCreate),
|
||||
"created",
|
||||
@@ -436,5 +460,15 @@ func init() {
|
||||
"added to the recycle bin",
|
||||
"加入回收站",
|
||||
},
|
||||
sI18nElme{
|
||||
string(api.ResultFailed),
|
||||
"failed",
|
||||
"失败",
|
||||
},
|
||||
sI18nElme{
|
||||
string(api.ResultSucceed),
|
||||
"successfully",
|
||||
"成功",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ func InitDB() error {
|
||||
ConfigManager,
|
||||
TemplateManager,
|
||||
ReceiverNotificationManager,
|
||||
SubscriptionManager,
|
||||
TopicManager,
|
||||
RobotManager,
|
||||
} {
|
||||
err := manager.InitializeData()
|
||||
if err != nil {
|
||||
|
||||
@@ -67,12 +67,11 @@ type SNotification struct {
|
||||
Topic string `width:"128" nullable:"true" create:"required" search:"user"`
|
||||
Priority string `width:"16" nullable:"true" create:"optional" list:"user" get:"user"`
|
||||
// swagger:ignore
|
||||
Message string `create:"required"`
|
||||
ReceivedAt time.Time `nullable:"true" list:"user" get:"user"`
|
||||
Event string `width:"32" nullable:"true" create:"optional"`
|
||||
AdvanceDays int
|
||||
SendTimes int
|
||||
Tag string `width:"16" nullable:"true" index:"true" create:"optional"`
|
||||
Message string `create:"required"`
|
||||
ReceivedAt time.Time `nullable:"true" list:"user" get:"user"`
|
||||
EventId string `width:"128" nullable:"true"`
|
||||
SendTimes int
|
||||
Tag string `width:"16" nullable:"true" index:"true" create:"optional"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -80,25 +79,44 @@ const (
|
||||
)
|
||||
|
||||
func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NotificationCreateInput) (api.NotificationCreateInput, error) {
|
||||
if len(input.Tag) == 0 && utils.IsInStringArray(input.Tag, []string{api.NOTIFICATION_TAG_ALERT}) {
|
||||
if len(input.Tag) > 0 && !utils.IsInStringArray(input.Tag, []string{api.NOTIFICATION_TAG_ALERT}) {
|
||||
return input, httperrors.NewInputParameterError("invalid tag")
|
||||
}
|
||||
if len(input.Receivers) == 0 {
|
||||
if len(input.Contacts) > 0 {
|
||||
if !userCred.IsAllow(rbacutils.ScopeSystem, api.SERVICE_TYPE, nm.KeywordPlural(), policy.PolicyActionPerform, SendByContact) {
|
||||
return input, httperrors.NewForbiddenError("can't send notification by contact, need receiver")
|
||||
return input, httperrors.NewForbiddenError("only admin can send notification by contact")
|
||||
}
|
||||
if len(input.Contacts) == 0 {
|
||||
input.Contacts = []string{""}
|
||||
}
|
||||
}
|
||||
log.Infof("notify input: %s", jsonutils.Marshal(input))
|
||||
|
||||
// check contact type enabled
|
||||
allContactType, err := ConfigManager.allContactType()
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
if !utils.IsInStringArray(input.ContactType, allContactType) {
|
||||
return input, httperrors.NewInputParameterError("Unconfigured contact type %q", input.ContactType)
|
||||
// check robot
|
||||
if len(input.Robots) > 0 {
|
||||
input.ContactType = api.ROBOT
|
||||
robots, err := RobotManager.FetchByIdOrNames(ctx, input.Robots...)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "RobotManager.FetchByIdOrNames")
|
||||
}
|
||||
idSet := sets.NewString()
|
||||
nameSet := sets.NewString()
|
||||
for i := range robots {
|
||||
idSet.Insert(robots[i].Id)
|
||||
nameSet.Insert(robots[i].Name)
|
||||
}
|
||||
for _, re := range input.Receivers {
|
||||
if idSet.Has(re) || nameSet.Has(re) {
|
||||
continue
|
||||
}
|
||||
if !input.IgnoreNonexistentReceiver {
|
||||
return input, httperrors.NewInputParameterError("no such robot whose id is %q", re)
|
||||
}
|
||||
}
|
||||
input.Robots = idSet.UnsortedList()
|
||||
if len(input.Robots) == 0 {
|
||||
return input, httperrors.NewInputParameterError("no valid receiver or contact")
|
||||
}
|
||||
}
|
||||
// check receivers
|
||||
if len(input.Receivers) > 0 {
|
||||
@@ -138,10 +156,12 @@ func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred
|
||||
length = len(input.Topic)
|
||||
}
|
||||
name := fmt.Sprintf("%s-%s-%s", input.Topic[:length], input.ContactType, nowStr)
|
||||
var err error
|
||||
input.Name, err = db.GenerateName(ctx, nm, ownerId, name)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to generate name for %s", name)
|
||||
}
|
||||
log.Infof("after validatecreate input: %s", jsonutils.Marshal(input))
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -160,9 +180,15 @@ func (n *SNotification) CustomizeCreate(ctx context.Context, userCred mcclient.T
|
||||
}
|
||||
}
|
||||
for i := range input.Contacts {
|
||||
_, err := ReceiverNotificationManager.CreateWithoutReceiver(ctx, userCred, input.Contacts[i], n.Id)
|
||||
_, err := ReceiverNotificationManager.CreateContact(ctx, userCred, input.Contacts[i], n.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.Create")
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.CreateContact")
|
||||
}
|
||||
}
|
||||
for i := range input.Robots {
|
||||
_, err := ReceiverNotificationManager.CreateRobot(ctx, userCred, input.Robots[i], n.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.CreateRobot")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -193,7 +219,13 @@ func (nm *SNotificationManager) AllowPerformEventNotify(ctx context.Context, use
|
||||
|
||||
// TODO: support project and domain
|
||||
func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NotificationManagerEventNotifyInput) (api.NotificationManagerEventNotifyOutput, error) {
|
||||
log.Infof("default receiverIds: %s", input.ReceiverIds)
|
||||
var output api.NotificationManagerEventNotifyOutput
|
||||
// check event
|
||||
_, err := parseEvent(input.Event)
|
||||
if err != nil {
|
||||
return output, httperrors.NewInputParameterError("unable to parse event %q", input.Event)
|
||||
}
|
||||
// contact type
|
||||
contactTypes := input.ContactTypes
|
||||
cts, err := ConfigManager.allContactType()
|
||||
@@ -205,65 +237,62 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
}
|
||||
|
||||
// receiver
|
||||
subscriptions, err := SubscriptionManager.SubsciptionByEvent(input.Event, input.AdvanceDays)
|
||||
topics, err := TopicManager.TopicsByEvent(input.Event, input.AdvanceDays)
|
||||
if err != nil {
|
||||
return output, errors.Wrapf(err, "unable fetch subscriptions by event %q", input.Event)
|
||||
}
|
||||
if len(subscriptions) == 0 {
|
||||
if len(topics) == 0 {
|
||||
return output, nil
|
||||
}
|
||||
var receiverIds []string
|
||||
for i := range subscriptions {
|
||||
receiverIds1, err := SubscriptionReceiverManager.getReceivers(ctx, subscriptions[i].Id, input.ProjectDomainId, input.ProjectId)
|
||||
for i := range topics {
|
||||
receiverIds1, err := SubscriberManager.getReceiversSent(ctx, topics[i].Id, input.ProjectDomainId, input.ProjectId)
|
||||
if err != nil {
|
||||
return output, errors.Wrap(err, "unable to get receive")
|
||||
}
|
||||
log.Infof("receiver for topic: %s", receiverIds1)
|
||||
receiverIds = append(receiverIds, receiverIds1...)
|
||||
}
|
||||
// deduplication
|
||||
receiverIds = sets.NewString(receiverIds...).UnsortedList()
|
||||
|
||||
// robot
|
||||
var robots []string
|
||||
for i := range subscriptions {
|
||||
robot, err := SubscriptionReceiverManager.robot(subscriptions[i].Id)
|
||||
for i := range topics {
|
||||
_robots, err := SubscriberManager.robot(topics[i].Id, input.ProjectDomainId, input.ProjectId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != errors.ErrNotFound {
|
||||
return output, errors.Wrapf(err, "unable fetch robot of subscription %q", subscriptions[i].Id)
|
||||
return output, errors.Wrapf(err, "unable fetch robot of subscription %q", topics[i].Id)
|
||||
}
|
||||
} else {
|
||||
robots = append(robots, robot)
|
||||
robots = append(robots, _robots...)
|
||||
}
|
||||
}
|
||||
robots = intersection(robots, intersection(cts, RobotContactTypes))
|
||||
var webhookRobots []string
|
||||
if len(robots) > 0 {
|
||||
robots = sets.NewString(robots...).UnsortedList()
|
||||
}
|
||||
|
||||
// webhook
|
||||
var webhooks []string
|
||||
for i := range subscriptions {
|
||||
webhook, err := SubscriptionReceiverManager.webhook(subscriptions[i].Id)
|
||||
rs, err := RobotManager.FetchByIdOrNames(ctx, robots...)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != errors.ErrNotFound {
|
||||
return output, errors.Wrapf(err, "unable to fetch webhook of subscription %q", subscriptions[i].Id)
|
||||
return output, errors.Wrap(err, "unable to get robots")
|
||||
}
|
||||
robots, webhookRobots = make([]string, 0, len(rs)), make([]string, 0, 1)
|
||||
for i := range rs {
|
||||
if rs[i].Type == api.ROBOT_TYPE_WEBHOOK {
|
||||
webhookRobots = append(webhookRobots, rs[i].Id)
|
||||
} else {
|
||||
robots = append(robots, rs[i].Id)
|
||||
}
|
||||
} else {
|
||||
webhooks = append(webhooks, webhook)
|
||||
}
|
||||
}
|
||||
if len(webhooks) > 0 {
|
||||
webhooks = sets.NewString(webhooks...).UnsortedList()
|
||||
}
|
||||
|
||||
message := jsonutils.Marshal(input.ResourceDetails).String()
|
||||
|
||||
// append default receiver
|
||||
receiverIds = append(receiverIds, input.ReceiverIds...)
|
||||
// fillter non-existed receiver
|
||||
receivers, err := ReceiverManager.FetchByIdOrNames(ctx, receiverIds...)
|
||||
if err != nil {
|
||||
return output, errors.Wrap(err, "unable to fetch receivers by ids")
|
||||
}
|
||||
webconsoleContacts := make([]string, 0)
|
||||
webconsoleContacts := sets.NewString()
|
||||
idSet := sets.NewString()
|
||||
for i := range receivers {
|
||||
idSet.Insert(receivers[i].Id)
|
||||
@@ -272,12 +301,18 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
if idSet.Has(re) {
|
||||
continue
|
||||
}
|
||||
webconsoleContacts = append(webconsoleContacts, re)
|
||||
webconsoleContacts.Insert(re)
|
||||
}
|
||||
receiverIds = idSet.UnsortedList()
|
||||
|
||||
// create event
|
||||
event, err := EventManager.CreateEvent(ctx, input.Event, message, input.AdvanceDays)
|
||||
if err != nil {
|
||||
return output, errors.Wrap(err, "unable to create Event")
|
||||
}
|
||||
|
||||
// webconsole
|
||||
err = nm.create(ctx, userCred, api.WEBCONSOLE, receiverIds, webconsoleContacts, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
err = nm.create(ctx, userCred, api.WEBCONSOLE, receiverIds, webconsoleContacts.UnsortedList(), input.Priority, event.Id)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.WEBCONSOLE,
|
||||
@@ -286,40 +321,98 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
}
|
||||
// normal contact type
|
||||
for _, ct := range contactTypes {
|
||||
err := nm.create(ctx, userCred, ct, receiverIds, []string{}, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
if ct == api.MOBILE {
|
||||
continue
|
||||
}
|
||||
err := nm.create(ctx, userCred, ct, receiverIds, nil, input.Priority, event.Id)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: ct,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
err = nm.createWithWebhookRobots(ctx, userCred, webhookRobots, input.Priority, event.Id)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.WEBHOOK,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
// robot
|
||||
for _, robot := range robots {
|
||||
err := nm.create(ctx, userCred, robot, []string{}, []string{}, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: robot,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
err = nm.createWithRobots(ctx, userCred, robots, input.Priority, event.Id)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.ROBOT,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
// webhook
|
||||
for _, webhook := range webhooks {
|
||||
err := nm.create(ctx, userCred, webhook, []string{}, []string{}, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: webhook,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.TokenCredential, contactType string, receiverIds, contacts []string, priority, topic, message, event string, advanceDays int) error {
|
||||
func (nm *SNotificationManager) createWithWebhookRobots(ctx context.Context, userCred mcclient.TokenCredential, webhookRobotIds []string, priority, eventId string) error {
|
||||
if len(webhookRobotIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := &SNotification{
|
||||
ContactType: api.WEBHOOK,
|
||||
Priority: priority,
|
||||
ReceivedAt: time.Now(),
|
||||
EventId: eventId,
|
||||
}
|
||||
n.Id = db.DefaultUUIDGenerator()
|
||||
for i := range webhookRobotIds {
|
||||
_, err := ReceiverNotificationManager.CreateRobot(ctx, userCred, webhookRobotIds[i], n.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.CreateRobot")
|
||||
}
|
||||
}
|
||||
err := nm.TableSpec().Insert(ctx, n)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to insert Notification")
|
||||
}
|
||||
n.SetModelManager(nm, n)
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", n, userCred, nil, "", "")
|
||||
if err != nil {
|
||||
log.Errorf("NotificationSendTask newTask error %v", err)
|
||||
} else {
|
||||
task.ScheduleRun(nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) createWithRobots(ctx context.Context, userCred mcclient.TokenCredential, robotIds []string, priority, eventId string) error {
|
||||
if len(robotIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := &SNotification{
|
||||
ContactType: api.ROBOT,
|
||||
Priority: priority,
|
||||
ReceivedAt: time.Now(),
|
||||
EventId: eventId,
|
||||
}
|
||||
n.Id = db.DefaultUUIDGenerator()
|
||||
for i := range robotIds {
|
||||
_, err := ReceiverNotificationManager.CreateRobot(ctx, userCred, robotIds[i], n.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.CreateRobot")
|
||||
}
|
||||
}
|
||||
err := nm.TableSpec().Insert(ctx, n)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to insert Notification")
|
||||
}
|
||||
n.SetModelManager(nm, n)
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", n, userCred, nil, "", "")
|
||||
if err != nil {
|
||||
log.Errorf("NotificationSendTask newTask error %v", err)
|
||||
} else {
|
||||
task.ScheduleRun(nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.TokenCredential, contactType string, receiverIds, contacts []string, priority, eventId string) error {
|
||||
if len(receiverIds)+len(contacts) == 0 {
|
||||
log.Infof("%s: no send", contactType)
|
||||
return nil
|
||||
@@ -327,11 +420,9 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To
|
||||
|
||||
n := &SNotification{
|
||||
ContactType: contactType,
|
||||
Message: message,
|
||||
Priority: priority,
|
||||
ReceivedAt: time.Now(),
|
||||
Event: event,
|
||||
AdvanceDays: advanceDays,
|
||||
EventId: eventId,
|
||||
}
|
||||
n.Id = db.DefaultUUIDGenerator()
|
||||
err := nm.TableSpec().Insert(ctx, n)
|
||||
@@ -345,9 +436,9 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To
|
||||
}
|
||||
}
|
||||
for i := range contacts {
|
||||
_, err := ReceiverNotificationManager.CreateWithoutReceiver(ctx, userCred, contacts[i], n.Id)
|
||||
_, err := ReceiverNotificationManager.CreateContact(ctx, userCred, contacts[i], n.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.Create")
|
||||
return errors.Wrap(err, "ReceiverNotificationManager.CreateContact")
|
||||
}
|
||||
}
|
||||
log.Infof("start NotificationSendTask for %s", contactType)
|
||||
@@ -426,6 +517,7 @@ func (n *SNotification) ReceiveDetails(userCred mcclient.TokenCredential, scope
|
||||
q.AppendField(subRQ.Field("name", "receiver_name"))
|
||||
q = q.Join(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
|
||||
}
|
||||
q.DebugQuery()
|
||||
ret := make([]api.ReceiveDetail, 0, 2)
|
||||
err := q.All(&ret)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
@@ -438,7 +530,11 @@ func (n *SNotification) ReceiveDetails(userCred mcclient.TokenCredential, scope
|
||||
func (n *SNotification) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
|
||||
// get title adn content
|
||||
lang := getLangSuffix(ctx)
|
||||
p, err := n.TemplateStore().FillWithTemplate(ctx, lang, n.Notification())
|
||||
nn, err := n.Notification()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
p, err := n.TemplateStore().FillWithTemplate(ctx, lang, nn)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
@@ -454,14 +550,26 @@ func (n *SNotification) getMoreDetails(ctx context.Context, userCred mcclient.To
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (n *SNotification) Notification() notifyv2.SNotification {
|
||||
func (n *SNotification) Notification() (notifyv2.SNotification, error) {
|
||||
if n.EventId == "" {
|
||||
return notifyv2.SNotification{
|
||||
ContactType: n.ContactType,
|
||||
Topic: n.Topic,
|
||||
Message: n.Message,
|
||||
}, nil
|
||||
}
|
||||
event, err := EventManager.GetEvent(n.EventId)
|
||||
if err != nil {
|
||||
return notifyv2.SNotification{}, err
|
||||
}
|
||||
e, _ := parseEvent(event.Event)
|
||||
return notifyv2.SNotification{
|
||||
ContactType: n.ContactType,
|
||||
Topic: n.Topic,
|
||||
Message: n.Message,
|
||||
Event: n.Event,
|
||||
AdvanceDays: n.AdvanceDays,
|
||||
}
|
||||
Message: event.Message,
|
||||
Event: e,
|
||||
AdvanceDays: event.AdvanceDays,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
@@ -710,7 +818,7 @@ func (nm *SNotificationManager) ReSend(ctx context.Context, userCred mcclient.To
|
||||
}
|
||||
|
||||
func (n *SNotification) TemplateStore() notifyv2.ITemplateStore {
|
||||
if len(n.Event) == 0 || n.ContactType == api.MOBILE {
|
||||
if len(n.EventId) == 0 || n.ContactType == api.MOBILE {
|
||||
return TemplateManager
|
||||
}
|
||||
return LocalTemplateManager
|
||||
|
||||
@@ -284,6 +284,10 @@ func (rm *SReceiverManager) ValidateCreateData(ctx context.Context, userCred mcc
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (r *SReceiver) IsEnabled() bool {
|
||||
return r.Enabled.Bool()
|
||||
}
|
||||
|
||||
var LaxMobileRegexp = regexp.MustCompile(`[0-9]{6,14}`)
|
||||
|
||||
func (r *SReceiver) IsEnabledContactType(ct string) (bool, error) {
|
||||
@@ -689,13 +693,18 @@ func (r *SReceiverManager) AllowPerformGetTypes(ctx context.Context, userCred mc
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *SReceiverManager) PerformGetTypes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigManagerGetTypesInput) (api.ConfigManagerGetTypesOutput, error) {
|
||||
func (rm *SReceiverManager) PerformGetTypes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigManagerGetTypesInput) (api.ConfigManagerGetTypesOutput, error) {
|
||||
output := api.ConfigManagerGetTypesOutput{}
|
||||
allContactType, err := ConfigManager.allContactType()
|
||||
t, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, input.Domain)
|
||||
if err != nil {
|
||||
return output, err
|
||||
return output, errors.Wrap(err, "unable to FetchDomainByIdOrName")
|
||||
}
|
||||
output.Types = sortContactType(ConfigManager.filterContactType(allContactType, input.Robot))
|
||||
ret, err := ConfigManager.availableContactTypes(t.Id)
|
||||
if err != nil {
|
||||
return output, errors.Wrap(err, "unable to get available contact types")
|
||||
}
|
||||
|
||||
output.Types = sortContactType(ret)
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@@ -906,9 +915,28 @@ func (r *SReceiver) Delete(ctx context.Context, userCred mcclient.TokenCredentia
|
||||
return err
|
||||
}
|
||||
}
|
||||
r.deleteReceiverInSubscriber(ctx)
|
||||
return r.SStatusStandaloneResourceBase.Delete(ctx, userCred)
|
||||
}
|
||||
|
||||
func (r *SReceiver) deleteReceiverInSubscriber(ctx context.Context) {
|
||||
q := SubscriberReceiverManager.Query().Equals("receiver_id", r.Id)
|
||||
srs := make([]SSubscriberReceiver, 0, 2)
|
||||
err := db.FetchModelObjects(SubscriberReceiverManager, q, &srs)
|
||||
if err != nil {
|
||||
log.Infof("unable to get SubscriberReceiver: %s", err.Error())
|
||||
}
|
||||
for i := range srs {
|
||||
sr := &srs[i]
|
||||
_, err := db.Update(sr, func() error {
|
||||
return sr.MarkDelete()
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("unable to delete subscriber receiver for receiver %q", r.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SReceiver) IsOwner(userCred mcclient.TokenCredential) bool {
|
||||
return r.Id == userCred.GetUserId()
|
||||
}
|
||||
@@ -1183,12 +1211,10 @@ func (rm *SReceiverManager) FetchByIDs(ctx context.Context, ids ...string) ([]SR
|
||||
return contacts, nil
|
||||
}
|
||||
|
||||
func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SReceiver, error) {
|
||||
func idOrNameFilter(q *sqlchemy.SQuery, idOrNames ...string) *sqlchemy.SQuery {
|
||||
if len(idOrNames) == 0 {
|
||||
return nil, nil
|
||||
return q
|
||||
}
|
||||
var err error
|
||||
q := rm.Query()
|
||||
var conds []sqlchemy.ICondition
|
||||
for _, idOrName := range idOrNames {
|
||||
conds = append(conds, sqlchemy.Equals(q.Field("name"), idOrName))
|
||||
@@ -1201,6 +1227,15 @@ func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...s
|
||||
} else if len(conds) > 1 {
|
||||
q = q.Filter(sqlchemy.OR(conds...))
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SReceiver, error) {
|
||||
if len(idOrNames) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
q := idOrNameFilter(rm.Query(), idOrNames...)
|
||||
receivers := make([]SReceiver, 0, len(idOrNames))
|
||||
err = db.FetchModelObjects(rm, q, &receivers)
|
||||
if err != nil {
|
||||
@@ -1255,3 +1290,32 @@ func (r *SReceiver) GetContact(cType string) (string, error) {
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (r *SReceiver) GetDomainId() string {
|
||||
return r.DomainId
|
||||
}
|
||||
|
||||
func (r *SReceiver) AllowPerformEnableContactType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "enable-contact-type")
|
||||
}
|
||||
|
||||
func (r *SReceiver) PerformEnableContactType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ReceiverEnableContactTypeInput) (jsonutils.JSONObject, error) {
|
||||
err := r.PullCache(false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to pull cache")
|
||||
}
|
||||
err = r.SetEnabledContactTypes(input.EnabledContactTypes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to set enabled contact types")
|
||||
}
|
||||
err = r.PushCache(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to push cache")
|
||||
}
|
||||
r.SetStatus(userCred, api.RECEIVER_STATUS_PULLING, "")
|
||||
err = r.StartSubcontactPullTask(ctx, userCred, nil, "")
|
||||
if err != nil {
|
||||
log.Errorf("unable to StartSubcontactPullTask: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
@@ -43,6 +45,7 @@ type SReceiverNotification struct {
|
||||
NotificationID string `width:"128" charset:"ascii" nullable:"false" index:"true"`
|
||||
// ignore if ReceiverID is not empty or default
|
||||
Contact string `width:"128" nullable:"false" index:"true"`
|
||||
ReceiverType string `width:"16"`
|
||||
SendAt time.Time `nullable:"false"`
|
||||
SendBy string `width:"128" nullable:"false"`
|
||||
Status string `width:"36" charset:"ascii"`
|
||||
@@ -57,6 +60,18 @@ func (rnm *SReceiverNotificationManager) Create(ctx context.Context, userCred mc
|
||||
rn := &SReceiverNotification{
|
||||
ReceiverID: receiverID,
|
||||
NotificationID: notificationID,
|
||||
ReceiverType: api.RECEIVER_TYPE_USER,
|
||||
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
|
||||
SendBy: userCred.GetUserId(),
|
||||
}
|
||||
return rn, rnm.TableSpec().Insert(ctx, rn)
|
||||
}
|
||||
|
||||
func (rnm *SReceiverNotificationManager) CreateRobot(ctx context.Context, userCred mcclient.TokenCredential, RobotID, notificationID string) (*SReceiverNotification, error) {
|
||||
rn := &SReceiverNotification{
|
||||
ReceiverID: RobotID,
|
||||
NotificationID: notificationID,
|
||||
ReceiverType: api.RECEIVER_TYPE_ROBOT,
|
||||
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
|
||||
SendBy: userCred.GetUserId(),
|
||||
}
|
||||
@@ -71,10 +86,10 @@ func (rnm *SReceiverNotificationManager) GetSlaveFieldName() string {
|
||||
return "receiver_id"
|
||||
}
|
||||
|
||||
func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
|
||||
func (rnm *SReceiverNotificationManager) CreateContact(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
|
||||
rn := &SReceiverNotification{
|
||||
NotificationID: notificationID,
|
||||
ReceiverID: ReceiverIdDefault,
|
||||
ReceiverType: api.RECEIVER_TYPE_CONTACT,
|
||||
Contact: contact,
|
||||
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
|
||||
SendBy: userCred.GetUserId(),
|
||||
@@ -82,7 +97,25 @@ func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Conte
|
||||
return rn, rnm.TableSpec().Insert(ctx, rn)
|
||||
}
|
||||
|
||||
func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
|
||||
func (rnm *SReceiverNotificationManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration {
|
||||
if r.Method == http.MethodGet && len(r.URL.Query().Get("export_keys")) > 0 {
|
||||
return time.Hour * 2
|
||||
}
|
||||
return -time.Second
|
||||
}
|
||||
|
||||
// func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
|
||||
// q := ReceiverManager.Query().Equals("id", rn.ReceiverID)
|
||||
// var receiver SReceiver
|
||||
// err := q.First(&receiver)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// receiver.SetModelManager(ReceiverManager, &receiver)
|
||||
// return &receiver, nil
|
||||
// }
|
||||
|
||||
func (rn *SReceiverNotification) receiver() (*SReceiver, error) {
|
||||
q := ReceiverManager.Query().Equals("id", rn.ReceiverID)
|
||||
var receiver SReceiver
|
||||
err := q.First(&receiver)
|
||||
@@ -93,6 +126,34 @@ func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
|
||||
return &receiver, nil
|
||||
}
|
||||
|
||||
func (rn *SReceiverNotification) robot() (*SRobot, error) {
|
||||
q := RobotManager.Query().Equals("id", rn.ReceiverID)
|
||||
var robot SRobot
|
||||
err := q.First(&robot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
robot.SetModelManager(RobotManager, &robot)
|
||||
return &robot, nil
|
||||
}
|
||||
|
||||
func (rn *SReceiverNotification) Receiver() (IReceiver, error) {
|
||||
switch rn.ReceiverType {
|
||||
case api.RECEIVER_TYPE_USER:
|
||||
return rn.receiver()
|
||||
case api.RECEIVER_TYPE_CONTACT:
|
||||
return &SContact{contact: rn.Contact}, nil
|
||||
case api.RECEIVER_TYPE_ROBOT:
|
||||
return rn.robot()
|
||||
default:
|
||||
// compatible
|
||||
if rn.ReceiverID != "" && rn.ReceiverID != ReceiverIdDefault {
|
||||
return rn.receiver()
|
||||
}
|
||||
return &SContact{contact: rn.Contact}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (rn *SReceiverNotification) BeforeSend(ctx context.Context, sendTime time.Time) error {
|
||||
if sendTime.IsZero() {
|
||||
sendTime = time.Now()
|
||||
@@ -117,3 +178,44 @@ func (rn *SReceiverNotification) AfterSend(ctx context.Context, success bool, re
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
type IReceiver interface {
|
||||
IsEnabled() bool
|
||||
GetDomainId() string
|
||||
IsEnabledContactType(string) (bool, error)
|
||||
IsVerifiedContactType(string) (bool, error)
|
||||
GetContact(string) (string, error)
|
||||
GetTemplateLang(context.Context) (string, error)
|
||||
}
|
||||
|
||||
type SReceiverBase struct {
|
||||
}
|
||||
|
||||
func (s SReceiverBase) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s SReceiverBase) GetDomainId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s SReceiverBase) IsEnabledContactType(_ string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s SReceiverBase) IsVerifiedContactType(_ string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s SReceiverBase) GetTemplateLang(ctx context.Context) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type SContact struct {
|
||||
SReceiverBase
|
||||
contact string
|
||||
}
|
||||
|
||||
func (s *SContact) GetContact(_ string) (string, error) {
|
||||
return s.contact, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
idenapi "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
rpcapi "yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type SRobotManager struct {
|
||||
db.SSharableVirtualResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
var RobotManager *SRobotManager
|
||||
|
||||
func init() {
|
||||
RobotManager = &SRobotManager{
|
||||
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
|
||||
SRobot{},
|
||||
"robots_tbl",
|
||||
"robot",
|
||||
"robots",
|
||||
),
|
||||
}
|
||||
RobotManager.SetVirtualObject(RobotManager)
|
||||
}
|
||||
|
||||
type SRobot struct {
|
||||
db.SSharableVirtualResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"`
|
||||
Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
}
|
||||
|
||||
func (rm *SRobotManager) InitializeData() error {
|
||||
log.Infof("start to init data for notify robot")
|
||||
// convert robot config
|
||||
q := ConfigManager.Query().In("type", append(RobotContactTypes, api.WEBHOOK))
|
||||
var configs []SConfig
|
||||
err := db.FetchModelObjects(ConfigManager, q, &configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(configs) == 0 {
|
||||
return nil
|
||||
}
|
||||
robots := make([]SRobot, len(configs))
|
||||
for i := range configs {
|
||||
webhook, _ := configs[i].Content.GetString("webhook")
|
||||
robot := SRobot{
|
||||
Address: webhook,
|
||||
Lang: "zh_CN",
|
||||
}
|
||||
robot.IsPublic = true
|
||||
robot.PublicScope = string(rbacutils.ScopeSystem)
|
||||
robot.DomainId = idenapi.DEFAULT_DOMAIN_ID
|
||||
robot.Status = api.RECEIVER_STATUS_READY
|
||||
switch configs[i].Type {
|
||||
case api.FEISHU_ROBOT:
|
||||
robot.Type = api.ROBOT_TYPE_FEISHU
|
||||
robot.Name = "Feishu Robot"
|
||||
case api.DINGTALK_ROBOT:
|
||||
robot.Type = api.ROBOT_TYPE_DINGTALK
|
||||
robot.Name = "Dingtalk Robot"
|
||||
case api.WORKWX_ROBOT:
|
||||
robot.Type = api.ROBOT_TYPE_WORKWX
|
||||
robot.Name = "Workwx Robot"
|
||||
case api.WEBHOOK:
|
||||
robot.Type = api.ROBOT_TYPE_WEBHOOK
|
||||
robot.Name = "Webhook"
|
||||
addresses := strings.Split(robot.Address, ",")
|
||||
for i := 1; i < len(addresses); i++ {
|
||||
robotn := robot
|
||||
robotn.Address = strings.TrimSpace(addresses[i])
|
||||
robots = append(robots, robotn)
|
||||
}
|
||||
robot.Address = addresses[0]
|
||||
}
|
||||
robots = append(robots, robot)
|
||||
}
|
||||
ctx := context.Background()
|
||||
var webhookRobotId string
|
||||
// insert new robot
|
||||
for i := range robots {
|
||||
err := rm.TableSpec().Insert(ctx, &robots[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if robots[i].Type == api.ROBOT_TYPE_WEBHOOK {
|
||||
webhookRobotId = robots[i].Id
|
||||
}
|
||||
}
|
||||
// delete old configs
|
||||
for i := range configs {
|
||||
config := &configs[i]
|
||||
_, err := db.Update(config, func() error {
|
||||
return config.MarkDelete()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// add webhook Robot to subscriber
|
||||
if len(webhookRobotId) > 0 {
|
||||
topics := make([]STopic, 0, 3)
|
||||
q = TopicManager.Query().In("name", []string{DefaultResourceCreateDelete, DefaultResourceChangeConfig, DefaultResourceUpdate})
|
||||
err := db.FetchModelObjects(TopicManager, q, &topics)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to fetch topics")
|
||||
}
|
||||
// create subscribers
|
||||
for i := range topics {
|
||||
subscriber := SSubscriber{
|
||||
TopicID: topics[i].Id,
|
||||
Type: api.SUBSCRIBER_TYPE_ROBOT,
|
||||
Identification: webhookRobotId,
|
||||
ResourceScope: "system",
|
||||
Scope: "system",
|
||||
}
|
||||
subscriber.Enabled = tristate.True
|
||||
err := SubscriberManager.TableSpec().Insert(ctx, &subscriber)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to create subscriber %s", jsonutils.Marshal(subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rm *SRobotManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.RobotCreateInput) (api.RobotCreateInput, error) {
|
||||
var err error
|
||||
input.SharableVirtualResourceCreateInput, err = rm.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ValidateCreateData")
|
||||
}
|
||||
// check type
|
||||
if !utils.IsInStringArray(input.Type, []string{api.ROBOT_TYPE_FEISHU, api.ROBOT_TYPE_WORKWX, api.ROBOT_TYPE_DINGTALK, api.ROBOT_TYPE_WEBHOOK}) {
|
||||
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
|
||||
}
|
||||
// check lang
|
||||
_, err = language.Parse(input.Lang)
|
||||
if err != nil {
|
||||
return input, httperrors.NewInputParameterError("invalid lang %q: %s", input.Lang, err.Error())
|
||||
}
|
||||
// check Address
|
||||
records, err := NotifyService.SendRobotMessage(ctx, input.Type, []*rpcapi.SReceiver{
|
||||
{
|
||||
Contact: input.Address,
|
||||
DomainId: input.ProjectDomainId,
|
||||
},
|
||||
}, "Validate", "This is a verification message, please ignore.")
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "unable to validate address")
|
||||
}
|
||||
if len(records) > 0 {
|
||||
return input, httperrors.NewInputParameterError("invalid address: %s", records[0].Reason)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) Receiver() *rpcapi.SReceiver {
|
||||
return &rpcapi.SReceiver{
|
||||
Contact: r.Address,
|
||||
DomainId: r.DomainId,
|
||||
}
|
||||
}
|
||||
|
||||
func (rm *SRobotManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.RobotDetails {
|
||||
sRows := rm.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]api.RobotDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].SharableVirtualResourceDetails = sRows[i]
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (rm *SRobotManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.RobotListInput) (*sqlchemy.SQuery, error) {
|
||||
q, err := rm.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, input.SharableResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q, err = rm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(input.Type) > 0 {
|
||||
q = q.Equals("type", input.Type)
|
||||
}
|
||||
if len(input.Lang) > 0 {
|
||||
q = q.Equals("lang", input.Lang)
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RobotUpdateInput) (api.RobotUpdateInput, error) {
|
||||
var err error
|
||||
input.SharableVirtualResourceBaseUpdateInput, err = r.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
|
||||
}
|
||||
// check lang
|
||||
_, err = language.Parse(input.Lang)
|
||||
if err != nil {
|
||||
return input, httperrors.NewInputParameterError("invalid lang %q: %s", input.Lang, err.Error())
|
||||
}
|
||||
// check Address
|
||||
records, err := NotifyService.SendRobotMessage(ctx, r.Type, []*rpcapi.SReceiver{
|
||||
{
|
||||
Contact: input.Address,
|
||||
DomainId: r.DomainId,
|
||||
},
|
||||
}, "Validate", "This is a verification message, please ignore.")
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "unable to validate address")
|
||||
}
|
||||
if len(records) > 0 {
|
||||
return input, httperrors.NewInputParameterError("invalid address: %s", records[0].Reason)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
err := r.SSharableVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Enabled = tristate.True
|
||||
r.Status = api.ROBOT_STATUS_READY
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rm *SRobotManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SRobot, error) {
|
||||
if len(idOrNames) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
q := idOrNameFilter(rm.Query(), idOrNames...)
|
||||
robots := make([]SRobot, 0, len(idOrNames))
|
||||
err = db.FetchModelObjects(rm, q, &robots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return robots, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) IsEnabled() bool {
|
||||
return r.Enabled.Bool()
|
||||
}
|
||||
|
||||
func (r *SRobot) IsEnabledContactType(ctype string) (bool, error) {
|
||||
return ctype == api.ROBOT || ctype == api.WEBHOOK, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) IsVerifiedContactType(ctype string) (bool, error) {
|
||||
return ctype == api.ROBOT || ctype == api.WEBHOOK, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) GetContact(ctype string) (string, error) {
|
||||
return r.Address, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) GetTemplateLang(ctx context.Context) (string, error) {
|
||||
lang, err := language.Parse(r.Lang)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to prase language %q", r.Lang)
|
||||
}
|
||||
tLang := notifyclientI18nTable.LookupByLang(lang, tempalteLang)
|
||||
return tLang, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) GetDomainId() string {
|
||||
return r.DomainId
|
||||
}
|
||||
|
||||
func (r *SRobot) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "enable")
|
||||
}
|
||||
|
||||
func (r *SRobot) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(r, ctx, userCred, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "disable")
|
||||
}
|
||||
|
||||
func (r *SRobot) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(r, ctx, userCred, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *SRobot) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
q := SubscriberManager.Query().Equals("type", notify.SUBSCRIBER_TYPE_ROBOT).Equals("identification", r.GetId())
|
||||
subscribers := make([]SSubscriber, 0, 2)
|
||||
err := db.FetchModelObjects(SubscriberManager, q, &subscribers)
|
||||
if err != nil {
|
||||
log.Errorf("unable to fetch subscribers with robot %q", r.GetId())
|
||||
return
|
||||
}
|
||||
for i := range subscribers {
|
||||
subscriber := &subscribers[i]
|
||||
_, err := db.Update(r, func() error {
|
||||
return subscriber.MarkDelete()
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("unable to delete subscriber %q because of deleting of robot %q", subscribers[i].GetId(), r.GetId())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
// 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"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"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/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
var SubscriberManager *SSubscriberManager
|
||||
|
||||
func init() {
|
||||
SubscriberManager = &SSubscriberManager{
|
||||
SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager(
|
||||
SSubscriber{},
|
||||
"subscriber_tbl",
|
||||
"subscriber",
|
||||
"subscribers",
|
||||
),
|
||||
}
|
||||
SubscriberManager.SetVirtualObject(ReceiverNotificationManager)
|
||||
}
|
||||
|
||||
type SSubscriberManager struct {
|
||||
db.SStandaloneAnonResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriber struct {
|
||||
db.SStandaloneAnonResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
TopicID string `width:"128" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"`
|
||||
Type string `width:"16" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"`
|
||||
Identification string `width:"128" charset:"ascii" nullable:"false" index:"true"`
|
||||
RoleScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"`
|
||||
ResourceScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"required"`
|
||||
ResourceAttributionId string `width:"128" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"`
|
||||
Scope string `width:"128" charset:"ascii" nullable:"false" create:"required"`
|
||||
DomainId string `width:"128" charset:"ascii" nullable:"false" create:"optional"`
|
||||
}
|
||||
|
||||
func (sm *SSubscriberManager) validateReceivers(ctx context.Context, receivers []string) ([]string, error) {
|
||||
rs, err := ReceiverManager.FetchByIdOrNames(ctx, receivers...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch Receivers")
|
||||
}
|
||||
reSet := sets.NewString(receivers...)
|
||||
reIds := make([]string, len(rs))
|
||||
for i := range rs {
|
||||
reSet.Delete(rs[i].GetId())
|
||||
reSet.Delete(rs[i].GetName())
|
||||
reIds[i] = rs[i].GetId()
|
||||
}
|
||||
if reSet.Len() > 0 {
|
||||
return nil, httperrors.NewInputParameterError("receivers %q not found", strings.Join(reSet.UnsortedList(), ", "))
|
||||
}
|
||||
return reIds, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriberManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.SubscriberCreateInput) (api.SubscriberCreateInput, error) {
|
||||
log.Infof("before deal: %s", jsonutils.Marshal(input))
|
||||
var err error
|
||||
// permission check
|
||||
sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain)
|
||||
switch input.Scope {
|
||||
case sSystem:
|
||||
allow := db.IsAdminAllowCreate(userCred, sm)
|
||||
if !allow {
|
||||
return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope)
|
||||
}
|
||||
case sDomain:
|
||||
allow := db.IsDomainAllowCreate(userCred, sm)
|
||||
if !allow {
|
||||
return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope)
|
||||
}
|
||||
default:
|
||||
return input, httperrors.NewInputParameterError("unknown scope %s", input.Scope)
|
||||
}
|
||||
input.StandaloneAnonResourceCreateInput, err = sm.SStandaloneAnonResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneAnonResourceCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData")
|
||||
}
|
||||
// check topic
|
||||
t, err := TopicManager.FetchById(input.TopicID)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to fetch topic %s", input.TopicID)
|
||||
}
|
||||
// check resource scope
|
||||
if !utils.IsInStringArray(input.ResourceScope, []string{api.SUBSCRIBER_SCOPE_SYSTEM, api.SUBSCRIBER_SCOPE_DOMAIN, api.SUBSCRIBER_SCOPE_PROJECT}) {
|
||||
return input, httperrors.NewInputParameterError("unknown resource_scope %q", input.ResourceScope)
|
||||
}
|
||||
// resource Attribution Id
|
||||
var domainId string
|
||||
switch input.ResourceScope {
|
||||
case api.SUBSCRIBER_SCOPE_SYSTEM:
|
||||
input.ResourceAttributionId = ""
|
||||
input.DomainId = ""
|
||||
case api.SUBSCRIBER_SCOPE_PROJECT:
|
||||
tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, input.ResourceAttributionId)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to fetch project %s", input.ResourceAttributionId)
|
||||
}
|
||||
domainId = tenant.DomainId
|
||||
input.DomainId = domainId
|
||||
input.ResourceAttributionId = tenant.GetId()
|
||||
case api.SUBSCRIBER_SCOPE_DOMAIN:
|
||||
tenant, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, input.ResourceAttributionId)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to fetch domain %s", input.ResourceAttributionId)
|
||||
}
|
||||
domainId = tenant.DomainId
|
||||
input.DomainId = domainId
|
||||
input.ResourceAttributionId = tenant.DomainId
|
||||
}
|
||||
if input.Scope == sDomain && domainId != userCred.GetDomainId() {
|
||||
return input, httperrors.NewForbiddenError("domain %s admin can't create subscriber for domain %s", userCred.GetDomainId(), domainId)
|
||||
}
|
||||
input.TopicID = t.GetId()
|
||||
switch input.Type {
|
||||
case api.SUBSCRIBER_TYPE_RECEIVER:
|
||||
reIds, err := sm.validateReceivers(ctx, input.Receivers)
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
input.Receivers = reIds
|
||||
case api.SUBSCRIBER_TYPE_ROLE:
|
||||
if input.RoleScope == "" {
|
||||
input.RoleScope = input.ResourceScope
|
||||
}
|
||||
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, input.Role)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable find role %q", input.Role)
|
||||
}
|
||||
input.Role = roleCache.GetId()
|
||||
case api.SUBSCRIBER_TYPE_ROBOT:
|
||||
robot, err := RobotManager.FetchByIdOrName(userCred, input.Robot)
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return input, httperrors.NewInputParameterError("robot %q not found", input.Robot)
|
||||
}
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to fetch robot %q", input.Robot)
|
||||
}
|
||||
input.Robot = robot.GetId()
|
||||
default:
|
||||
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
|
||||
}
|
||||
log.Infof("after deal input: %s", jsonutils.Marshal(input))
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
err := s.SStandaloneAnonResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SVirtualResourceBase.CustomizeCreate")
|
||||
}
|
||||
var input api.SubscriberCreateInput
|
||||
_ = data.Unmarshal(&input)
|
||||
switch input.Type {
|
||||
case api.SUBSCRIBER_TYPE_RECEIVER:
|
||||
err := s.SetReceivers(ctx, input.Receivers)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to set connect receivers with subscriber %s", s.Id)
|
||||
}
|
||||
case api.SUBSCRIBER_TYPE_ROBOT:
|
||||
s.Identification = input.Robot
|
||||
case api.SUBSCRIBER_TYPE_ROLE:
|
||||
s.Identification = input.Role
|
||||
}
|
||||
s.Enabled = tristate.True
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberChangeInput) (jsonutils.JSONObject, error) {
|
||||
if s.Scope == string(rbacutils.ScopeSystem) {
|
||||
if !db.IsAdminAllowUpdate(userCred, s) {
|
||||
return nil, httperrors.NewForbiddenError("")
|
||||
}
|
||||
} else {
|
||||
if !db.IsDomainAllowUpdate(userCred, s) {
|
||||
return nil, httperrors.NewForbiddenError("")
|
||||
}
|
||||
if s.DomainId != userCred.GetDomainId() {
|
||||
return nil, httperrors.NewForbiddenError("")
|
||||
}
|
||||
}
|
||||
switch s.Type {
|
||||
case api.SUBSCRIBER_TYPE_RECEIVER:
|
||||
err := s.SetReceivers(ctx, input.Receivers)
|
||||
if err != nil {
|
||||
log.Errorf("unable to set receivers %s", input.Receivers)
|
||||
}
|
||||
case api.SUBSCRIBER_TYPE_ROBOT:
|
||||
s.Identification = input.Robot
|
||||
case api.SUBSCRIBER_TYPE_ROLE:
|
||||
s.Identification = input.Role
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriberManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.SubscriberListInput) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = sm.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneAnonResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
q, err = sm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain)
|
||||
if input.Scope == "" {
|
||||
input.Scope = sSystem
|
||||
}
|
||||
switch input.Scope {
|
||||
case sSystem:
|
||||
allow := db.IsAdminAllowList(userCred, sm)
|
||||
if !allow {
|
||||
return nil, httperrors.NewForbiddenError("")
|
||||
}
|
||||
case sDomain:
|
||||
allow := db.IsAdminAllowList(userCred, sm)
|
||||
if !allow {
|
||||
return nil, httperrors.NewForbiddenError("")
|
||||
}
|
||||
q = q.Equals("domain_id", userCred.GetDomainId())
|
||||
default:
|
||||
return nil, httperrors.NewInputParameterError("unkown scope %s", input.Scope)
|
||||
}
|
||||
if input.TopicID != "" {
|
||||
q = q.Equals("topic_id", input.TopicID)
|
||||
}
|
||||
if input.Type != "" {
|
||||
q = q.Equals("type", input.Type)
|
||||
}
|
||||
if input.ResourceScope != "" {
|
||||
q = q.Equals("resource_scope", input.ResourceScope)
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriberManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.SubscriberDetails {
|
||||
var err error
|
||||
vRows := sm.SStandaloneAnonResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]api.SubscriberDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneAnonResourceDetails = vRows[i]
|
||||
s := objs[i].(*SSubscriber)
|
||||
switch s.Type {
|
||||
case api.SUBSCRIBER_TYPE_RECEIVER:
|
||||
rows[i].Receivers, err = s.receiverIdentifications()
|
||||
if err != nil {
|
||||
log.Errorf("unable to get receiverIdentifications for subscriber %q: %v", s.Id, err)
|
||||
}
|
||||
case api.SUBSCRIBER_TYPE_ROBOT:
|
||||
rows[i].Robot, err = s.robotIdentification()
|
||||
if err != nil {
|
||||
log.Errorf("unable get robotIdentification for subscriber %q: %v", s.Id, err)
|
||||
}
|
||||
case api.SUBSCRIBER_TYPE_ROLE:
|
||||
rows[i].Role, err = s.roleIdentification()
|
||||
if err != nil {
|
||||
log.Errorf("unable to get roleIdentification for subscriber %q: %v", s.Id, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (s *SSubscriber) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
err := s.SStandaloneAnonResourceBase.CustomizeDelete(ctx, userCred, query, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Scope == string(rbacutils.ScopeSystem) {
|
||||
if !db.IsAdminAllowDelete(userCred, s) {
|
||||
return httperrors.NewForbiddenError("")
|
||||
}
|
||||
} else {
|
||||
if !db.IsDomainAllowDelete(userCred, s) {
|
||||
return httperrors.NewForbiddenError("")
|
||||
}
|
||||
if s.DomainId != userCred.GetDomainId() {
|
||||
return httperrors.NewForbiddenError("")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) receiverIdentifications() ([]api.Identification, error) {
|
||||
srSubq := SubscriberReceiverManager.Query().Equals("subscription_id", s.Id).SubQuery()
|
||||
rq := ReceiverManager.Query("id", "name")
|
||||
rq = rq.Join(srSubq, sqlchemy.Equals(srSubq.Field("receiver_id"), rq.Field("id")))
|
||||
var ret []api.Identification
|
||||
err := rq.All(&ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) robotIdentification() (api.Identification, error) {
|
||||
var ret api.Identification
|
||||
q := RobotManager.Query("id", "name").Equals("id", s.Identification)
|
||||
err := q.First(&ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) roleIdentification() (api.Identification, error) {
|
||||
var ret api.Identification
|
||||
q := db.RoleCacheManager.Query("id", "name").Equals("id", s.Identification)
|
||||
err := q.First(&ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriberManager) robot(tid, projectDomainId, projectId string) ([]string, error) {
|
||||
srs, err := srm.findSuitableOnes(tid, projectDomainId, projectId, api.SUBSCRIBER_TYPE_ROBOT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
robotIds := make([]string, len(srs))
|
||||
for i := range srs {
|
||||
robotIds[i] = srs[i].Identification
|
||||
}
|
||||
return robotIds, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriberManager) findSuitableOnes(tid, projectDomainId, projectId string, types ...string) ([]SSubscriber, error) {
|
||||
q := srm.Query().Equals("topic_id", tid).IsTrue("enabled")
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_PROJECT),
|
||||
sqlchemy.Equals(q.Field("resource_attribution_id"), projectId),
|
||||
),
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_DOMAIN),
|
||||
sqlchemy.Equals(q.Field("resource_attribution_id"), projectDomainId),
|
||||
),
|
||||
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_SYSTEM),
|
||||
))
|
||||
switch len(types) {
|
||||
case 0:
|
||||
case 1:
|
||||
q = q.Equals("type", types[0])
|
||||
default:
|
||||
q = q.In("type", types)
|
||||
}
|
||||
q.DebugQuery()
|
||||
srs := make([]SSubscriber, 0, 1)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
// TODO: Use cache to increase speed
|
||||
func (srm *SSubscriberManager) getReceiversSent(ctx context.Context, tid string, projectDomainId string, projectId string) ([]string, error) {
|
||||
srs, err := srm.findSuitableOnes(tid, projectDomainId, projectId, api.SUBSCRIBER_TYPE_RECEIVER, api.SUBSCRIBER_TYPE_ROLE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers := make([]string, 0, len(srs))
|
||||
roleMap := make(map[string][]string, 3)
|
||||
receivermap := make(map[string]*[]string, 3)
|
||||
for _, sr := range srs {
|
||||
if sr.Type == api.SUBSCRIBER_TYPE_RECEIVER {
|
||||
rIds, err := sr.getReceivers()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get receivers")
|
||||
}
|
||||
receivers = append(receivers, rIds...)
|
||||
} else if sr.Type == api.SUBSCRIBER_TYPE_ROLE {
|
||||
roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Identification)
|
||||
receivermap[sr.RoleScope] = &[]string{}
|
||||
}
|
||||
}
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
for _scope, _roles := range roleMap {
|
||||
scope, roles := _scope, _roles
|
||||
receivers := receivermap[scope]
|
||||
errgo.Go(func() error {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("roles", jsonutils.NewStringArray(roles))
|
||||
query.Set("effective", jsonutils.JSONTrue)
|
||||
switch scope {
|
||||
case api.SUBSCRIBER_SCOPE_SYSTEM:
|
||||
case api.SUBSCRIBER_SCOPE_DOMAIN:
|
||||
if len(projectDomainId) == 0 {
|
||||
return fmt.Errorf("need projectDomainId")
|
||||
}
|
||||
query.Set("project_domain_id", jsonutils.NewString(projectDomainId))
|
||||
case api.SUBSCRIBER_SCOPE_PROJECT:
|
||||
if len(projectId) == 0 {
|
||||
return fmt.Errorf("need projectId")
|
||||
}
|
||||
query.Add(jsonutils.NewString(projectId), "scope", "project", "id")
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, "", "")
|
||||
log.Debugf("query for role-assignments: %s", query.String())
|
||||
listRet, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to list RoleAssignments")
|
||||
}
|
||||
log.Debugf("return value for role-assignments: %s", jsonutils.Marshal(listRet))
|
||||
for i := range listRet.Data {
|
||||
ras := listRet.Data[i]
|
||||
user, err := ras.Get("user")
|
||||
if err == nil {
|
||||
id, err := user.GetString("id")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
|
||||
}
|
||||
*receivers = append(*receivers, id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err = errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, res := range receivermap {
|
||||
receivers = append(receivers, *res...)
|
||||
}
|
||||
// de-duplication
|
||||
return sets.NewString(receivers...).UnsortedList(), nil
|
||||
}
|
||||
|
||||
func (sr *SSubscriber) getReceivers() ([]string, error) {
|
||||
srrs, err := SubscriberReceiverManager.getBySubscriberId(sr.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rIds := make([]string, len(srrs))
|
||||
for i := range srrs {
|
||||
rIds[i] = srrs[i].ReceiverId
|
||||
}
|
||||
return rIds, nil
|
||||
}
|
||||
|
||||
func (sr *SSubscriber) SetReceivers(ctx context.Context, receiverIds []string) error {
|
||||
srrs, err := SubscriberReceiverManager.getBySubscriberId(sr.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get SRReceiver by Subscriber %s", sr.Id)
|
||||
}
|
||||
dbReceivers := make([]string, len(srrs))
|
||||
for i := range srrs {
|
||||
dbReceivers[i] = srrs[i].ReceiverId
|
||||
}
|
||||
var addReceivers, rmReceivers []string
|
||||
sort.Strings(dbReceivers)
|
||||
sort.Strings(receiverIds)
|
||||
for i, j := 0, 0; i < len(dbReceivers) || j < len(receiverIds); {
|
||||
switch {
|
||||
case i == len(dbReceivers):
|
||||
addReceivers = append(addReceivers, receiverIds[j])
|
||||
j++
|
||||
case j == len(receiverIds):
|
||||
rmReceivers = append(rmReceivers, dbReceivers[i])
|
||||
i++
|
||||
case dbReceivers[i] > receiverIds[j]:
|
||||
rmReceivers = append(rmReceivers, dbReceivers[i])
|
||||
i++
|
||||
case dbReceivers[i] < receiverIds[j]:
|
||||
addReceivers = append(addReceivers, receiverIds[j])
|
||||
j++
|
||||
case dbReceivers[i] == receiverIds[j]:
|
||||
}
|
||||
}
|
||||
// add
|
||||
for _, rId := range addReceivers {
|
||||
_, err := SubscriberReceiverManager.create(ctx, sr.Id, rId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to connect subscription receiver %q with receiver %q", sr.Id, rId)
|
||||
}
|
||||
}
|
||||
for _, rId := range rmReceivers {
|
||||
err := SubscriberReceiverManager.delete(sr.Id, rId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to disconnect subscription receiver %q with receiver %q", sr.Id, rId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, s, "set-receiver")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberSetReceiverInput) (jsonutils.JSONObject, error) {
|
||||
reIds, err := SubscriberManager.validateReceivers(ctx, input.Receivers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, s.SetReceivers(ctx, reIds)
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, s, "enable")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(s, ctx, userCred, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, s, "disable")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(s, ctx, userCred, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
var SubscriberReceiverManager *SSubscriberReceiverManager
|
||||
|
||||
func init() {
|
||||
SubscriberReceiverManager = &SSubscriberReceiverManager{
|
||||
SJointResourceBaseManager: db.NewJointResourceBaseManager(
|
||||
SSubscriberReceiver{},
|
||||
"subscriber_receiver_tbl",
|
||||
"subscriber_receiver",
|
||||
"subscriber_receivers",
|
||||
SubscriberManager,
|
||||
ReceiverManager,
|
||||
),
|
||||
}
|
||||
SubscriberReceiverManager.SetVirtualObject(SubscriberReceiverManager)
|
||||
}
|
||||
|
||||
type SSubscriberReceiverManager struct {
|
||||
db.SJointResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriberReceiver struct {
|
||||
db.SJointResourceBase
|
||||
SubscriberId string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
ReceiverId string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) getBySubscriberId(sId string) ([]SSubscriberReceiver, error) {
|
||||
q := srm.Query().Equals("subscriber_id", sId)
|
||||
srs := make([]SSubscriberReceiver, 0, 2)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) create(ctx context.Context, sId, receiverId string) (*SSubscriberReceiver, error) {
|
||||
sr := &SSubscriberReceiver{
|
||||
SubscriberId: sId,
|
||||
ReceiverId: receiverId,
|
||||
}
|
||||
return sr, srm.TableSpec().Insert(ctx, sr)
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) delete(sId, receiverId string) error {
|
||||
q := srm.Query().Equals("receiver_id", receiverId)
|
||||
var sr SSubscriberReceiver
|
||||
err := q.First(&sr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srp := &sr
|
||||
_, err = db.Update(srp, func() error {
|
||||
srp.MarkDelete()
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -1,577 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"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/bitmap"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
func parseEvent(es string) (notify.SEvent, error) {
|
||||
ess := strings.Split(es, notify.DelimiterInEvent)
|
||||
if len(ess) != 2 {
|
||||
return notify.SEvent{}, fmt.Errorf("invalid event string %q", es)
|
||||
}
|
||||
return notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1])), nil
|
||||
}
|
||||
|
||||
type SSubscriptionManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
var SubscriptionManager *SSubscriptionManager
|
||||
|
||||
func init() {
|
||||
SubscriptionManager = &SSubscriptionManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscription{},
|
||||
"subscription_tbl",
|
||||
"subscription",
|
||||
"subscriptions",
|
||||
),
|
||||
}
|
||||
SubscriptionManager.SetVirtualObject(SubscriptionManager)
|
||||
}
|
||||
|
||||
type SSubscription struct {
|
||||
db.SStandaloneResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Resources uint64 `nullable:"false"`
|
||||
Actions uint32 `nullable:"false"`
|
||||
AdvanceDays int `nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultResourceCreateDelete = "resource create or delete"
|
||||
DefaultResourceChangeConfig = "resource change config"
|
||||
DefaultResourceUpdate = "resource update"
|
||||
DefaultResourceReleaseDue1Day = "resource release due 1 day"
|
||||
DefaultResourceReleaseDue3Day = "resource release due 3 day"
|
||||
DefaultScheduledTaskExecute = "scheduled task execute"
|
||||
DefaultScalingPolicyExecute = "scaling policy execute"
|
||||
DefaultSnapshotPolicyExecute = "snapshot policy execute"
|
||||
)
|
||||
|
||||
func (sm *SSubscriptionManager) InitializeData() error {
|
||||
initSNames := sets.NewString(
|
||||
DefaultResourceCreateDelete,
|
||||
DefaultResourceChangeConfig,
|
||||
DefaultResourceUpdate,
|
||||
DefaultResourceReleaseDue1Day,
|
||||
DefaultResourceReleaseDue3Day,
|
||||
DefaultScheduledTaskExecute,
|
||||
DefaultScalingPolicyExecute,
|
||||
DefaultSnapshotPolicyExecute,
|
||||
)
|
||||
q := sm.Query()
|
||||
subscriptions := make([]SSubscription, 0, initSNames.Len())
|
||||
err := db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
for i := range subscriptions {
|
||||
ss := &subscriptions[i]
|
||||
initSNames.Delete(ss.Name)
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, name := range initSNames.UnsortedList() {
|
||||
ss := new(SSubscription)
|
||||
ss.Name = name
|
||||
switch name {
|
||||
case DefaultResourceCreateDelete:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceChangeConfig:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionChangeConfig)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceUpdate:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionUpdate)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceReleaseDue1Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 1
|
||||
case DefaultResourceReleaseDue3Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 3
|
||||
case DefaultScheduledTaskExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultScalingPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultSnapshotPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
}
|
||||
err := sm.TableSpec().Insert(ctx, ss)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to insert %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input notify.SubscriptionListInput) (*sqlchemy.SQuery, error) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []notify.SubscriptionDetails {
|
||||
sRows := sm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]notify.SubscriptionDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneResourceDetails = sRows[i]
|
||||
ss := objs[i].(*SSubscription)
|
||||
rows[i].Resources = ss.getResources()
|
||||
srs, err := ss.subscriptionReceiverDiss()
|
||||
if err != nil {
|
||||
log.Errorf("unable to get subscriptionReceivers: %v", err)
|
||||
}
|
||||
for j := range srs {
|
||||
sr := &srs[j]
|
||||
switch sr.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
rows[i].Receivers.Receivers = append(rows[i].Receivers.Receivers, notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.ReceiverName,
|
||||
})
|
||||
case ReceiverRole:
|
||||
rows[i].Receivers.ReceivingRoles = append(rows[i].Receivers.ReceivingRoles, notify.ReceivingRoleIDAndName{
|
||||
IDAndName: notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.RoleName,
|
||||
},
|
||||
Scope: sr.RoleScope,
|
||||
})
|
||||
case ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot:
|
||||
rows[i].Robot = sr.ReceiverType
|
||||
case ReceiverWeebhook:
|
||||
rows[i].Webhook = sr.ReceiverType
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverDis struct {
|
||||
SSubscriptionReceiver
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
func (s *SSubscription) subscriptionReceiverDiss() ([]SSubscriptionReceiverDis, error) {
|
||||
q := SubscriptionReceiverManager.Query().Equals("subscription_id", s.Id)
|
||||
rq := ReceiverManager.Query("id", "name").SubQuery()
|
||||
roq := db.RoleCacheManager.Query("id", "name").SubQuery()
|
||||
q = q.LeftJoin(rq, sqlchemy.Equals(q.Field("receiver"), rq.Field("id")))
|
||||
q = q.LeftJoin(roq, sqlchemy.Equals(q.Field("receiver"), roq.Field("id")))
|
||||
// It looks strange, but the order of append cannot be changed
|
||||
q.AppendField(q.QueryFields()...)
|
||||
q.AppendField(rq.Field("name", "receiver_name"))
|
||||
q.AppendField(roq.Field("name", "role_name"))
|
||||
srs := make([]SSubscriptionReceiverDis, 0)
|
||||
err := q.All(&srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch All")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return nil, httperrors.NewForbiddenError("prohibit creation")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return input, httperrors.NewForbiddenError("update prohibited")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("prohibit deletion")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-receiver")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetReceiverInput) (jsonutils.JSONObject, error) {
|
||||
// check input
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
// check receiving roles
|
||||
validScopes := []string{string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain), string(rbacutils.ScopeProject)}
|
||||
for i := range input.ReceivingRoles {
|
||||
role := input.ReceivingRoles[i]
|
||||
index := i
|
||||
errgo.Go(func() error {
|
||||
if len(role.Scope) == 0 {
|
||||
return httperrors.NewInputParameterError("empty scope for role %q", role.Role)
|
||||
}
|
||||
if !utils.IsInStringArray(role.Scope, validScopes) {
|
||||
return httperrors.NewInputParameterError("invalid scope %q for role %q, need %s, %s or %s", role.Scope, role.Role, rbacutils.ScopeSystem, rbacutils.ScopeDomain, rbacutils.ScopeProject)
|
||||
}
|
||||
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, role.Role)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable find role %q", role)
|
||||
}
|
||||
input.ReceivingRoles[index].Role = roleCache.GetId()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err := errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers, err := ReceiverManager.FetchByIdOrNames(ctx, input.Receivers...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch Receivers")
|
||||
}
|
||||
reSet := sets.NewString(input.Receivers...)
|
||||
reIds := make([]string, len(receivers))
|
||||
for i := range receivers {
|
||||
reSet.Delete(receivers[i].GetId())
|
||||
reSet.Delete(receivers[i].GetName())
|
||||
reIds[i] = receivers[i].GetId()
|
||||
}
|
||||
if reSet.Len() > 0 {
|
||||
return nil, httperrors.NewInputParameterError("receivers %q not found", strings.Join(reSet.UnsortedList(), ", "))
|
||||
}
|
||||
input.Receivers = reIds
|
||||
|
||||
// deal with subscriptionReceivers
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, ReceiverNormal, ReceiverRole)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
reSet = sets.NewString(input.Receivers...)
|
||||
reRoleSet := make(map[notify.ReceivingRole]struct{})
|
||||
for _, role := range input.ReceivingRoles {
|
||||
reRoleSet[role] = struct{}{}
|
||||
}
|
||||
for i := range srs {
|
||||
rs := &srs[i]
|
||||
switch rs.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
if !reSet.Has(rs.Receiver) {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
reSet.Delete(rs.Receiver)
|
||||
case ReceiverRole:
|
||||
role := rs.receivingRole()
|
||||
if _, ok := reRoleSet[role]; !ok {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
delete(reRoleSet, role)
|
||||
}
|
||||
}
|
||||
for _, re := range reSet.UnsortedList() {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, re, ReceiverNormal, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create receiver %s", re)
|
||||
}
|
||||
}
|
||||
for role := range reRoleSet {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, role.Role, ReceiverRole, role.Scope)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create role %s", role)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-robot")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetRobotInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Robot, ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown robot %q", input.Robot)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-webhook")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetWebhookInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Webhook, ReceiverWeebhook)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown webhook %q", input.Webhook)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) setSingleReceiver(ctx context.Context, re string, reTypes ...string) error {
|
||||
receiverRobot := reTypes
|
||||
if !utils.IsInStringArray(re, receiverRobot) {
|
||||
return errors.ErrNotFound
|
||||
}
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, receiverRobot...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
// create one
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, "", re, "")
|
||||
return err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return fmt.Errorf("multi robot receiver")
|
||||
}
|
||||
// update
|
||||
sr := &srs[0]
|
||||
_, err = db.Update(sr, func() error {
|
||||
sr.ReceiverType = re
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SSubscription) addResources(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
v := converter.resourceValue(resource)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Resources += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) addAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
v := converter.actionValue(action)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Actions += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) getResources() []string {
|
||||
vs := bitmap.Uint64ToIntArray(s.Resources)
|
||||
resources := make([]string, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
resources = append(resources, converter.resource(v))
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func (s *SSubscription) getActions() []notify.SAction {
|
||||
vs := bitmap.Uint2IntArray(s.Actions)
|
||||
actions := make([]notify.SAction, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
actions = append(actions, converter.action(v))
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) SubsciptionByEvent(eventStr string, advanceDays int) ([]SSubscription, error) {
|
||||
event, err := parseEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to parse event %q", event)
|
||||
}
|
||||
resourceV := converter.resourceValue(event.ResourceType())
|
||||
actionV := converter.actionValue(event.Action())
|
||||
q := sm.Query().Equals("advance_days", advanceDays)
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("resources"), 1<<resourceV), 0))
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("actions"), 1<<actionV), 0))
|
||||
var subscriptions []SSubscription
|
||||
err = db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
q.DebugQuery()
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
converter = &sConverter{
|
||||
resourceValueMap: make(map[string]int, 5),
|
||||
resourceList: make([]string, 0, 5),
|
||||
actionList: make([]notify.SAction, 0, 5),
|
||||
actionValueMap: make(map[notify.SAction]int, 5),
|
||||
}
|
||||
converter.registerResource(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SECGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK,
|
||||
)
|
||||
converter.registerAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
notify.ActionUpdate,
|
||||
notify.ActionRebuildRoot,
|
||||
notify.ActionResetPassword,
|
||||
notify.ActionChangeConfig,
|
||||
notify.ActionExpiredRelease,
|
||||
notify.ActionExecute,
|
||||
)
|
||||
}
|
||||
|
||||
var converter *sConverter
|
||||
|
||||
type sConverter struct {
|
||||
resourceValueMap map[string]int
|
||||
resourceList []string
|
||||
actionValueMap map[notify.SAction]int
|
||||
actionList []notify.SAction
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerResource(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
if _, ok := rc.resourceValueMap[resource]; ok {
|
||||
return
|
||||
}
|
||||
rc.resourceList = append(rc.resourceList, resource)
|
||||
rc.resourceValueMap[resource] = len(rc.resourceList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
if _, ok := rc.actionValueMap[action]; ok {
|
||||
return
|
||||
}
|
||||
rc.actionList = append(rc.actionList, action)
|
||||
rc.actionValueMap[action] = len(rc.actionList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) resourceValue(resource string) int {
|
||||
v, ok := rc.resourceValueMap[resource]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) resource(resourceValue int) string {
|
||||
if resourceValue < 0 || resourceValue >= len(rc.resourceList) {
|
||||
return ""
|
||||
}
|
||||
return rc.resourceList[resourceValue]
|
||||
}
|
||||
|
||||
func (rc *sConverter) actionValue(action notify.SAction) int {
|
||||
v, ok := rc.actionValueMap[action]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) action(actionValue int) notify.SAction {
|
||||
if actionValue < 0 || actionValue >= len(rc.actionList) {
|
||||
return notify.SAction("")
|
||||
}
|
||||
return rc.actionList[actionValue]
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var SubscriptionReceiverManager *SSubscriptionReceiverManager
|
||||
|
||||
func init() {
|
||||
SubscriptionReceiverManager = &SSubscriptionReceiverManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscriptionReceiver{},
|
||||
"subscriptionreceiver_tbl",
|
||||
"subscriptionreceiver",
|
||||
"subscriptionreceivers",
|
||||
),
|
||||
}
|
||||
SubscriptionReceiverManager.SetVirtualObject(ReceiverNotificationManager)
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriptionReceiver struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
SubscriptionID string `width:"128" charset:"ascii" nullable:"fase" index:"true"`
|
||||
// role id or receiver id or other and the value type is determined by the ReceiverType
|
||||
Receiver string `width:"128" charset:"ascii" nullable:"false"`
|
||||
ReceiverType string `width:"16" charset:"ascii" nullable:"false" index:"true"`
|
||||
RoleScope string `width:"8" charset:"ascii" nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
ReceiverRole = "role"
|
||||
ReceiverNormal = "normal"
|
||||
ReceiverFeishuRobot = notify.FEISHU_ROBOT
|
||||
ReceiverDingtalkRobot = notify.DINGTALK_ROBOT
|
||||
ReceiverWorkwxRobot = notify.WORKWX_ROBOT
|
||||
ReceiverWeebhook = "webhook"
|
||||
|
||||
ScopeSystem = "system"
|
||||
ScopeDomain = "domain"
|
||||
ScopeProject = "project"
|
||||
)
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) robot(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ReceiverFeishuRobot, ReceiverDingtalkRobot, ReceiverWorkwxRobot)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) webhook(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ssid, ReceiverWeebhook)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findSingleReceiver(ssid string, receiverTypes ...string) (string, error) {
|
||||
srs, err := srm.findReceivers(ssid, receiverTypes...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return "", errors.Error("multi receiver")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
return "", errors.ErrNotFound
|
||||
}
|
||||
return srs[0].ReceiverType, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findReceivers(ssid string, receiverTypes ...string) ([]SSubscriptionReceiver, error) {
|
||||
if len(receiverTypes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
q := srm.Query().Equals("subscription_id", ssid)
|
||||
if len(receiverTypes) == 1 {
|
||||
q = q.Equals("receiver_type", receiverTypes[0])
|
||||
} else {
|
||||
q = q.In("receiver_type", receiverTypes)
|
||||
}
|
||||
srs := make([]SSubscriptionReceiver, 0)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
// TODO: Use cache to increase speed
|
||||
func (srm *SSubscriptionReceiverManager) getReceivers(ctx context.Context, ssid string, projectDomainId string, projectId string) ([]string, error) {
|
||||
srs, err := srm.findReceivers(ssid, ReceiverRole, ReceiverNormal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers := make([]string, 0, len(srs))
|
||||
roleMap := make(map[string][]string, 3)
|
||||
receivermap := make(map[string]*[]string, 3)
|
||||
for _, sr := range srs {
|
||||
if sr.ReceiverType == ReceiverNormal {
|
||||
receivers = append(receivers, sr.Receiver)
|
||||
} else if sr.ReceiverType == ReceiverRole {
|
||||
roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Receiver)
|
||||
receivermap[sr.RoleScope] = &[]string{}
|
||||
}
|
||||
}
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
for scope, roles := range roleMap {
|
||||
receivers := receivermap[scope]
|
||||
errgo.Go(func() error {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("roles", jsonutils.NewStringArray(roles))
|
||||
query.Set("effective", jsonutils.JSONTrue)
|
||||
switch scope {
|
||||
case ScopeSystem:
|
||||
case ScopeDomain:
|
||||
if len(projectDomainId) == 0 {
|
||||
return fmt.Errorf("need projectDomainId")
|
||||
}
|
||||
query.Set("project_domain_id", jsonutils.NewString(projectDomainId))
|
||||
case ScopeProject:
|
||||
if len(projectId) == 0 {
|
||||
return fmt.Errorf("need projectId")
|
||||
}
|
||||
query.Add(jsonutils.NewString(projectId), "scope", "project", "id")
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, "", "")
|
||||
log.Debugf("query for role-assignments: %s", query.String())
|
||||
listRet, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to list RoleAssignments")
|
||||
}
|
||||
log.Debugf("return value for role-assignments: %s", jsonutils.Marshal(listRet))
|
||||
for i := range listRet.Data {
|
||||
ras := listRet.Data[i]
|
||||
user, err := ras.Get("user")
|
||||
if err == nil {
|
||||
id, err := user.GetString("id")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
|
||||
}
|
||||
*receivers = append(*receivers, id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err = errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, res := range receivermap {
|
||||
receivers = append(receivers, *res...)
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) create(ctx context.Context, ssid, receiver, receiverType, roleScope string) (*SSubscriptionReceiver, error) {
|
||||
sr := &SSubscriptionReceiver{
|
||||
SubscriptionID: ssid,
|
||||
Receiver: receiver,
|
||||
ReceiverType: receiverType,
|
||||
RoleScope: roleScope,
|
||||
}
|
||||
err := srm.TableSpec().Insert(ctx, sr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to insert")
|
||||
}
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (sr *SSubscriptionReceiver) receivingRole() notify.ReceivingRole {
|
||||
return notify.ReceivingRole{
|
||||
Role: sr.Receiver,
|
||||
Scope: sr.RoleScope,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
// 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"
|
||||
"sync"
|
||||
|
||||
"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/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"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/bitmap"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
func parseEvent(es string) (notify.SEvent, error) {
|
||||
es = strings.ToLower(es)
|
||||
ess := strings.Split(es, notify.DelimiterInEvent)
|
||||
if len(ess) != 2 && len(ess) != 3 {
|
||||
return notify.SEvent{}, fmt.Errorf("invalid event string %q", es)
|
||||
}
|
||||
event := notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1]))
|
||||
if len(ess) == 3 {
|
||||
event = event.WithResult(notify.SResult(ess[2]))
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
type STopicManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
var TopicManager *STopicManager
|
||||
|
||||
func init() {
|
||||
TopicManager = &STopicManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
STopic{},
|
||||
"topic_tbl",
|
||||
"topic",
|
||||
"topics",
|
||||
),
|
||||
}
|
||||
TopicManager.SetVirtualObject(TopicManager)
|
||||
}
|
||||
|
||||
type STopic struct {
|
||||
db.SStandaloneResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Resources uint64 `nullable:"false"`
|
||||
Actions uint32 `nullable:"false"`
|
||||
Results uint8 `nullable:"false"`
|
||||
AdvanceDays int `nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultResourceCreateDelete = "resource create or delete"
|
||||
DefaultResourceChangeConfig = "resource change config"
|
||||
DefaultResourceUpdate = "resource update"
|
||||
DefaultResourceReleaseDue1Day = "resource release due 1 day"
|
||||
DefaultResourceReleaseDue3Day = "resource release due 3 day"
|
||||
DefaultScheduledTaskExecute = "scheduled task execute"
|
||||
DefaultScalingPolicyExecute = "scaling policy execute"
|
||||
DefaultSnapshotPolicyExecute = "snapshot policy execute"
|
||||
DefaultResourceOperationFailed = "resource operation failed"
|
||||
)
|
||||
|
||||
func (sm *STopicManager) InitializeData() error {
|
||||
initSNames := sets.NewString(
|
||||
DefaultResourceCreateDelete,
|
||||
DefaultResourceChangeConfig,
|
||||
DefaultResourceUpdate,
|
||||
DefaultResourceReleaseDue1Day,
|
||||
DefaultResourceReleaseDue3Day,
|
||||
DefaultScheduledTaskExecute,
|
||||
DefaultScalingPolicyExecute,
|
||||
DefaultSnapshotPolicyExecute,
|
||||
DefaultResourceOperationFailed,
|
||||
)
|
||||
q := sm.Query()
|
||||
topics := make([]STopic, 0, initSNames.Len())
|
||||
err := db.FetchModelObjects(sm, q, &topics)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
for i := range topics {
|
||||
t := &topics[i]
|
||||
initSNames.Delete(t.Name)
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, name := range initSNames.UnsortedList() {
|
||||
t := new(STopic)
|
||||
t.Name = name
|
||||
t.Enabled = tristate.True
|
||||
switch name {
|
||||
case DefaultResourceCreateDelete:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_SCALINGGROUP,
|
||||
notify.TOPIC_RESOURCE_IMAGE,
|
||||
notify.TOPIC_RESOURCE_DISK,
|
||||
notify.TOPIC_RESOURCE_SNAPSHOT,
|
||||
notify.TOPIC_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.TOPIC_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.TOPIC_RESOURCE_NETWORK,
|
||||
notify.TOPIC_RESOURCE_EIP,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCER,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCERACL,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.TOPIC_RESOURCE_BUCKET,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
notify.TOPIC_RESOURCE_BAREMETAL,
|
||||
)
|
||||
t.addAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
case DefaultResourceChangeConfig:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_DISK,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
t.addAction(notify.ActionChangeConfig)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
case DefaultResourceUpdate:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_DISK,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
t.addAction(notify.ActionUpdate)
|
||||
t.addAction(notify.ActionRebuildRoot)
|
||||
t.addAction(notify.ActionResetPassword)
|
||||
t.addAction(notify.ActionChangeIpaddr)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
case DefaultResourceReleaseDue1Day:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_DISK,
|
||||
notify.TOPIC_RESOURCE_EIP,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCER,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
t.addAction(notify.ActionExpiredRelease)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
t.AdvanceDays = 1
|
||||
case DefaultResourceReleaseDue3Day:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_DISK,
|
||||
notify.TOPIC_RESOURCE_EIP,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCER,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
t.addAction(notify.ActionExpiredRelease)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
t.AdvanceDays = 3
|
||||
case DefaultScheduledTaskExecute:
|
||||
t.addResources(notify.TOPIC_RESOURCE_SCHEDULEDTASK)
|
||||
t.addAction(notify.ActionExecute)
|
||||
t.Type = notify.TOPIC_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultScalingPolicyExecute:
|
||||
t.addResources(notify.TOPIC_RESOURCE_SCALINGPOLICY)
|
||||
t.addAction(notify.ActionExecute)
|
||||
t.Type = notify.TOPIC_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultSnapshotPolicyExecute:
|
||||
t.addResources(notify.TOPIC_RESOURCE_SNAPSHOTPOLICY)
|
||||
t.addAction(notify.ActionExecute)
|
||||
t.Type = notify.TOPIC_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultResourceOperationFailed:
|
||||
t.addResources(
|
||||
notify.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_EIP,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCER,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
t.addAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionSyncStatus,
|
||||
notify.ActionRebuildRoot,
|
||||
notify.ActionChangeConfig,
|
||||
notify.ActionCreateBackupServer,
|
||||
notify.ActionDelBackupServer,
|
||||
notify.ActionMigrate,
|
||||
)
|
||||
t.Type = notify.TOPIC_TYPE_RESOURCE
|
||||
}
|
||||
err := sm.TableSpec().Insert(ctx, t)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to insert %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *STopicManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input notify.TopicListInput) (*sqlchemy.SQuery, error) {
|
||||
return sm.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneResourceListInput)
|
||||
}
|
||||
|
||||
func (sm *STopicManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []notify.TopicDetails {
|
||||
sRows := sm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]notify.TopicDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneResourceDetails = sRows[i]
|
||||
ss := objs[i].(*STopic)
|
||||
rows[i].Resources = ss.getResources()
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
type SSubscriberDis struct {
|
||||
SSubscriber
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
func (s *STopic) subscriptionReceiverDiss() ([]SSubscriberDis, error) {
|
||||
q := SubscriberManager.Query().Equals("subscription_id", s.Id)
|
||||
rq := ReceiverManager.Query("id", "name").SubQuery()
|
||||
roq := db.RoleCacheManager.Query("id", "name").SubQuery()
|
||||
q = q.LeftJoin(rq, sqlchemy.Equals(q.Field("receiver"), rq.Field("id")))
|
||||
q = q.LeftJoin(roq, sqlchemy.Equals(q.Field("receiver"), roq.Field("id")))
|
||||
// It looks strange, but the order of append cannot be changed
|
||||
q.AppendField(q.QueryFields()...)
|
||||
q.AppendField(rq.Field("name", "receiver_name"))
|
||||
q.AppendField(roq.Field("name", "role_name"))
|
||||
srs := make([]SSubscriberDis, 0)
|
||||
err := q.All(&srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch All")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (sm *STopicManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return nil, httperrors.NewForbiddenError("prohibit creation")
|
||||
}
|
||||
|
||||
func (ss *STopic) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return input, httperrors.NewForbiddenError("update prohibited")
|
||||
}
|
||||
|
||||
func (ss *STopic) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("prohibit deletion")
|
||||
}
|
||||
|
||||
func (s *STopic) addResources(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
v := converter.resourceValue(resource)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Resources += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *STopic) addAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
v := converter.actionValue(action)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Actions += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *STopic) getResources() []string {
|
||||
vs := bitmap.Uint64ToIntArray(s.Resources)
|
||||
resources := make([]string, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
resources = append(resources, converter.resource(v))
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func (s *STopic) getActions() []notify.SAction {
|
||||
vs := bitmap.Uint2IntArray(s.Actions)
|
||||
actions := make([]notify.SAction, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
actions = append(actions, converter.action(v))
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func (sm *STopicManager) TopicsByEvent(eventStr string, advanceDays int) ([]STopic, error) {
|
||||
event, err := parseEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to parse event %q", event)
|
||||
}
|
||||
resourceV := converter.resourceValue(event.ResourceType())
|
||||
if resourceV < 0 {
|
||||
log.Warningf("unknown resource type: %s", event.ResourceType())
|
||||
return nil, nil
|
||||
}
|
||||
actionV := converter.actionValue(event.Action())
|
||||
if actionV < 0 {
|
||||
log.Warningf("unknown action type: %s", event.Action())
|
||||
return nil, nil
|
||||
}
|
||||
q := sm.Query().Equals("advance_days", advanceDays)
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("resources"), 1<<resourceV), 0))
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("actions"), 1<<actionV), 0))
|
||||
var topics []STopic
|
||||
err = db.FetchModelObjects(sm, q, &topics)
|
||||
if err != nil {
|
||||
q.DebugQuery()
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return topics, nil
|
||||
}
|
||||
|
||||
func (t *STopic) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.PerformEnableInput) bool {
|
||||
return db.IsAdminAllowPerform(userCred, t, "enable")
|
||||
}
|
||||
|
||||
func (t *STopic) PreCheckPerformAction(
|
||||
ctx context.Context, userCred mcclient.TokenCredential,
|
||||
action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
|
||||
) error {
|
||||
if err := t.SStandaloneResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
|
||||
return err
|
||||
}
|
||||
if action == "enable" || action == "disable" {
|
||||
if !db.IsAdminAllowPerform(userCred, t, action) {
|
||||
return httperrors.NewForbiddenError("only allow admin to perform enable operations")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *STopic) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.PerformEnableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(t, ctx, userCred, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *STopic) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.PerformDisableInput) bool {
|
||||
return db.IsAdminAllowPerform(userCred, t, "disable")
|
||||
}
|
||||
|
||||
func (t *STopic) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.PerformDisableInput) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(t, ctx, userCred, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
converter = &sConverter{
|
||||
resource2Value: &sync.Map{},
|
||||
value2Resource: &sync.Map{},
|
||||
action2Value: &sync.Map{},
|
||||
value2Action: &sync.Map{},
|
||||
}
|
||||
converter.registerResource(
|
||||
map[string]int{
|
||||
notify.TOPIC_RESOURCE_SERVER: 0,
|
||||
notify.TOPIC_RESOURCE_SCALINGGROUP: 1,
|
||||
notify.TOPIC_RESOURCE_SCALINGPOLICY: 2,
|
||||
notify.TOPIC_RESOURCE_IMAGE: 3,
|
||||
notify.TOPIC_RESOURCE_DISK: 4,
|
||||
notify.TOPIC_RESOURCE_SNAPSHOT: 5,
|
||||
notify.TOPIC_RESOURCE_INSTANCESNAPSHOT: 6,
|
||||
notify.TOPIC_RESOURCE_SNAPSHOTPOLICY: 7,
|
||||
notify.TOPIC_RESOURCE_NETWORK: 8,
|
||||
notify.TOPIC_RESOURCE_EIP: 9,
|
||||
notify.TOPIC_RESOURCE_SECGROUP: 10,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCER: 11,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCERACL: 12,
|
||||
notify.TOPIC_RESOURCE_LOADBALANCERCERTIFICATE: 13,
|
||||
notify.TOPIC_RESOURCE_BUCKET: 14,
|
||||
notify.TOPIC_RESOURCE_DBINSTANCE: 15,
|
||||
notify.TOPIC_RESOURCE_ELASTICCACHE: 16,
|
||||
notify.TOPIC_RESOURCE_SCHEDULEDTASK: 17,
|
||||
notify.TOPIC_RESOURCE_BAREMETAL: 18,
|
||||
},
|
||||
)
|
||||
converter.registerAction(
|
||||
map[notify.SAction]int{
|
||||
notify.ActionCreate: 0,
|
||||
notify.ActionDelete: 1,
|
||||
notify.ActionPendingDelete: 2,
|
||||
notify.ActionUpdate: 3,
|
||||
notify.ActionRebuildRoot: 4,
|
||||
notify.ActionResetPassword: 5,
|
||||
notify.ActionChangeConfig: 6,
|
||||
notify.ActionExpiredRelease: 7,
|
||||
notify.ActionExecute: 8,
|
||||
notify.ActionChangeIpaddr: 9,
|
||||
notify.ActionSyncStatus: 10,
|
||||
notify.ActionCleanData: 11,
|
||||
notify.ActionMigrate: 12,
|
||||
notify.ActionCreateBackupServer: 13,
|
||||
notify.ActionDelBackupServer: 14,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
var converter *sConverter
|
||||
|
||||
type sConverter struct {
|
||||
resource2Value *sync.Map
|
||||
value2Resource *sync.Map
|
||||
action2Value *sync.Map
|
||||
value2Action *sync.Map
|
||||
}
|
||||
|
||||
type sResourceValue struct {
|
||||
resource string
|
||||
value int
|
||||
}
|
||||
|
||||
type sActionValue struct {
|
||||
action string
|
||||
value int
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerResource(resourceValues map[string]int) {
|
||||
for resource, value := range resourceValues {
|
||||
if v, ok := rc.resource2Value.Load(resource); ok && v.(int) != value {
|
||||
log.Fatalf("resource '%s' has been mapped to value '%d', and it is not allowed to map to another value '%d'", resource, v, value)
|
||||
}
|
||||
if r, ok := rc.value2Resource.Load(value); ok && r.(string) != resource {
|
||||
log.Fatalf("value '%d' has been mapped to resource '%s', and it is not allowed to map to another resource '%s'", value, r, resource)
|
||||
}
|
||||
rc.resource2Value.Store(resource, value)
|
||||
rc.value2Resource.Store(value, resource)
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerAction(actionValues map[notify.SAction]int) {
|
||||
for action, value := range actionValues {
|
||||
if v, ok := rc.action2Value.Load(action); ok && v.(int) != value {
|
||||
log.Fatalf("action '%s' has been mapped to value '%d', and it is not allowed to map to another value '%d'", action, v, value)
|
||||
}
|
||||
if a, ok := rc.value2Action.Load(value); ok && a.(notify.SAction) != action {
|
||||
log.Fatalf("value '%d' has been mapped to action '%s', and it is not allowed to map to another action '%s'", value, a, action)
|
||||
}
|
||||
rc.action2Value.Store(action, value)
|
||||
rc.value2Action.Store(value, action)
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) resourceValue(resource string) int {
|
||||
v, ok := rc.resource2Value.Load(resource)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v.(int)
|
||||
}
|
||||
|
||||
func (rc *sConverter) resource(resourceValue int) string {
|
||||
r, ok := rc.value2Resource.Load(resourceValue)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return r.(string)
|
||||
}
|
||||
|
||||
func (rc *sConverter) actionValue(action notify.SAction) int {
|
||||
v, ok := rc.action2Value.Load(action)
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v.(int)
|
||||
}
|
||||
|
||||
func (rc *sConverter) action(actionValue int) notify.SAction {
|
||||
a, ok := rc.value2Action.Load(actionValue)
|
||||
if !ok {
|
||||
return notify.SAction("")
|
||||
}
|
||||
return a.(notify.SAction)
|
||||
}
|
||||
@@ -41,13 +41,31 @@ func (c *SendNotificationClient) Send(ctx context.Context, in *SendParams, opts
|
||||
return c.sendAgentClient.Send(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error) {
|
||||
func (c *SendNotificationClient) AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.AddConfig(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.DeleteConfig(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.UpdateConfig(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
|
||||
func (c *SendNotificationClient) CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.CompleteConfig(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.ValidateConfig(ctx, in, opts...)
|
||||
@@ -59,6 +77,12 @@ func (c *SendNotificationClient) UseridByMobile(ctx context.Context, in *UseridB
|
||||
return c.sendAgentClient.UseridByMobile(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
return c.sendAgentClient.Ready(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (c *SendNotificationClient) BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -26,15 +26,15 @@ var _ = math.Inf
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type SendParams struct {
|
||||
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
|
||||
Topic string `protobuf:"bytes,2,opt,name=Topic,proto3" json:"Topic,omitempty"`
|
||||
Title string `protobuf:"bytes,3,opt,name=Title,proto3" json:"Title,omitempty"`
|
||||
Message string `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
|
||||
Priority string `protobuf:"bytes,5,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
RemoteTemplate string `protobuf:"bytes,6,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
Receiver *SReceiver `protobuf:"bytes,1,opt,name=Receiver,proto3" json:"Receiver,omitempty"`
|
||||
Topic string `protobuf:"bytes,2,opt,name=Topic,proto3" json:"Topic,omitempty"`
|
||||
Title string `protobuf:"bytes,3,opt,name=Title,proto3" json:"Title,omitempty"`
|
||||
Message string `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
|
||||
Priority string `protobuf:"bytes,5,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
RemoteTemplate string `protobuf:"bytes,6,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SendParams) Reset() { *m = SendParams{} }
|
||||
@@ -62,11 +62,11 @@ func (m *SendParams) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_SendParams proto.InternalMessageInfo
|
||||
|
||||
func (m *SendParams) GetContact() string {
|
||||
func (m *SendParams) GetReceiver() *SReceiver {
|
||||
if m != nil {
|
||||
return m.Contact
|
||||
return m.Receiver
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SendParams) GetTopic() string {
|
||||
@@ -104,47 +104,220 @@ func (m *SendParams) GetRemoteTemplate() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type UpdateConfigParams struct {
|
||||
type ValidateConfigInput struct {
|
||||
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *UpdateConfigParams) Reset() { *m = UpdateConfigParams{} }
|
||||
func (m *UpdateConfigParams) String() string { return proto.CompactTextString(m) }
|
||||
func (*UpdateConfigParams) ProtoMessage() {}
|
||||
func (*UpdateConfigParams) Descriptor() ([]byte, []int) {
|
||||
func (m *ValidateConfigInput) Reset() { *m = ValidateConfigInput{} }
|
||||
func (m *ValidateConfigInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*ValidateConfigInput) ProtoMessage() {}
|
||||
func (*ValidateConfigInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{1}
|
||||
}
|
||||
|
||||
func (m *UpdateConfigParams) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_UpdateConfigParams.Unmarshal(m, b)
|
||||
func (m *ValidateConfigInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ValidateConfigInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *UpdateConfigParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_UpdateConfigParams.Marshal(b, m, deterministic)
|
||||
func (m *ValidateConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ValidateConfigInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *UpdateConfigParams) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_UpdateConfigParams.Merge(m, src)
|
||||
func (m *ValidateConfigInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ValidateConfigInput.Merge(m, src)
|
||||
}
|
||||
func (m *UpdateConfigParams) XXX_Size() int {
|
||||
return xxx_messageInfo_UpdateConfigParams.Size(m)
|
||||
func (m *ValidateConfigInput) XXX_Size() int {
|
||||
return xxx_messageInfo_ValidateConfigInput.Size(m)
|
||||
}
|
||||
func (m *UpdateConfigParams) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_UpdateConfigParams.DiscardUnknown(m)
|
||||
func (m *ValidateConfigInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ValidateConfigInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_UpdateConfigParams proto.InternalMessageInfo
|
||||
var xxx_messageInfo_ValidateConfigInput proto.InternalMessageInfo
|
||||
|
||||
func (m *UpdateConfigParams) GetConfigs() map[string]string {
|
||||
func (m *ValidateConfigInput) GetConfigs() map[string]string {
|
||||
if m != nil {
|
||||
return m.Configs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AddConfigInput struct {
|
||||
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *AddConfigInput) Reset() { *m = AddConfigInput{} }
|
||||
func (m *AddConfigInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*AddConfigInput) ProtoMessage() {}
|
||||
func (*AddConfigInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{2}
|
||||
}
|
||||
|
||||
func (m *AddConfigInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_AddConfigInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *AddConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_AddConfigInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *AddConfigInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_AddConfigInput.Merge(m, src)
|
||||
}
|
||||
func (m *AddConfigInput) XXX_Size() int {
|
||||
return xxx_messageInfo_AddConfigInput.Size(m)
|
||||
}
|
||||
func (m *AddConfigInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_AddConfigInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_AddConfigInput proto.InternalMessageInfo
|
||||
|
||||
func (m *AddConfigInput) GetConfigs() map[string]string {
|
||||
if m != nil {
|
||||
return m.Configs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AddConfigInput) GetDomainId() string {
|
||||
if m != nil {
|
||||
return m.DomainId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UpdateConfigInput struct {
|
||||
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *UpdateConfigInput) Reset() { *m = UpdateConfigInput{} }
|
||||
func (m *UpdateConfigInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*UpdateConfigInput) ProtoMessage() {}
|
||||
func (*UpdateConfigInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{3}
|
||||
}
|
||||
|
||||
func (m *UpdateConfigInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_UpdateConfigInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *UpdateConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_UpdateConfigInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *UpdateConfigInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_UpdateConfigInput.Merge(m, src)
|
||||
}
|
||||
func (m *UpdateConfigInput) XXX_Size() int {
|
||||
return xxx_messageInfo_UpdateConfigInput.Size(m)
|
||||
}
|
||||
func (m *UpdateConfigInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_UpdateConfigInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_UpdateConfigInput proto.InternalMessageInfo
|
||||
|
||||
func (m *UpdateConfigInput) GetConfigs() map[string]string {
|
||||
if m != nil {
|
||||
return m.Configs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UpdateConfigInput) GetDomainId() string {
|
||||
if m != nil {
|
||||
return m.DomainId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type DeleteConfigInput struct {
|
||||
DomainId string `protobuf:"bytes,1,opt,name=domainId,proto3" json:"domainId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DeleteConfigInput) Reset() { *m = DeleteConfigInput{} }
|
||||
func (m *DeleteConfigInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeleteConfigInput) ProtoMessage() {}
|
||||
func (*DeleteConfigInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{4}
|
||||
}
|
||||
|
||||
func (m *DeleteConfigInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DeleteConfigInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DeleteConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DeleteConfigInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DeleteConfigInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DeleteConfigInput.Merge(m, src)
|
||||
}
|
||||
func (m *DeleteConfigInput) XXX_Size() int {
|
||||
return xxx_messageInfo_DeleteConfigInput.Size(m)
|
||||
}
|
||||
func (m *DeleteConfigInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DeleteConfigInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DeleteConfigInput proto.InternalMessageInfo
|
||||
|
||||
func (m *DeleteConfigInput) GetDomainId() string {
|
||||
if m != nil {
|
||||
return m.DomainId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CompleteConfigInput struct {
|
||||
ConfigInput []*AddConfigInput `protobuf:"bytes,1,rep,name=ConfigInput,proto3" json:"ConfigInput,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CompleteConfigInput) Reset() { *m = CompleteConfigInput{} }
|
||||
func (m *CompleteConfigInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*CompleteConfigInput) ProtoMessage() {}
|
||||
func (*CompleteConfigInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{5}
|
||||
}
|
||||
|
||||
func (m *CompleteConfigInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CompleteConfigInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CompleteConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CompleteConfigInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CompleteConfigInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CompleteConfigInput.Merge(m, src)
|
||||
}
|
||||
func (m *CompleteConfigInput) XXX_Size() int {
|
||||
return xxx_messageInfo_CompleteConfigInput.Size(m)
|
||||
}
|
||||
func (m *CompleteConfigInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CompleteConfigInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CompleteConfigInput proto.InternalMessageInfo
|
||||
|
||||
func (m *CompleteConfigInput) GetConfigInput() []*AddConfigInput {
|
||||
if m != nil {
|
||||
return m.ConfigInput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UseridByMobileParams struct {
|
||||
Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"`
|
||||
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -154,7 +327,7 @@ func (m *UseridByMobileParams) Reset() { *m = UseridByMobileParams{} }
|
||||
func (m *UseridByMobileParams) String() string { return proto.CompactTextString(m) }
|
||||
func (*UseridByMobileParams) ProtoMessage() {}
|
||||
func (*UseridByMobileParams) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{2}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{6}
|
||||
}
|
||||
|
||||
func (m *UseridByMobileParams) XXX_Unmarshal(b []byte) error {
|
||||
@@ -182,6 +355,13 @@ func (m *UseridByMobileParams) GetMobile() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *UseridByMobileParams) GetDomainId() string {
|
||||
if m != nil {
|
||||
return m.DomainId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
@@ -192,7 +372,7 @@ func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{3}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{7}
|
||||
}
|
||||
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
@@ -224,7 +404,7 @@ func (m *UseridByMobileReply) Reset() { *m = UseridByMobileReply{} }
|
||||
func (m *UseridByMobileReply) String() string { return proto.CompactTextString(m) }
|
||||
func (*UseridByMobileReply) ProtoMessage() {}
|
||||
func (*UseridByMobileReply) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{4}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{8}
|
||||
}
|
||||
|
||||
func (m *UseridByMobileReply) XXX_Unmarshal(b []byte) error {
|
||||
@@ -264,7 +444,7 @@ func (m *ValidateConfigReply) Reset() { *m = ValidateConfigReply{} }
|
||||
func (m *ValidateConfigReply) String() string { return proto.CompactTextString(m) }
|
||||
func (*ValidateConfigReply) ProtoMessage() {}
|
||||
func (*ValidateConfigReply) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{5}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{9}
|
||||
}
|
||||
|
||||
func (m *ValidateConfigReply) XXX_Unmarshal(b []byte) error {
|
||||
@@ -299,22 +479,69 @@ func (m *ValidateConfigReply) GetMsg() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type BatchSendParams struct {
|
||||
Contacts []string `protobuf:"bytes,1,rep,name=Contacts,proto3" json:"Contacts,omitempty"`
|
||||
Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
|
||||
Message string `protobuf:"bytes,3,opt,name=Message,proto3" json:"Message,omitempty"`
|
||||
Priority string `protobuf:"bytes,4,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
RemoteTemplate string `protobuf:"bytes,5,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
|
||||
type SReceiver struct {
|
||||
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
|
||||
DomainId string `protobuf:"bytes,2,opt,name=DomainId,proto3" json:"DomainId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SReceiver) Reset() { *m = SReceiver{} }
|
||||
func (m *SReceiver) String() string { return proto.CompactTextString(m) }
|
||||
func (*SReceiver) ProtoMessage() {}
|
||||
func (*SReceiver) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{10}
|
||||
}
|
||||
|
||||
func (m *SReceiver) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SReceiver.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SReceiver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SReceiver.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SReceiver) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SReceiver.Merge(m, src)
|
||||
}
|
||||
func (m *SReceiver) XXX_Size() int {
|
||||
return xxx_messageInfo_SReceiver.Size(m)
|
||||
}
|
||||
func (m *SReceiver) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SReceiver.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SReceiver proto.InternalMessageInfo
|
||||
|
||||
func (m *SReceiver) GetContact() string {
|
||||
if m != nil {
|
||||
return m.Contact
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SReceiver) GetDomainId() string {
|
||||
if m != nil {
|
||||
return m.DomainId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type BatchSendParams struct {
|
||||
Receivers []*SReceiver `protobuf:"bytes,1,rep,name=Receivers,proto3" json:"Receivers,omitempty"`
|
||||
Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
|
||||
Message string `protobuf:"bytes,3,opt,name=Message,proto3" json:"Message,omitempty"`
|
||||
Priority string `protobuf:"bytes,4,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
RemoteTemplate string `protobuf:"bytes,5,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *BatchSendParams) Reset() { *m = BatchSendParams{} }
|
||||
func (m *BatchSendParams) String() string { return proto.CompactTextString(m) }
|
||||
func (*BatchSendParams) ProtoMessage() {}
|
||||
func (*BatchSendParams) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{6}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{11}
|
||||
}
|
||||
|
||||
func (m *BatchSendParams) XXX_Unmarshal(b []byte) error {
|
||||
@@ -335,9 +562,9 @@ func (m *BatchSendParams) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_BatchSendParams proto.InternalMessageInfo
|
||||
|
||||
func (m *BatchSendParams) GetContacts() []string {
|
||||
func (m *BatchSendParams) GetReceivers() []*SReceiver {
|
||||
if m != nil {
|
||||
return m.Contacts
|
||||
return m.Receivers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -371,18 +598,18 @@ func (m *BatchSendParams) GetRemoteTemplate() string {
|
||||
}
|
||||
|
||||
type FailedRecord struct {
|
||||
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
|
||||
Reason string `protobuf:"bytes,2,opt,name=Reason,proto3" json:"Reason,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
Receiver *SReceiver `protobuf:"bytes,1,opt,name=Receiver,proto3" json:"Receiver,omitempty"`
|
||||
Reason string `protobuf:"bytes,2,opt,name=Reason,proto3" json:"Reason,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *FailedRecord) Reset() { *m = FailedRecord{} }
|
||||
func (m *FailedRecord) String() string { return proto.CompactTextString(m) }
|
||||
func (*FailedRecord) ProtoMessage() {}
|
||||
func (*FailedRecord) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{7}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{12}
|
||||
}
|
||||
|
||||
func (m *FailedRecord) XXX_Unmarshal(b []byte) error {
|
||||
@@ -403,11 +630,11 @@ func (m *FailedRecord) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_FailedRecord proto.InternalMessageInfo
|
||||
|
||||
func (m *FailedRecord) GetContact() string {
|
||||
func (m *FailedRecord) GetReceiver() *SReceiver {
|
||||
if m != nil {
|
||||
return m.Contact
|
||||
return m.Receiver
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *FailedRecord) GetReason() string {
|
||||
@@ -428,7 +655,7 @@ func (m *BatchSendReply) Reset() { *m = BatchSendReply{} }
|
||||
func (m *BatchSendReply) String() string { return proto.CompactTextString(m) }
|
||||
func (*BatchSendReply) ProtoMessage() {}
|
||||
func (*BatchSendReply) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{8}
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{13}
|
||||
}
|
||||
|
||||
func (m *BatchSendReply) XXX_Unmarshal(b []byte) error {
|
||||
@@ -456,55 +683,156 @@ func (m *BatchSendReply) GetFailedRecords() []*FailedRecord {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReadyInput struct {
|
||||
DomainIds []string `protobuf:"bytes,1,rep,name=DomainIds,proto3" json:"DomainIds,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ReadyInput) Reset() { *m = ReadyInput{} }
|
||||
func (m *ReadyInput) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadyInput) ProtoMessage() {}
|
||||
func (*ReadyInput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{14}
|
||||
}
|
||||
|
||||
func (m *ReadyInput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadyInput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ReadyInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ReadyInput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ReadyInput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ReadyInput.Merge(m, src)
|
||||
}
|
||||
func (m *ReadyInput) XXX_Size() int {
|
||||
return xxx_messageInfo_ReadyInput.Size(m)
|
||||
}
|
||||
func (m *ReadyInput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ReadyInput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ReadyInput proto.InternalMessageInfo
|
||||
|
||||
func (m *ReadyInput) GetDomainIds() []string {
|
||||
if m != nil {
|
||||
return m.DomainIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReadyOutput struct {
|
||||
Ok bool `protobuf:"varint,1,opt,name=Ok,proto3" json:"Ok,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ReadyOutput) Reset() { *m = ReadyOutput{} }
|
||||
func (m *ReadyOutput) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadyOutput) ProtoMessage() {}
|
||||
func (*ReadyOutput) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_63fdd68f7eb311f9, []int{15}
|
||||
}
|
||||
|
||||
func (m *ReadyOutput) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadyOutput.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ReadyOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ReadyOutput.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ReadyOutput) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ReadyOutput.Merge(m, src)
|
||||
}
|
||||
func (m *ReadyOutput) XXX_Size() int {
|
||||
return xxx_messageInfo_ReadyOutput.Size(m)
|
||||
}
|
||||
func (m *ReadyOutput) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ReadyOutput.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ReadyOutput proto.InternalMessageInfo
|
||||
|
||||
func (m *ReadyOutput) GetOk() bool {
|
||||
if m != nil {
|
||||
return m.Ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*SendParams)(nil), "apis.SendParams")
|
||||
proto.RegisterType((*UpdateConfigParams)(nil), "apis.UpdateConfigParams")
|
||||
proto.RegisterMapType((map[string]string)(nil), "apis.UpdateConfigParams.ConfigsEntry")
|
||||
proto.RegisterType((*ValidateConfigInput)(nil), "apis.ValidateConfigInput")
|
||||
proto.RegisterMapType((map[string]string)(nil), "apis.ValidateConfigInput.ConfigsEntry")
|
||||
proto.RegisterType((*AddConfigInput)(nil), "apis.AddConfigInput")
|
||||
proto.RegisterMapType((map[string]string)(nil), "apis.AddConfigInput.ConfigsEntry")
|
||||
proto.RegisterType((*UpdateConfigInput)(nil), "apis.UpdateConfigInput")
|
||||
proto.RegisterMapType((map[string]string)(nil), "apis.UpdateConfigInput.ConfigsEntry")
|
||||
proto.RegisterType((*DeleteConfigInput)(nil), "apis.DeleteConfigInput")
|
||||
proto.RegisterType((*CompleteConfigInput)(nil), "apis.CompleteConfigInput")
|
||||
proto.RegisterType((*UseridByMobileParams)(nil), "apis.UseridByMobileParams")
|
||||
proto.RegisterType((*Empty)(nil), "apis.Empty")
|
||||
proto.RegisterType((*UseridByMobileReply)(nil), "apis.UseridByMobileReply")
|
||||
proto.RegisterType((*ValidateConfigReply)(nil), "apis.ValidateConfigReply")
|
||||
proto.RegisterType((*SReceiver)(nil), "apis.SReceiver")
|
||||
proto.RegisterType((*BatchSendParams)(nil), "apis.BatchSendParams")
|
||||
proto.RegisterType((*FailedRecord)(nil), "apis.FailedRecord")
|
||||
proto.RegisterType((*BatchSendReply)(nil), "apis.BatchSendReply")
|
||||
proto.RegisterType((*ReadyInput)(nil), "apis.ReadyInput")
|
||||
proto.RegisterType((*ReadyOutput)(nil), "apis.ReadyOutput")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("send_server.proto", fileDescriptor_63fdd68f7eb311f9) }
|
||||
|
||||
var fileDescriptor_63fdd68f7eb311f9 = []byte{
|
||||
// 497 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xdd, 0x6a, 0xd4, 0x40,
|
||||
0x14, 0xc7, 0xc9, 0x7e, 0xef, 0xe9, 0xba, 0xd6, 0xe9, 0x5a, 0xc6, 0x5c, 0x95, 0x40, 0xa5, 0x37,
|
||||
0xe6, 0xa2, 0x22, 0x2c, 0xbd, 0xd1, 0xb6, 0xac, 0x82, 0x50, 0x28, 0xb1, 0xf5, 0x56, 0xa6, 0xc9,
|
||||
0x71, 0x1d, 0x4c, 0x32, 0x21, 0x33, 0x2d, 0xe4, 0x31, 0x7c, 0x04, 0xdf, 0x40, 0xf0, 0x05, 0x65,
|
||||
0x3e, 0xb2, 0x9b, 0xac, 0xbb, 0xed, 0x5d, 0x7e, 0xe7, 0x8b, 0x39, 0xe7, 0xff, 0x27, 0xf0, 0x42,
|
||||
0x62, 0x9e, 0x7c, 0x93, 0x58, 0x3e, 0x60, 0x19, 0x16, 0xa5, 0x50, 0x82, 0xf4, 0x58, 0xc1, 0x65,
|
||||
0xf0, 0xc7, 0x03, 0xf8, 0x82, 0x79, 0x72, 0xcd, 0x4a, 0x96, 0x49, 0x42, 0x61, 0x78, 0x29, 0x72,
|
||||
0xc5, 0x62, 0x45, 0xbd, 0x23, 0xef, 0x64, 0x1c, 0xd5, 0x48, 0x66, 0xd0, 0xbf, 0x11, 0x05, 0x8f,
|
||||
0x69, 0xc7, 0xc4, 0x2d, 0x98, 0x28, 0x57, 0x29, 0xd2, 0xae, 0x8b, 0x6a, 0xd0, 0x53, 0xae, 0x50,
|
||||
0x4a, 0xb6, 0x44, 0xda, 0xb3, 0x53, 0x1c, 0x12, 0x1f, 0x46, 0xd7, 0x25, 0x17, 0x25, 0x57, 0x15,
|
||||
0xed, 0x9b, 0xd4, 0x8a, 0xc9, 0x6b, 0x98, 0x46, 0x98, 0x09, 0x85, 0x37, 0x98, 0x15, 0x29, 0x53,
|
||||
0x48, 0x07, 0xa6, 0x62, 0x23, 0x1a, 0xfc, 0xf2, 0x80, 0xdc, 0x16, 0x09, 0x53, 0x78, 0x29, 0xf2,
|
||||
0xef, 0x7c, 0xe9, 0x9e, 0xfe, 0x1e, 0x86, 0xb1, 0x61, 0x49, 0xbd, 0xa3, 0xee, 0xc9, 0xde, 0xe9,
|
||||
0x71, 0xa8, 0x37, 0x0c, 0xff, 0x2f, 0x0d, 0x2d, 0xc8, 0x45, 0xae, 0xca, 0x2a, 0xaa, 0xbb, 0xfc,
|
||||
0x33, 0x98, 0x34, 0x13, 0x64, 0x1f, 0xba, 0x3f, 0xb1, 0x72, 0x77, 0xd0, 0x9f, 0x7a, 0xdb, 0x07,
|
||||
0x96, 0xde, 0x63, 0x7d, 0x03, 0x03, 0x67, 0x9d, 0xb9, 0x17, 0x84, 0x30, 0xbb, 0x95, 0x58, 0xf2,
|
||||
0xe4, 0xa2, 0xba, 0x12, 0x77, 0x3c, 0x45, 0xf7, 0xa8, 0x43, 0x18, 0x64, 0x86, 0xdd, 0x18, 0x47,
|
||||
0xc1, 0x10, 0xfa, 0x8b, 0xac, 0x50, 0x55, 0xf0, 0x06, 0x0e, 0xda, 0x8d, 0x11, 0x16, 0x69, 0xa5,
|
||||
0xfb, 0xee, 0x4d, 0xb8, 0xee, 0xb3, 0x14, 0x9c, 0xc3, 0xc1, 0x57, 0x96, 0xf2, 0xf5, 0x46, 0xb6,
|
||||
0x9c, 0xc2, 0x90, 0x4b, 0x93, 0x30, 0xf5, 0xa3, 0xa8, 0x46, 0xbd, 0x44, 0x26, 0x97, 0xee, 0xc1,
|
||||
0xfa, 0x33, 0xf8, 0xed, 0xc1, 0xf3, 0x0b, 0xa6, 0xe2, 0x1f, 0x0d, 0xd9, 0x7d, 0x18, 0x39, 0x9d,
|
||||
0xed, 0xf1, 0xc6, 0xd1, 0x8a, 0xd7, 0x12, 0x77, 0x76, 0x48, 0xdc, 0xdd, 0x2d, 0x71, 0xef, 0x49,
|
||||
0x89, 0xfb, 0x5b, 0x25, 0xfe, 0x00, 0x93, 0x8f, 0x8c, 0xa7, 0x98, 0x44, 0x18, 0x8b, 0x32, 0x79,
|
||||
0xc4, 0x96, 0x87, 0x30, 0x88, 0x90, 0x49, 0x91, 0xbb, 0xe7, 0x39, 0x0a, 0x3e, 0xc3, 0x74, 0xb5,
|
||||
0xa4, 0xbd, 0xd1, 0x1c, 0x9e, 0x35, 0x67, 0xd6, 0x2e, 0x21, 0xd6, 0x25, 0xcd, 0x54, 0xd4, 0x2e,
|
||||
0x3c, 0xfd, 0xdb, 0x81, 0xb1, 0x9e, 0x73, 0xbe, 0xc4, 0x5c, 0x91, 0x63, 0xe8, 0x69, 0x20, 0xfb,
|
||||
0xb6, 0x71, 0x7d, 0x45, 0x7f, 0xcf, 0x46, 0x8c, 0xb0, 0xe4, 0x1d, 0x4c, 0x9a, 0xce, 0x23, 0x74,
|
||||
0x97, 0x1b, 0xdb, 0x6d, 0x0b, 0x98, 0xb6, 0x05, 0x7e, 0xa4, 0xf1, 0x95, 0xcd, 0x6c, 0x33, 0xc4,
|
||||
0x27, 0x98, 0xb6, 0x6d, 0x45, 0x7c, 0x37, 0x66, 0x8b, 0x4b, 0xeb, 0x41, 0xdb, 0x8c, 0x38, 0x87,
|
||||
0xf1, 0xea, 0x8e, 0xe4, 0xa5, 0xad, 0xdb, 0x70, 0x8f, 0x3f, 0xdb, 0x08, 0x9b, 0xce, 0xbb, 0x81,
|
||||
0xf9, 0xcd, 0xbc, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x71, 0x6f, 0x12, 0x61, 0x7b, 0x04, 0x00,
|
||||
0x00,
|
||||
// 722 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x5d, 0x6b, 0xd4, 0x4c,
|
||||
0x14, 0x26, 0xfb, 0xd9, 0x9c, 0xed, 0xbb, 0xed, 0x4e, 0xf7, 0xed, 0x9b, 0x77, 0x51, 0xa8, 0x41,
|
||||
0x4b, 0x51, 0xbb, 0x42, 0x15, 0x59, 0x2a, 0x88, 0xdb, 0x2f, 0x69, 0xa1, 0xb4, 0x4c, 0x5b, 0x6f,
|
||||
0x65, 0xba, 0x39, 0xae, 0xa1, 0xf9, 0x22, 0x99, 0x2d, 0xe4, 0xd2, 0xdf, 0xe0, 0x6f, 0xf0, 0xca,
|
||||
0x5b, 0xff, 0x81, 0x7f, 0x4c, 0x32, 0x93, 0xc9, 0x26, 0xd9, 0x54, 0x11, 0xc1, 0xbb, 0x3c, 0x67,
|
||||
0x9e, 0x73, 0xce, 0x93, 0x93, 0x39, 0x4f, 0xa0, 0x17, 0xa1, 0x67, 0xbd, 0x8f, 0x30, 0xbc, 0xc5,
|
||||
0x70, 0x18, 0x84, 0x3e, 0xf7, 0x49, 0x83, 0x05, 0x76, 0x64, 0x7e, 0xd7, 0x00, 0x2e, 0xd0, 0xb3,
|
||||
0xce, 0x59, 0xc8, 0xdc, 0x88, 0x3c, 0x81, 0x25, 0x8a, 0x13, 0xb4, 0x6f, 0x31, 0x34, 0xb4, 0x0d,
|
||||
0x6d, 0xab, 0xb3, 0xb3, 0x32, 0x4c, 0x78, 0xc3, 0x0b, 0x15, 0xa6, 0x19, 0x81, 0xf4, 0xa1, 0x79,
|
||||
0xe9, 0x07, 0xf6, 0xc4, 0xa8, 0x6d, 0x68, 0x5b, 0x3a, 0x95, 0x40, 0x44, 0x6d, 0xee, 0xa0, 0x51,
|
||||
0x4f, 0xa3, 0x09, 0x20, 0x06, 0xb4, 0x4f, 0x31, 0x8a, 0xd8, 0x14, 0x8d, 0x86, 0x88, 0x2b, 0x48,
|
||||
0x06, 0xb0, 0x74, 0x1e, 0xda, 0x7e, 0x68, 0xf3, 0xd8, 0x68, 0x8a, 0xa3, 0x0c, 0x93, 0x4d, 0xe8,
|
||||
0x52, 0x74, 0x7d, 0x8e, 0x97, 0xe8, 0x06, 0x0e, 0xe3, 0x68, 0xb4, 0x04, 0xa3, 0x14, 0x35, 0x3f,
|
||||
0x6b, 0xb0, 0xf6, 0x8e, 0x39, 0xb6, 0xc5, 0x38, 0xee, 0xfb, 0xde, 0x07, 0x7b, 0x7a, 0xec, 0x05,
|
||||
0x33, 0x4e, 0xde, 0x40, 0x7b, 0x22, 0x60, 0x64, 0x68, 0x1b, 0xf5, 0xad, 0xce, 0xce, 0xa6, 0x7c,
|
||||
0x9b, 0x0a, 0xee, 0x50, 0x3e, 0x47, 0x87, 0x1e, 0x0f, 0x63, 0xaa, 0xd2, 0x06, 0xbb, 0xb0, 0x9c,
|
||||
0x3f, 0x20, 0xab, 0x50, 0xbf, 0xc1, 0x58, 0xcc, 0x46, 0xa7, 0xc9, 0x63, 0xf2, 0xbe, 0xb7, 0xcc,
|
||||
0x99, 0xa1, 0x9a, 0x82, 0x00, 0xbb, 0xb5, 0x91, 0x66, 0x7e, 0xd1, 0xa0, 0x3b, 0xb6, 0xac, 0xbc,
|
||||
0xa0, 0x57, 0x65, 0x41, 0x0f, 0xa4, 0xa0, 0x22, 0xad, 0x5a, 0x4b, 0x32, 0x29, 0xcb, 0x77, 0x99,
|
||||
0xed, 0x1d, 0x5b, 0x69, 0xb3, 0x0c, 0xff, 0x91, 0xce, 0xaf, 0x1a, 0xf4, 0xae, 0x82, 0xf2, 0xec,
|
||||
0x5e, 0x97, 0xa5, 0x3e, 0x94, 0x52, 0x17, 0x98, 0x7f, 0x59, 0xed, 0x33, 0xe8, 0x1d, 0xa0, 0x83,
|
||||
0x45, 0xb1, 0xf9, 0x66, 0x5a, 0xb1, 0x99, 0x79, 0x0a, 0x6b, 0xfb, 0xbe, 0x1b, 0x94, 0x53, 0x5e,
|
||||
0x42, 0x27, 0x07, 0xd3, 0x77, 0xec, 0x57, 0x7d, 0x0e, 0x9a, 0x27, 0x9a, 0x27, 0xd0, 0xbf, 0x8a,
|
||||
0x30, 0xb4, 0xad, 0xbd, 0xf8, 0xd4, 0xbf, 0xb6, 0x1d, 0x4c, 0x57, 0x67, 0x1d, 0x5a, 0xae, 0xc0,
|
||||
0xa9, 0x80, 0x14, 0xfd, 0x6c, 0x0e, 0x66, 0x1b, 0x9a, 0x87, 0x6e, 0xc0, 0x63, 0x73, 0x1b, 0xd6,
|
||||
0x8a, 0x45, 0x29, 0x06, 0x4e, 0x9c, 0xd4, 0x9c, 0x89, 0xb0, 0xaa, 0x29, 0x91, 0x39, 0x2e, 0x5f,
|
||||
0x77, 0x49, 0x37, 0xa0, 0x6d, 0x47, 0xe2, 0x40, 0xf0, 0x97, 0xa8, 0x82, 0xc9, 0x80, 0xdd, 0x68,
|
||||
0x9a, 0xf6, 0x4f, 0x1e, 0xcd, 0x31, 0xe8, 0xd9, 0x4e, 0x27, 0x89, 0xfb, 0xbe, 0xc7, 0xd9, 0x84,
|
||||
0xa7, 0x8d, 0x14, 0x4c, 0xd4, 0x1f, 0x94, 0xd4, 0x2b, 0x6c, 0x7e, 0xd3, 0x60, 0x65, 0x8f, 0xf1,
|
||||
0xc9, 0xc7, 0x9c, 0x81, 0x6c, 0x83, 0xae, 0xaa, 0xaa, 0x7b, 0xb3, 0xe0, 0x20, 0x73, 0xc6, 0xdc,
|
||||
0x2c, 0x6a, 0x77, 0x98, 0x45, 0xfd, 0x6e, 0xb3, 0x68, 0xfc, 0xd2, 0x2c, 0x9a, 0x95, 0x66, 0x71,
|
||||
0x01, 0xcb, 0x47, 0xcc, 0x76, 0xd0, 0xa2, 0x38, 0xf1, 0x43, 0xeb, 0xf7, 0x3c, 0x6f, 0x1d, 0x5a,
|
||||
0x14, 0x59, 0xe4, 0x7b, 0xa9, 0xe2, 0x14, 0x99, 0x27, 0xd0, 0xcd, 0x46, 0x21, 0x3f, 0xc6, 0x08,
|
||||
0xfe, 0xc9, 0xb7, 0x51, 0xd3, 0x20, 0xb2, 0x76, 0xfe, 0x88, 0x16, 0x89, 0xe6, 0x63, 0x00, 0x8a,
|
||||
0xcc, 0x8a, 0xe5, 0x3d, 0xbd, 0x07, 0xba, 0x9a, 0xb8, 0xac, 0xa1, 0xd3, 0x79, 0xc0, 0xbc, 0x0f,
|
||||
0x1d, 0xc1, 0x3d, 0x9b, 0xf1, 0x84, 0xdc, 0x85, 0xda, 0xd9, 0x4d, 0xfa, 0xf1, 0x6b, 0x67, 0x37,
|
||||
0x3b, 0x9f, 0x1a, 0xa0, 0x27, 0x92, 0xc6, 0x53, 0xf4, 0x38, 0x79, 0x0a, 0x4d, 0x41, 0x26, 0xab,
|
||||
0x52, 0xc4, 0xbc, 0xcb, 0xa0, 0x97, 0x8b, 0xa4, 0xb5, 0x1e, 0x41, 0x23, 0x49, 0x55, 0xe4, 0xf9,
|
||||
0x47, 0x1e, 0x74, 0x64, 0x44, 0x5c, 0x5d, 0x32, 0x04, 0x3d, 0x5b, 0x17, 0x52, 0xb9, 0x3f, 0x45,
|
||||
0xfe, 0x08, 0xba, 0xc5, 0x75, 0x24, 0xff, 0xcb, 0xe3, 0x8a, 0x25, 0x2d, 0x66, 0xbe, 0x80, 0xe5,
|
||||
0xbc, 0xf9, 0x90, 0xff, 0xee, 0x30, 0xa4, 0x85, 0xac, 0xbc, 0x5f, 0xa8, 0xac, 0x05, 0x0f, 0x29,
|
||||
0x66, 0x1d, 0x41, 0xb7, 0xb8, 0x61, 0x4a, 0x65, 0xc5, 0xaf, 0x63, 0x50, 0x79, 0x24, 0x6f, 0xc1,
|
||||
0x5b, 0xe8, 0x16, 0x17, 0x9b, 0x0c, 0x52, 0xd5, 0x15, 0x1e, 0xa2, 0x0a, 0x55, 0x59, 0xc1, 0x08,
|
||||
0xf4, 0xec, 0x82, 0x91, 0x7f, 0x25, 0xaf, 0xb4, 0x7c, 0x83, 0x7e, 0x29, 0x2c, 0x32, 0xaf, 0x5b,
|
||||
0xe2, 0x7f, 0xff, 0xfc, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x44, 0xad, 0xbd, 0x04, 0x08,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -519,9 +847,13 @@ const _ = grpc.SupportPackageIsVersion4
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type SendAgentClient interface {
|
||||
Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error)
|
||||
Send(ctx context.Context, in *SendParams, opts ...grpc.CallOption) (*Empty, error)
|
||||
UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error)
|
||||
ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error)
|
||||
AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error)
|
||||
CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error)
|
||||
UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error)
|
||||
DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error)
|
||||
ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error)
|
||||
UseridByMobile(ctx context.Context, in *UseridByMobileParams, opts ...grpc.CallOption) (*UseridByMobileReply, error)
|
||||
BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error)
|
||||
}
|
||||
@@ -534,6 +866,15 @@ func NewSendAgentClient(cc *grpc.ClientConn) SendAgentClient {
|
||||
return &sendAgentClient{cc}
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error) {
|
||||
out := new(ReadyOutput)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/Ready", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) Send(ctx context.Context, in *SendParams, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/Send", in, out, opts...)
|
||||
@@ -543,7 +884,25 @@ func (c *sendAgentClient) Send(ctx context.Context, in *SendParams, opts ...grpc
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error) {
|
||||
func (c *sendAgentClient) AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/AddConfig", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/CompleteConfig", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/UpdateConfig", in, out, opts...)
|
||||
if err != nil {
|
||||
@@ -552,7 +911,16 @@ func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigPara
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
|
||||
func (c *sendAgentClient) DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/DeleteConfig", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sendAgentClient) ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
|
||||
out := new(ValidateConfigReply)
|
||||
err := c.cc.Invoke(ctx, "/apis.SendAgent/ValidateConfig", in, out, opts...)
|
||||
if err != nil {
|
||||
@@ -581,9 +949,13 @@ func (c *sendAgentClient) BatchSend(ctx context.Context, in *BatchSendParams, op
|
||||
|
||||
// SendAgentServer is the server API for SendAgent service.
|
||||
type SendAgentServer interface {
|
||||
Ready(context.Context, *ReadyInput) (*ReadyOutput, error)
|
||||
Send(context.Context, *SendParams) (*Empty, error)
|
||||
UpdateConfig(context.Context, *UpdateConfigParams) (*Empty, error)
|
||||
ValidateConfig(context.Context, *UpdateConfigParams) (*ValidateConfigReply, error)
|
||||
AddConfig(context.Context, *AddConfigInput) (*Empty, error)
|
||||
CompleteConfig(context.Context, *CompleteConfigInput) (*Empty, error)
|
||||
UpdateConfig(context.Context, *UpdateConfigInput) (*Empty, error)
|
||||
DeleteConfig(context.Context, *DeleteConfigInput) (*Empty, error)
|
||||
ValidateConfig(context.Context, *ValidateConfigInput) (*ValidateConfigReply, error)
|
||||
UseridByMobile(context.Context, *UseridByMobileParams) (*UseridByMobileReply, error)
|
||||
BatchSend(context.Context, *BatchSendParams) (*BatchSendReply, error)
|
||||
}
|
||||
@@ -592,13 +964,25 @@ type SendAgentServer interface {
|
||||
type UnimplementedSendAgentServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedSendAgentServer) Ready(ctx context.Context, req *ReadyInput) (*ReadyOutput, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ready not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) Send(ctx context.Context, req *SendParams) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) UpdateConfig(ctx context.Context, req *UpdateConfigParams) (*Empty, error) {
|
||||
func (*UnimplementedSendAgentServer) AddConfig(ctx context.Context, req *AddConfigInput) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddConfig not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) CompleteConfig(ctx context.Context, req *CompleteConfigInput) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CompleteConfig not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) UpdateConfig(ctx context.Context, req *UpdateConfigInput) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateConfig not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) ValidateConfig(ctx context.Context, req *UpdateConfigParams) (*ValidateConfigReply, error) {
|
||||
func (*UnimplementedSendAgentServer) DeleteConfig(ctx context.Context, req *DeleteConfigInput) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteConfig not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) ValidateConfig(ctx context.Context, req *ValidateConfigInput) (*ValidateConfigReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ValidateConfig not implemented")
|
||||
}
|
||||
func (*UnimplementedSendAgentServer) UseridByMobile(ctx context.Context, req *UseridByMobileParams) (*UseridByMobileReply, error) {
|
||||
@@ -612,6 +996,24 @@ func RegisterSendAgentServer(s *grpc.Server, srv SendAgentServer) {
|
||||
s.RegisterService(&_SendAgent_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _SendAgent_Ready_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ReadyInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SendAgentServer).Ready(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apis.SendAgent/Ready",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).Ready(ctx, req.(*ReadyInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendParams)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -630,8 +1032,44 @@ func _SendAgent_Send_Handler(srv interface{}, ctx context.Context, dec func(inte
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_AddConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddConfigInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SendAgentServer).AddConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apis.SendAgent/AddConfig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).AddConfig(ctx, req.(*AddConfigInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_CompleteConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CompleteConfigInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SendAgentServer).CompleteConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apis.SendAgent/CompleteConfig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).CompleteConfig(ctx, req.(*CompleteConfigInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateConfigParams)
|
||||
in := new(UpdateConfigInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -643,13 +1081,31 @@ func _SendAgent_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec f
|
||||
FullMethod: "/apis.SendAgent/UpdateConfig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).UpdateConfig(ctx, req.(*UpdateConfigParams))
|
||||
return srv.(SendAgentServer).UpdateConfig(ctx, req.(*UpdateConfigInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_DeleteConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteConfigInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SendAgentServer).DeleteConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apis.SendAgent/DeleteConfig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).DeleteConfig(ctx, req.(*DeleteConfigInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SendAgent_ValidateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateConfigParams)
|
||||
in := new(ValidateConfigInput)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -661,7 +1117,7 @@ func _SendAgent_ValidateConfig_Handler(srv interface{}, ctx context.Context, dec
|
||||
FullMethod: "/apis.SendAgent/ValidateConfig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SendAgentServer).ValidateConfig(ctx, req.(*UpdateConfigParams))
|
||||
return srv.(SendAgentServer).ValidateConfig(ctx, req.(*ValidateConfigInput))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -706,14 +1162,30 @@ var _SendAgent_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "apis.SendAgent",
|
||||
HandlerType: (*SendAgentServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ready",
|
||||
Handler: _SendAgent_Ready_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Send",
|
||||
Handler: _SendAgent_Send_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddConfig",
|
||||
Handler: _SendAgent_AddConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CompleteConfig",
|
||||
Handler: _SendAgent_CompleteConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateConfig",
|
||||
Handler: _SendAgent_UpdateConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteConfig",
|
||||
Handler: _SendAgent_DeleteConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ValidateConfig",
|
||||
Handler: _SendAgent_ValidateConfig_Handler,
|
||||
|
||||
@@ -17,7 +17,7 @@ syntax = "proto3";
|
||||
package apis;
|
||||
|
||||
message SendParams {
|
||||
string Contact = 1;
|
||||
SReceiver Receiver = 1;
|
||||
string Topic = 2;
|
||||
string Title = 3;
|
||||
string Message = 4;
|
||||
@@ -25,12 +25,31 @@ message SendParams {
|
||||
string RemoteTemplate = 6;
|
||||
}
|
||||
|
||||
message UpdateConfigParams {
|
||||
message ValidateConfigInput {
|
||||
map<string, string> configs = 1;
|
||||
}
|
||||
|
||||
message AddConfigInput {
|
||||
map<string, string> configs = 1;
|
||||
string domainId = 2;
|
||||
}
|
||||
|
||||
message UpdateConfigInput {
|
||||
map<string, string> configs = 1;
|
||||
string domainId = 2;
|
||||
}
|
||||
|
||||
message DeleteConfigInput {
|
||||
string domainId = 1;
|
||||
}
|
||||
|
||||
message CompleteConfigInput {
|
||||
repeated AddConfigInput ConfigInput = 1;
|
||||
}
|
||||
|
||||
message UseridByMobileParams {
|
||||
string mobile = 1;
|
||||
string domainId = 2;
|
||||
}
|
||||
|
||||
message Empty {
|
||||
@@ -45,8 +64,13 @@ message ValidateConfigReply {
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
message SReceiver {
|
||||
string Contact = 1;
|
||||
string DomainId = 2;
|
||||
}
|
||||
|
||||
message BatchSendParams {
|
||||
repeated string Contacts = 1;
|
||||
repeated SReceiver Receivers = 1;
|
||||
string Title = 2;
|
||||
string Message = 3;
|
||||
string Priority = 4;
|
||||
@@ -54,7 +78,7 @@ message BatchSendParams {
|
||||
}
|
||||
|
||||
message FailedRecord {
|
||||
string Contact = 1;
|
||||
SReceiver Receiver = 1;
|
||||
string Reason = 2;
|
||||
}
|
||||
|
||||
@@ -62,10 +86,23 @@ message BatchSendReply {
|
||||
repeated FailedRecord FailedRecords = 1;
|
||||
}
|
||||
|
||||
message ReadyInput {
|
||||
repeated string DomainIds = 1;
|
||||
}
|
||||
|
||||
message ReadyOutput {
|
||||
bool Ok = 1;
|
||||
}
|
||||
|
||||
service SendAgent {
|
||||
rpc Send(SendParams) returns (Empty);
|
||||
rpc UpdateConfig(UpdateConfigParams) returns (Empty);
|
||||
rpc ValidateConfig(UpdateConfigParams) returns (ValidateConfigReply);
|
||||
rpc UseridByMobile(UseridByMobileParams) returns (UseridByMobileReply);
|
||||
rpc Ready(ReadyInput) returns (ReadyOutput);
|
||||
rpc Send (SendParams) returns (Empty);
|
||||
rpc AddConfig (AddConfigInput) returns (Empty);
|
||||
rpc CompleteConfig (CompleteConfigInput) returns (Empty);
|
||||
rpc UpdateConfig (UpdateConfigInput) returns (Empty);
|
||||
rpc DeleteConfig (DeleteConfigInput) returns (Empty);
|
||||
rpc ValidateConfig (ValidateConfigInput) returns (ValidateConfigReply);
|
||||
rpc UseridByMobile (UseridByMobileParams) returns (UseridByMobileReply);
|
||||
rpc BatchSend (BatchSendParams) returns (BatchSendReply);
|
||||
}
|
||||
|
||||
|
||||
+200
-68
@@ -34,7 +34,6 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
notifyv2 "yunion.io/x/onecloud/pkg/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/models"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
)
|
||||
@@ -135,11 +134,57 @@ func (self *SRpcService) BatchSend(ctx context.Context, contactType string, args
|
||||
if len(args.RemoteTemplate) == 0 && contactType == api.MOBILE {
|
||||
return nil, fmt.Errorf("empty remote template for mobile type notification")
|
||||
}
|
||||
domainIds := make([]string, len(args.Receivers))
|
||||
for i := range domainIds {
|
||||
domainIds[i] = args.Receivers[i].DomainId
|
||||
}
|
||||
checks, err := self.configStore.BatchCheckConfig(contactType, domainIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "BatchCheckConfig")
|
||||
}
|
||||
hasSystem, err := self.configStore.HasSystemConfig(contactType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "HasSystemConfig")
|
||||
}
|
||||
ret := make([]*apis.FailedRecord, 0)
|
||||
receiverIndex := 0
|
||||
for i := range checks {
|
||||
if checks[i] {
|
||||
receiverIndex++
|
||||
continue
|
||||
}
|
||||
if hasSystem {
|
||||
args.Receivers[receiverIndex].DomainId = ""
|
||||
receiverIndex++
|
||||
continue
|
||||
}
|
||||
ret = append(ret, &apis.FailedRecord{
|
||||
Receiver: args.Receivers[i],
|
||||
Reason: fmt.Sprintf("no %q config for in domain %q and system", contactType, domainIds[i]),
|
||||
})
|
||||
args.Receivers = append(args.Receivers[:receiverIndex], args.Receivers[receiverIndex+1:]...)
|
||||
}
|
||||
f := func(service *apis.SendNotificationClient) (interface{}, error) {
|
||||
// check ready
|
||||
domainIds := make([]string, len(args.Receivers))
|
||||
for i := range domainIds {
|
||||
domainIds[i] = args.Receivers[i].DomainId
|
||||
}
|
||||
output, err := service.Ready(ctx, &apis.ReadyInput{DomainIds: domainIds})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !output.Ok {
|
||||
// if NOINIT, try to restart server and send again
|
||||
service, err = self.restartService(ctx, contactType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "restart service %s failed", contactType)
|
||||
}
|
||||
}
|
||||
return service.BatchSend(ctx, &args)
|
||||
}
|
||||
|
||||
ret, err := self.execute(ctx, f, contactType)
|
||||
i, err := self.execute(ctx, f, contactType)
|
||||
if err != nil {
|
||||
s, ok := status.FromError(err)
|
||||
if !ok {
|
||||
@@ -147,29 +192,54 @@ func (self *SRpcService) BatchSend(ctx context.Context, contactType string, args
|
||||
}
|
||||
return nil, errors.Error(s.Message())
|
||||
}
|
||||
reply := ret.(*apis.BatchSendReply)
|
||||
return reply.FailedRecords, nil
|
||||
reply := i.(*apis.BatchSendReply)
|
||||
return append(ret, reply.FailedRecords...), nil
|
||||
}
|
||||
|
||||
// RestartService can restart remote rpc server and pass config info.
|
||||
// This function should be call immediately after init notify server firstly
|
||||
// This function should be call immediately after accept the request about changing config.
|
||||
func (self *SRpcService) RestartService(ctx context.Context, config notifyv2.SConfig, serviceName string) {
|
||||
_, err := self.restartWithConfig(ctx, serviceName, config)
|
||||
if err != nil {
|
||||
log.Debugf("restart service failed: %s", err)
|
||||
// UpdateConfig can update config for rpc service with domainId
|
||||
func (self *SRpcService) UpdateConfig(ctx context.Context, service string, config notifyv2.SConfig) error {
|
||||
var (
|
||||
sendService *apis.SendNotificationClient
|
||||
err error
|
||||
)
|
||||
|
||||
sendService, ok := self.SendServices.Get(service)
|
||||
if !ok {
|
||||
return fmt.Errorf("no such service %s", service)
|
||||
}
|
||||
|
||||
args := apis.UpdateConfigInput{
|
||||
Configs: config.Config,
|
||||
DomainId: config.DomainId,
|
||||
}
|
||||
_, err = sendService.UpdateConfig(ctx, &args)
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
if st.Code() != codes.NotFound {
|
||||
return errors.Error(st.Message())
|
||||
}
|
||||
_, err = sendService.AddConfig(ctx, &apis.AddConfigInput{
|
||||
Configs: config.Config,
|
||||
DomainId: config.DomainId,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "try to add config but failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error) {
|
||||
func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceName string, domainId string) (string, error) {
|
||||
|
||||
iMobile := api.ParseInternationalMobile(mobile)
|
||||
// compatible
|
||||
if iMobile.AreaCode == "86" {
|
||||
mobile = iMobile.Mobile
|
||||
}
|
||||
args := apis.UseridByMobileParams{}
|
||||
args.Mobile = mobile
|
||||
args := apis.UseridByMobileParams{
|
||||
Mobile: mobile,
|
||||
DomainId: domainId,
|
||||
}
|
||||
|
||||
f := func(service *apis.SendNotificationClient) (interface{}, error) {
|
||||
return service.UseridByMobile(ctx, &args)
|
||||
@@ -187,7 +257,7 @@ func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceNam
|
||||
if s.Code() == codes.NotFound {
|
||||
return "", errors.Wrap(notifyv2.ErrNoSuchMobile, s.Message())
|
||||
}
|
||||
if s.Code() == codes.PermissionDenied {
|
||||
if s.Code() == codes.FailedPrecondition {
|
||||
return "", errors.Wrap(notifyv2.ErrIncompleteConfig, s.Message())
|
||||
}
|
||||
return "", err
|
||||
@@ -246,40 +316,47 @@ func (self *SRpcService) execute(ctx context.Context, f func(client *apis.SendNo
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// restartSrevice fetch config from IServiceConfigStore and Call rpc.UpdateConfig
|
||||
func (self *SRpcService) restartService(ctx context.Context, serviceName string) (*apis.SendNotificationClient, error) {
|
||||
var ErrGetConfig = errors.Error("Get Config Failed")
|
||||
|
||||
config, err := self.configStore.GetConfig(serviceName)
|
||||
func (self *SRpcService) completeConfig(ctx context.Context, serviceName string, sendService *apis.SendNotificationClient) error {
|
||||
// get config
|
||||
configs, err := self.configStore.GetConfigs(serviceName)
|
||||
if err != nil {
|
||||
log.Debugf("getConfig of serveice %s from database error", serviceName)
|
||||
return nil, models.ErrGetConfig
|
||||
log.Errorf("getConfig of serveice %s from database error", serviceName)
|
||||
return ErrGetConfig
|
||||
}
|
||||
return self.restartWithConfig(ctx, serviceName, config)
|
||||
|
||||
// update config for service
|
||||
configInput := make([]*apis.AddConfigInput, len(configs))
|
||||
for i := range configInput {
|
||||
configInput[i] = &apis.AddConfigInput{
|
||||
Configs: configs[i].Config,
|
||||
DomainId: configs[i].DomainId,
|
||||
}
|
||||
}
|
||||
_, err = sendService.CompleteConfig(ctx, &apis.CompleteConfigInput{
|
||||
ConfigInput: configInput,
|
||||
})
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
if st.Code() == codes.FailedPrecondition {
|
||||
// no such rpc serve
|
||||
err = fmt.Errorf(st.Message())
|
||||
}
|
||||
if st.Code() == codes.Unavailable {
|
||||
err = fmt.Errorf("service is unavailable for now: %s", st.Message())
|
||||
}
|
||||
return errors.Wrap(err, "UpdateConfig")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) restartWithConfig(ctx context.Context, serviceName string,
|
||||
config map[string]string) (*apis.SendNotificationClient, error) {
|
||||
|
||||
var (
|
||||
sendService *apis.SendNotificationClient
|
||||
err error
|
||||
)
|
||||
|
||||
sendService, ok := self.SendServices.Get(serviceName)
|
||||
|
||||
func (self *SRpcService) restartService(ctx context.Context, service string) (*apis.SendNotificationClient, error) {
|
||||
sendService, ok := self.SendServices.Get(service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no such service, please start new service")
|
||||
}
|
||||
|
||||
args := apis.UpdateConfigParams{}
|
||||
args.Configs = config
|
||||
_, err = sendService.UpdateConfig(ctx, &args)
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
return nil, errors.Error(st.Message())
|
||||
}
|
||||
|
||||
return sendService, nil
|
||||
return sendService, self.completeConfig(ctx, service, sendService)
|
||||
}
|
||||
|
||||
// startNewService try to start a new rpc service named serviceName
|
||||
@@ -308,30 +385,7 @@ func (self *SRpcService) startNewService(ctx context.Context, serviceName string
|
||||
return sendService, nil
|
||||
}
|
||||
|
||||
// get config
|
||||
config, err := self.configStore.GetConfig(serviceName)
|
||||
if err != nil {
|
||||
log.Errorf("getConfig of serveice %s from database error", serviceName)
|
||||
return nil, models.ErrGetConfig
|
||||
}
|
||||
|
||||
// update config for service
|
||||
args := apis.UpdateConfigParams{}
|
||||
args.Configs = config
|
||||
_, err = sendService.UpdateConfig(ctx, &args)
|
||||
if err != nil {
|
||||
st := status.Convert(err)
|
||||
if st.Code() == codes.FailedPrecondition {
|
||||
// no such rpc serve
|
||||
err = fmt.Errorf(st.Message())
|
||||
}
|
||||
if st.Code() == codes.Unavailable {
|
||||
err = fmt.Errorf("service is unavailable for now: %s", st.Message())
|
||||
}
|
||||
return nil, errors.Wrap(err, "UpdateConfig")
|
||||
}
|
||||
|
||||
return sendService, nil
|
||||
return sendService, self.completeConfig(ctx, serviceName, sendService)
|
||||
}
|
||||
|
||||
// closeService will remove service record from self.SendServices and try to remove sock file
|
||||
@@ -374,8 +428,48 @@ func (self *SRpcService) updateService(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool,
|
||||
message string, err error) {
|
||||
func (self *SRpcService) AddConfig(ctx context.Context, service string, config notifyv2.SConfig) error {
|
||||
var (
|
||||
sendService *apis.SendNotificationClient
|
||||
err error
|
||||
)
|
||||
|
||||
sendService, ok := self.SendServices.Get(service)
|
||||
if !ok {
|
||||
return fmt.Errorf("no such service %s", service)
|
||||
}
|
||||
args := apis.AddConfigInput{
|
||||
DomainId: config.DomainId,
|
||||
Configs: config.Config,
|
||||
}
|
||||
_, err = sendService.AddConfig(ctx, &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) DeleteConfig(ctx context.Context, service, domainId string) error {
|
||||
var (
|
||||
sendService *apis.SendNotificationClient
|
||||
err error
|
||||
)
|
||||
|
||||
sendService, ok := self.SendServices.Get(service)
|
||||
if !ok {
|
||||
return fmt.Errorf("no such service %s", service)
|
||||
}
|
||||
args := apis.DeleteConfigInput{
|
||||
DomainId: domainId,
|
||||
}
|
||||
_, err = sendService.DeleteConfig(ctx, &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error) {
|
||||
|
||||
sendService, ok := self.SendServices.Get(cType)
|
||||
|
||||
@@ -389,7 +483,7 @@ func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, confi
|
||||
return
|
||||
}
|
||||
}
|
||||
param := apis.UpdateConfigParams{
|
||||
param := apis.ValidateConfigInput{
|
||||
Configs: configs,
|
||||
}
|
||||
rep, err := sendService.ValidateConfig(ctx, ¶m)
|
||||
@@ -405,6 +499,44 @@ func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, confi
|
||||
return rep.IsValid, rep.Msg, nil
|
||||
}
|
||||
|
||||
func robotType2ContactType(rType string) string {
|
||||
switch rType {
|
||||
case api.ROBOT_TYPE_FEISHU:
|
||||
return api.FEISHU_ROBOT
|
||||
case api.ROBOT_TYPE_DINGTALK:
|
||||
return api.DINGTALK_ROBOT
|
||||
case api.ROBOT_TYPE_WORKWX:
|
||||
return api.DINGTALK_ROBOT
|
||||
case api.ROBOT_TYPE_WEBHOOK:
|
||||
return api.WEBHOOK
|
||||
}
|
||||
return rType
|
||||
}
|
||||
|
||||
func (self *SRpcService) SendRobotMessage(ctx context.Context, rType string, receivers []*apis.SReceiver, title string, message string) ([]*apis.FailedRecord, error) {
|
||||
log.Infof("rType: %s", rType)
|
||||
contactType := robotType2ContactType(rType)
|
||||
args := apis.BatchSendParams{
|
||||
Receivers: receivers,
|
||||
Title: title,
|
||||
Message: message,
|
||||
}
|
||||
f := func(service *apis.SendNotificationClient) (interface{}, error) {
|
||||
return service.BatchSend(ctx, &args)
|
||||
}
|
||||
|
||||
ret, err := self.execute(ctx, f, contactType)
|
||||
if err != nil {
|
||||
s, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.Error(s.Message())
|
||||
}
|
||||
reply := ret.(*apis.BatchSendReply)
|
||||
return reply.FailedRecords, nil
|
||||
}
|
||||
|
||||
func grpcDialWithUnixSocket(ctx context.Context, socketPath string) (*grpc.ClientConn, error) {
|
||||
return grpc.DialContext(ctx, socketPath, grpc.WithInsecure(), grpc.WithTimeout(time.Second*5), grpc.WithDialer(
|
||||
func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
|
||||
@@ -37,8 +37,9 @@ func InitHandlers(app *appsrv.Application) {
|
||||
db.TenantCacheManager,
|
||||
db.RoleCacheManager,
|
||||
models.SubContactManager,
|
||||
db.SharedResourceManager,
|
||||
models.VerificationManager,
|
||||
models.SubscriptionReceiverManager,
|
||||
models.EventManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
@@ -50,7 +51,9 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.NotificationManager,
|
||||
models.ConfigManager,
|
||||
models.TemplateManager,
|
||||
models.SubscriptionManager,
|
||||
models.TopicManager,
|
||||
models.RobotManager,
|
||||
models.SubscriberManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -37,6 +37,16 @@ func (self *NotificationSendTask) taskFailed(ctx context.Context, notification *
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(reason))
|
||||
}
|
||||
|
||||
type DomainContact struct {
|
||||
DomainId string
|
||||
Contact string
|
||||
}
|
||||
|
||||
type ReceiverSpec struct {
|
||||
receiver models.IReceiver
|
||||
rNotificaion *models.SReceiverNotification
|
||||
}
|
||||
|
||||
func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
notification := obj.(*models.SNotification)
|
||||
if notification.Status == apis.NOTIFICATION_STATUS_OK {
|
||||
@@ -50,20 +60,6 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
}
|
||||
notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_SENDING, "")
|
||||
|
||||
// split rns
|
||||
var (
|
||||
rnsWithReceiver []*models.SReceiverNotification
|
||||
rnsWithoutReceiver []*models.SReceiverNotification
|
||||
)
|
||||
|
||||
for i := range rns {
|
||||
if rns[i].ReceiverID == models.ReceiverIdDefault {
|
||||
rnsWithoutReceiver = append(rnsWithoutReceiver, &rns[i])
|
||||
} else {
|
||||
rnsWithReceiver = append(rnsWithReceiver, &rns[i])
|
||||
}
|
||||
}
|
||||
|
||||
failedRecord := make([]string, 0)
|
||||
sendFail := func(rn *models.SReceiverNotification, reason string) {
|
||||
rn.AfterSend(ctx, false, reason)
|
||||
@@ -71,123 +67,123 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
}
|
||||
|
||||
// build contactMap
|
||||
contactMap := make(map[string]*models.SReceiverNotification)
|
||||
contactMapEn := make(map[string]*models.SReceiverNotification)
|
||||
contactmapCn := make(map[string]*models.SReceiverNotification)
|
||||
for i := range rnsWithReceiver {
|
||||
if len(rnsWithReceiver[i].ReceiverID) == 0 {
|
||||
contactMap[rnsWithReceiver[i].Contact] = rnsWithReceiver[i]
|
||||
continue
|
||||
}
|
||||
receiver, err := rnsWithReceiver[i].Receiver()
|
||||
receivers := make([]ReceiverSpec, 0, 0)
|
||||
receiversEn := make([]ReceiverSpec, 0, len(rns)/2)
|
||||
receiversCn := make([]ReceiverSpec, 0, len(rns)/2)
|
||||
for i := range rns {
|
||||
receiver, err := rns[i].Receiver()
|
||||
if err != nil {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
|
||||
sendFail(&rns[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
|
||||
continue
|
||||
}
|
||||
// check receiver enabled
|
||||
if receiver.Enabled.IsFalse() {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled receiver"))
|
||||
if !receiver.IsEnabled() {
|
||||
sendFail(&rns[i], fmt.Sprintf("disabled receiver"))
|
||||
continue
|
||||
}
|
||||
// check contact enabled
|
||||
enabled, err := receiver.IsEnabledContactType(notification.ContactType)
|
||||
if err != nil {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
|
||||
sendFail(&rns[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
|
||||
continue
|
||||
}
|
||||
if !enabled {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
|
||||
sendFail(&rns[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
|
||||
continue
|
||||
}
|
||||
|
||||
// check contact verified
|
||||
verified, err := receiver.IsVerifiedContactType(notification.ContactType)
|
||||
if err != nil {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
|
||||
sendFail(&rns[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
|
||||
continue
|
||||
}
|
||||
if !verified {
|
||||
sendFail(rnsWithReceiver[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
|
||||
sendFail(&rns[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
|
||||
continue
|
||||
}
|
||||
|
||||
contact, err := receiver.GetContact(notification.ContactType)
|
||||
if err != nil {
|
||||
reason := fmt.Sprintf("fail to fetch contact: %s", err.Error())
|
||||
sendFail(rnsWithReceiver[i], reason)
|
||||
continue
|
||||
}
|
||||
// contact, err := receiver.GetContact(notification.ContactType)
|
||||
// if err != nil {
|
||||
// reason := fmt.Sprintf("fail to fetch contact: %s", err.Error())
|
||||
// sendFail(&rns[i], reason)
|
||||
// continue
|
||||
// }
|
||||
lang, err := receiver.GetTemplateLang(ctx)
|
||||
if err != nil {
|
||||
reason := fmt.Sprintf("fail to GetTemplateLang: %s", err.Error())
|
||||
sendFail(rnsWithReceiver[i], reason)
|
||||
sendFail(&rns[i], reason)
|
||||
continue
|
||||
}
|
||||
switch lang {
|
||||
case "":
|
||||
contactMap[contact] = rnsWithReceiver[i]
|
||||
receivers = append(receivers, ReceiverSpec{
|
||||
receiver: receiver,
|
||||
rNotificaion: &rns[i],
|
||||
})
|
||||
case apis.TEMPLATE_LANG_EN:
|
||||
contactMapEn[contact] = rnsWithReceiver[i]
|
||||
receiversEn = append(receiversEn, ReceiverSpec{
|
||||
receiver: receiver,
|
||||
rNotificaion: &rns[i],
|
||||
})
|
||||
case apis.TEMPLATE_LANG_CN:
|
||||
contactmapCn[contact] = rnsWithReceiver[i]
|
||||
receiversCn = append(receiversCn, ReceiverSpec{
|
||||
receiver: receiver,
|
||||
rNotificaion: &rns[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for i := range rnsWithoutReceiver {
|
||||
contactMap[rnsWithoutReceiver[i].Contact] = rnsWithoutReceiver[i]
|
||||
}
|
||||
|
||||
var contactLen int
|
||||
for lang, contactMap := range map[string]map[string]*models.SReceiverNotification{
|
||||
"": contactMap,
|
||||
apis.TEMPLATE_LANG_CN: contactmapCn,
|
||||
apis.TEMPLATE_LANG_EN: contactMapEn,
|
||||
for lang, receivers := range map[string][]ReceiverSpec{
|
||||
"": receivers,
|
||||
apis.TEMPLATE_LANG_CN: receiversCn,
|
||||
apis.TEMPLATE_LANG_EN: receiversEn,
|
||||
} {
|
||||
if len(contactMap) == 0 {
|
||||
if len(receivers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// send
|
||||
p, err := notification.TemplateStore().FillWithTemplate(ctx, lang, notification.Notification())
|
||||
nn, err := notification.Notification()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, notification, err.Error(), false)
|
||||
}
|
||||
p, err := notification.TemplateStore().FillWithTemplate(ctx, lang, nn)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, notification, err.Error(), false)
|
||||
}
|
||||
// set status before send
|
||||
now := time.Now()
|
||||
contacts := make([]string, 0, len(contactMap))
|
||||
for c, rn := range contactMap {
|
||||
rn.BeforeSend(ctx, now)
|
||||
contacts = append(contacts, c)
|
||||
for _, rn := range receivers {
|
||||
rn.rNotificaion.BeforeSend(ctx, now)
|
||||
}
|
||||
|
||||
contactLen += len(contacts)
|
||||
contactLen += len(receivers)
|
||||
|
||||
// send
|
||||
fds, err := models.NotifyService.BatchSend(ctx, notification.ContactType, rpcapi.BatchSendParams{
|
||||
Contacts: contacts,
|
||||
Title: p.Title,
|
||||
Message: p.Message,
|
||||
Priority: p.Priority,
|
||||
RemoteTemplate: p.RemoteTemplate,
|
||||
})
|
||||
fds, err := self.batchSend(ctx, notification.ContactType, receivers, p)
|
||||
if err != nil {
|
||||
for _, rn := range contactMap {
|
||||
sendFail(rn, err.Error())
|
||||
for _, r := range receivers {
|
||||
sendFail(r.rNotificaion, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
// check result
|
||||
failedRnIds := make(map[int64]struct{}, 0)
|
||||
for _, fd := range fds {
|
||||
rn := contactMap[fd.Contact]
|
||||
sendFail(rn, fd.Reason)
|
||||
delete(contactMap, fd.Contact)
|
||||
sendFail(fd.rNotificaion, fd.Reason)
|
||||
failedRnIds[fd.rNotificaion.RowId] = struct{}{}
|
||||
}
|
||||
// after send for successful notify
|
||||
for _, rn := range contactMap {
|
||||
rn.AfterSend(ctx, true, "")
|
||||
for _, r := range receivers {
|
||||
if _, ok := failedRnIds[r.rNotificaion.RowId]; ok {
|
||||
continue
|
||||
}
|
||||
r.rNotificaion.AfterSend(ctx, true, "")
|
||||
}
|
||||
}
|
||||
if len(failedRecord) > 0 && len(failedRecord) == contactLen {
|
||||
if len(failedRecord) > 0 && len(failedRecord) >= len(rns) {
|
||||
self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
|
||||
return
|
||||
}
|
||||
@@ -199,3 +195,83 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
logclient.AddActionLogWithContext(ctx, notification, logclient.ACT_SEND_NOTIFICATION, "", self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
type FailedReceiverSpec struct {
|
||||
ReceiverSpec
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (self *NotificationSendTask) batchSend(ctx context.Context, contactType string, receivers []ReceiverSpec, params rpcapi.SendParams) (fails []FailedReceiverSpec, err error) {
|
||||
log.Infof("contactType: %s, receivers: %s, params: %s", contactType, receivers, jsonutils.Marshal(params))
|
||||
if contactType != apis.ROBOT && contactType != apis.WEBHOOK {
|
||||
return self._batchSend(ctx, contactType, receivers, func(res []*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error) {
|
||||
return models.NotifyService.BatchSend(ctx, contactType, rpcapi.BatchSendParams{
|
||||
Receivers: res,
|
||||
Title: params.Title,
|
||||
Message: params.Message,
|
||||
Priority: params.Priority,
|
||||
RemoteTemplate: params.RemoteTemplate,
|
||||
})
|
||||
})
|
||||
}
|
||||
robots := make(map[string][]ReceiverSpec)
|
||||
for i := range receivers {
|
||||
robot := receivers[i].receiver.(*models.SRobot)
|
||||
robots[robot.Type] = append(robots[robot.Type], receivers[i])
|
||||
}
|
||||
for rType, robots := range robots {
|
||||
_fails, err := self._batchSend(ctx, contactType, robots, func(res []*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error) {
|
||||
return models.NotifyService.SendRobotMessage(ctx, rType, res, params.Title, params.Message)
|
||||
})
|
||||
if err != nil {
|
||||
for i := range robots {
|
||||
fails = append(fails, FailedReceiverSpec{
|
||||
ReceiverSpec: robots[i],
|
||||
Reason: err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
fails = append(fails, _fails...)
|
||||
}
|
||||
return fails, nil
|
||||
}
|
||||
|
||||
func (self *NotificationSendTask) _batchSend(ctx context.Context, contactType string, receivers []ReceiverSpec, send func([]*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error)) (fails []FailedReceiverSpec, err error) {
|
||||
rpcReceivers := make([]*rpcapi.SReceiver, len(receivers))
|
||||
rpc2Receiver := make(map[DomainContact]ReceiverSpec, len(receivers))
|
||||
for i := range receivers {
|
||||
contact, err := receivers[i].receiver.GetContact(contactType)
|
||||
if err != nil {
|
||||
fails = append(fails, FailedReceiverSpec{
|
||||
ReceiverSpec: receivers[i],
|
||||
Reason: fmt.Sprintf("fail to fetch contact: %s", err.Error()),
|
||||
})
|
||||
continue
|
||||
}
|
||||
rpcReceivers[i] = &rpcapi.SReceiver{
|
||||
DomainId: receivers[i].receiver.GetDomainId(),
|
||||
Contact: contact,
|
||||
}
|
||||
rpc2Receiver[DomainContact{
|
||||
DomainId: receivers[i].receiver.GetDomainId(),
|
||||
Contact: contact,
|
||||
}] = receivers[i]
|
||||
}
|
||||
fds, err := send(rpcReceivers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check result
|
||||
for _, fd := range fds {
|
||||
dc := DomainContact{
|
||||
DomainId: fd.Receiver.DomainId,
|
||||
Contact: fd.Receiver.Contact,
|
||||
}
|
||||
receiver := rpc2Receiver[dc]
|
||||
fails = append(fails, FailedReceiverSpec{
|
||||
ReceiverSpec: receiver,
|
||||
Reason: fd.Reason,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
@@ -61,6 +62,13 @@ func (self *RepullSuncontactTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
}
|
||||
subq := models.SubContactManager.Query("receiver_id").Equals("type", config.Type).SubQuery()
|
||||
q := models.ReceiverManager.Query()
|
||||
if config.Attribution == notify.CONFIG_ATTRIBUTION_DOMAIN {
|
||||
q = q.Equals("domain_id", config.DomainId)
|
||||
} else {
|
||||
// The system-level config update should not affect the receiver under the domain with config
|
||||
configq := models.ConfigManager.Query("domain_id").Equals("attribution", notify.CONFIG_ATTRIBUTION_DOMAIN).SubQuery()
|
||||
q = q.Join(configq, sqlchemy.NotEquals(q.Field("domain_id"), configq.Field("domain_id")))
|
||||
}
|
||||
q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("receiver_id")))
|
||||
rs := make([]models.SReceiver, 0)
|
||||
err := db.FetchModelObjects(models.ReceiverManager, q, &rs)
|
||||
|
||||
@@ -72,7 +72,7 @@ func (self *SubcontactPullTask) OnInit(ctx context.Context, obj db.IStandaloneMo
|
||||
if !utils.IsInStringArray(cType, PullContactType) {
|
||||
continue
|
||||
}
|
||||
userid, err := models.NotifyService.ContactByMobile(ctx, receiver.Mobile, cType)
|
||||
userid, err := models.NotifyService.ContactByMobile(ctx, receiver.Mobile, cType, receiver.GetDomainId())
|
||||
if err != nil {
|
||||
var reason string
|
||||
if errors.Cause(err) == notify.ErrNoSuchMobile {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
notifyv2 "yunion.io/x/onecloud/pkg/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/models"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
@@ -81,7 +82,10 @@ func (self *VerificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
self.taskFailed(ctx, receiver, err.Error())
|
||||
return
|
||||
}
|
||||
param.Contact = contact
|
||||
param.Receiver = &apis.SReceiver{
|
||||
Contact: contact,
|
||||
DomainId: receiver.DomainId,
|
||||
}
|
||||
err = models.NotifyService.Send(ctx, contactType, param)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, receiver, err.Error())
|
||||
|
||||
Reference in New Issue
Block a user