Merge pull request #11424 from rainzm/notify/domain_subs

Notify managed by domain admin
This commit is contained in:
Zexi Li
2021-06-24 10:42:16 +08:00
committed by GitHub
87 changed files with 4317 additions and 1573 deletions
@@ -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 }}
+8 -113
View File
@@ -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))
}
+3
View File
@@ -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,
+2
View File
@@ -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))
}
+32
View File
@@ -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))
}
+11 -6
View File
@@ -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))
}
+14 -5
View File
@@ -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
View File
@@ -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"
)
+42 -1
View File
@@ -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
}
+11 -3
View File
@@ -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
+4
View File
@@ -162,3 +162,7 @@ type ReceiverIntellijGetInput struct {
// required: true
Scope string `json:"scope"`
}
type ReceiverEnableContactTypeInput struct {
EnabledContactTypes []string `json:"enabled_contact_types"`
}
+43
View File
@@ -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"`
}
+118
View File
@@ -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
}
-77
View File
@@ -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
}
+36
View File
@@ -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 {
}
+42 -22
View File
@@ -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
+1
View File
@@ -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))
}
+3
View File
@@ -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
+5
View File
@@ -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
)
+24 -10
View File
@@ -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
+23
View File
@@ -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)
+84
View File
@@ -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())
}
}
}
}
}
+5
View File
@@ -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,
+1 -3
View File
@@ -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(
+1
View File
@@ -95,6 +95,7 @@ func InitHandlers(app *appsrv.Application) {
models.CloudimageManager,
models.WafRuleStatementManager,
models.BillingResourceCheckManager,
} {
db.RegisterModelManager(manager)
}
+2
View File
@@ -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
+5
View File
@@ -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)
}
+5
View File
@@ -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())
}
+10 -1
View File
@@ -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)
}
+15 -2
View File
@@ -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()))
}
+9 -4
View File
@@ -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)
}
+5
View File
@@ -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)
}
+10 -2
View File
@@ -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) {
+5 -1
View File
@@ -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)
}
+17 -12
View File
@@ -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)
}
+6 -11
View File
@@ -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
+6 -10
View File
@@ -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)
}
+10 -2
View File
@@ -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)
}
+30 -12
View File
@@ -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)
}
+1
View File
@@ -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),
+82
View File
@@ -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
}
+22 -1
View File
@@ -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
}
+69
View File
@@ -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
}
+44 -47
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+68
View File
@@ -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
}
+64 -30
View File
@@ -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",
"成功",
},
)
}
+2 -1
View File
@@ -32,7 +32,8 @@ func InitDB() error {
ConfigManager,
TemplateManager,
ReceiverNotificationManager,
SubscriptionManager,
TopicManager,
RobotManager,
} {
err := manager.InitializeData()
if err != nil {
+187 -79
View File
@@ -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
+72 -8
View File
@@ -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
}
+105 -3
View File
@@ -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
}
+337
View File
@@ -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())
}
}
}
+564
View File
@@ -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
}
+80
View File
@@ -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
}
-577
View File
@@ -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]
}
-201
View File
@@ -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,
}
}
+511
View File
@@ -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)
}
+26 -2
View File
@@ -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()
+571 -99
View File
@@ -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,
+45 -8
View File
@@ -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
View File
@@ -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, &param)
@@ -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) {
+5 -2
View File
@@ -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)
+146 -70
View File
@@ -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)
+1 -1
View File
@@ -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 {
+5 -1
View File
@@ -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())