mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
feat(notify): topic and subscriber at the domain level
This commit is contained in:
@@ -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("set-receiver", new(notify.SubscriberSetReceiverOptions))
|
||||
cmd1.Perform("enable", new(notify.SubscriberOptions))
|
||||
cmd1.Perform("disable", new(notify.SubscriberOptions))
|
||||
}
|
||||
|
||||
+27
-25
@@ -84,31 +84,33 @@ const (
|
||||
RECEIVER_TYPE_CONTACT = "contact"
|
||||
RECEIVER_TYPE_ROBOT = "robot"
|
||||
|
||||
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"
|
||||
TOPIC_TYPE_RESOURCE = "resource"
|
||||
TOPIC_TYPE_AUTOMATED_PROCESS = "automated_process"
|
||||
|
||||
SUBSCRIPTION_TYPE_RESOURCE = "resource"
|
||||
SUBSCRIPTION_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"
|
||||
|
||||
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"
|
||||
SUBSCRIBER_TYPE_ROLE = "role"
|
||||
SUBSCRIBER_TYPE_ROBOT = "robot"
|
||||
SUBSCRIBER_TYPE_RECEIVER = "receiver"
|
||||
|
||||
SUBSCRIBER_SCOPE_SYSTEM = "system"
|
||||
SUBSCRIBER_SCOPE_DOMAIN = "domain"
|
||||
SUBSCRIBER_SCOPE_PROJECT = "project"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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.VirtualResourceCreateInput
|
||||
|
||||
// description: Id of Topic
|
||||
// required
|
||||
TopicID string
|
||||
|
||||
// description: scope of resource
|
||||
// enum: system,domain,project
|
||||
ResourceScope 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
|
||||
}
|
||||
|
||||
type SubscriberListInput struct {
|
||||
apis.VirtualResourceListInput
|
||||
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
|
||||
}
|
||||
|
||||
type Identification struct {
|
||||
// example: 036fed49483b412888a760c2bc995caa
|
||||
ID string `json:"id"`
|
||||
// example: test
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type SubscriberDetails struct {
|
||||
apis.VirtualResourceDetails
|
||||
SSubscriber
|
||||
|
||||
// description: receivers
|
||||
Receivers []Identification
|
||||
|
||||
// description: role
|
||||
Role Identification
|
||||
|
||||
// description: robot
|
||||
Robot Identification
|
||||
}
|
||||
|
||||
type SubscriberSetReceiverInput struct {
|
||||
Receivers []string
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type SubscriptionListInput struct {
|
||||
}
|
||||
|
||||
type SubscriptionDetails struct {
|
||||
apis.StatusStandaloneResourceDetails
|
||||
// description: resources managed
|
||||
// example: ["server", "eip", "disk"]
|
||||
Resources []string `json:"resource_types"`
|
||||
// description: receivers of the message sent
|
||||
Receivers SubscriptionReceiver `json:"receivers"`
|
||||
// description: type of robot send message
|
||||
// example: dingtalk_robot
|
||||
Robot string `json:"robot"`
|
||||
// description: webhook send message
|
||||
// example: webhook
|
||||
Webhook string `json:"webhook"`
|
||||
}
|
||||
|
||||
type IDAndName struct {
|
||||
// example: 036fed49483b412888a760c2bc995caa
|
||||
ID string `json:"id"`
|
||||
// example: test
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ReceivingRoleIDAndName struct {
|
||||
IDAndName
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionReceiver struct {
|
||||
Receivers []IDAndName `json:"receivers"`
|
||||
ReceivingRoles []ReceivingRoleIDAndName `json:"roles"`
|
||||
}
|
||||
|
||||
type SubscriptionSetReceiverInput struct {
|
||||
ReceivingRoles []ReceivingRole `json:"roles"`
|
||||
Receivers []string `json:"receivers"`
|
||||
}
|
||||
|
||||
type ReceivingRole struct {
|
||||
// description: id or name of role
|
||||
Role string `json:"role"`
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionSetRobotInput struct {
|
||||
// description: robot
|
||||
// enum: feishu-robot,dingtalk-robot,workwx-robot
|
||||
Robot string
|
||||
}
|
||||
|
||||
type SubscriptionSetWebhookInput struct {
|
||||
Webhook string
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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"`
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -21,13 +21,14 @@ type ConfigsManager struct {
|
||||
}
|
||||
|
||||
var (
|
||||
NotifyReceiver modulebase.ResourceManager
|
||||
NotifyConfig modulebase.ResourceManager
|
||||
NotifyRobot 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() {
|
||||
@@ -71,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)
|
||||
}
|
||||
|
||||
@@ -14,84 +14,76 @@
|
||||
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 {
|
||||
Name string `positional:"true"`
|
||||
TopicId string `positional:"true"`
|
||||
ResourceScope string `positional:"true" choices:"system|domain|project"`
|
||||
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 if 'role'"`
|
||||
Robot string `help:"required if type if 'robot'"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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 SubscriberSetReceiverOptions struct {
|
||||
SubscriberOptions
|
||||
Receivers []string
|
||||
}
|
||||
|
||||
func (ssr *SubscriberSetReceiverOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("receivers", jsonutils.NewStringArray(ssr.Receivers))
|
||||
return params, nil
|
||||
}
|
||||
|
||||
@@ -182,12 +182,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 +212,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,87 +307,87 @@ 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",
|
||||
"定时任务",
|
||||
},
|
||||
|
||||
@@ -32,8 +32,8 @@ func InitDB() error {
|
||||
ConfigManager,
|
||||
TemplateManager,
|
||||
ReceiverNotificationManager,
|
||||
SubscriptionManager,
|
||||
RobotManager,
|
||||
TopicManager,
|
||||
RobotManager,
|
||||
} {
|
||||
err := manager.InitializeData()
|
||||
if err != nil {
|
||||
|
||||
@@ -232,16 +232,16 @@ 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")
|
||||
}
|
||||
@@ -252,14 +252,14 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
|
||||
// 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))
|
||||
@@ -267,22 +267,6 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
robots = sets.NewString(robots...).UnsortedList()
|
||||
}
|
||||
|
||||
// webhook
|
||||
var webhooks []string
|
||||
for i := range subscriptions {
|
||||
webhook, err := SubscriptionReceiverManager.webhook(subscriptions[i].Id)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != errors.ErrNotFound {
|
||||
return output, errors.Wrapf(err, "unable to fetch webhook of subscription %q", subscriptions[i].Id)
|
||||
}
|
||||
} else {
|
||||
webhooks = append(webhooks, webhook)
|
||||
}
|
||||
}
|
||||
if len(webhooks) > 0 {
|
||||
webhooks = sets.NewString(webhooks...).UnsortedList()
|
||||
}
|
||||
|
||||
message := jsonutils.Marshal(input.ResourceDetails).String()
|
||||
|
||||
// fillter non-existed receiver
|
||||
@@ -313,39 +297,56 @@ 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)
|
||||
err := nm.create(ctx, userCred, ct, receiverIds, nil, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
if err != nil {
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: ct,
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// 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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = nm.createWithRobots(ctx, userCred, robots, input.Priority, "", message, input.Event, input.AdvanceDays)
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.ROBOT,
|
||||
Reason: err.Error(),
|
||||
})
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) createWithRobots(ctx context.Context, userCred mcclient.TokenCredential, robotIds []string, priority, topic, message, event string, advanceDays int) error {
|
||||
if len(robotIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := &SNotification{
|
||||
ContactType: api.ROBOT,
|
||||
Message: message,
|
||||
Priority: priority,
|
||||
ReceivedAt: time.Now(),
|
||||
Event: event,
|
||||
AdvanceDays: advanceDays,
|
||||
}
|
||||
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, topic, message, event string, advanceDays int) error {
|
||||
if len(receiverIds)+len(contacts) == 0 {
|
||||
log.Infof("%s: no send", contactType)
|
||||
@@ -374,7 +375,7 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To
|
||||
for i := range contacts {
|
||||
_, 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)
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
// 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/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/stringutils2"
|
||||
)
|
||||
|
||||
var SubscriberManager *SSubscriberManager
|
||||
|
||||
func init() {
|
||||
SubscriberManager = &SSubscriberManager{
|
||||
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
||||
SSubscriber{},
|
||||
"subscriber_tbl",
|
||||
"subscriber",
|
||||
"subscribers",
|
||||
),
|
||||
}
|
||||
SubscriberManager.SetVirtualObject(ReceiverNotificationManager)
|
||||
}
|
||||
|
||||
type SSubscriberManager struct {
|
||||
db.SVirtualResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriber struct {
|
||||
db.SVirtualResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
TopicID string `width:"128" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user"`
|
||||
Type string `width:"16" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user"`
|
||||
Identification string `width:"128" charset:"ascii" nullable:"false"`
|
||||
RoleScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user"`
|
||||
ResourceScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user"`
|
||||
}
|
||||
|
||||
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) {
|
||||
var err error
|
||||
input.VirtualResourceCreateInput, err = sm.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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.SVirtualResourceBase.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
|
||||
}
|
||||
return 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.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VirtualResourceListInput)
|
||||
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")
|
||||
}
|
||||
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.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]api.SubscriberDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].VirtualResourceDetails = 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) 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("subscription_id", tid)
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_PROJECT),
|
||||
sqlchemy.Equals(q.Field("project_id"), projectId),
|
||||
),
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_DOMAIN),
|
||||
sqlchemy.Equals(q.Field("domain_id"), projectDomainId),
|
||||
),
|
||||
sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_SYSTEM),
|
||||
))
|
||||
switch len(types) {
|
||||
case 0:
|
||||
case 1:
|
||||
q = q.Equals("type", types[0])
|
||||
default:
|
||||
q = q.In("type", types)
|
||||
}
|
||||
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_ROBOT {
|
||||
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 s.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, s, "set-receiver")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberSetReceiverInput) (jsonutils.JSONObject, error) {
|
||||
reIds, err := SubscriberManager.validateReceivers(ctx, input.Receivers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, s.SetReceivers(ctx, reIds)
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, s, "enable")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(s, ctx, userCred, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SSubscriber) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, s, "disable")
|
||||
}
|
||||
|
||||
func (s *SSubscriber) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
err := db.EnabledPerformEnable(s, ctx, userCred, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnabledPerformEnable")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
)
|
||||
|
||||
var SubscriberReceiverManager *SSubscriberReceiverManager
|
||||
|
||||
func init() {
|
||||
SubscriberReceiverManager = &SSubscriberReceiverManager{
|
||||
SJointResourceBaseManager: db.NewJointResourceBaseManager(
|
||||
SSubscriberReceiver{},
|
||||
"subscriber_receiver_tbl",
|
||||
"subscriber_receiver",
|
||||
"subscriber_receivers",
|
||||
SubscriberManager,
|
||||
ReceiverManager,
|
||||
),
|
||||
}
|
||||
SubscriberReceiverManager.SetVirtualObject(SubscriberReceiverManager)
|
||||
}
|
||||
|
||||
type SSubscriberReceiverManager struct {
|
||||
db.SJointResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriberReceiver struct {
|
||||
db.SJointResourceBase
|
||||
SubscriberId string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
ReceiverId string `width:"36" charset:"ascii" nullable:"false" index:"true"`
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) getBySubscriberId(sId string) ([]SSubscriberReceiver, error) {
|
||||
q := srm.Query().Equals("subscriber_id", sId)
|
||||
srs := make([]SSubscriberReceiver, 0, 2)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) create(ctx context.Context, sId, receiverId string) (*SSubscriberReceiver, error) {
|
||||
sr := &SSubscriberReceiver{
|
||||
SubscriberId: sId,
|
||||
ReceiverId: receiverId,
|
||||
}
|
||||
return sr, srm.TableSpec().Insert(ctx, sr)
|
||||
}
|
||||
|
||||
func (srm *SSubscriberReceiverManager) delete(sId, receiverId string) error {
|
||||
q := srm.Query().Equals("receiver_id", receiverId)
|
||||
var sr SSubscriberReceiver
|
||||
err := q.First(&sr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srp := &sr
|
||||
_, err = db.Update(srp, func() error {
|
||||
srp.MarkDelete()
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -1,577 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/bitmap"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
func parseEvent(es string) (notify.SEvent, error) {
|
||||
ess := strings.Split(es, notify.DelimiterInEvent)
|
||||
if len(ess) != 2 {
|
||||
return notify.SEvent{}, fmt.Errorf("invalid event string %q", es)
|
||||
}
|
||||
return notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1])), nil
|
||||
}
|
||||
|
||||
type SSubscriptionManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
var SubscriptionManager *SSubscriptionManager
|
||||
|
||||
func init() {
|
||||
SubscriptionManager = &SSubscriptionManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscription{},
|
||||
"subscription_tbl",
|
||||
"subscription",
|
||||
"subscriptions",
|
||||
),
|
||||
}
|
||||
SubscriptionManager.SetVirtualObject(SubscriptionManager)
|
||||
}
|
||||
|
||||
type SSubscription struct {
|
||||
db.SStandaloneResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Resources uint64 `nullable:"false"`
|
||||
Actions uint32 `nullable:"false"`
|
||||
AdvanceDays int `nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultResourceCreateDelete = "resource create or delete"
|
||||
DefaultResourceChangeConfig = "resource change config"
|
||||
DefaultResourceUpdate = "resource update"
|
||||
DefaultResourceReleaseDue1Day = "resource release due 1 day"
|
||||
DefaultResourceReleaseDue3Day = "resource release due 3 day"
|
||||
DefaultScheduledTaskExecute = "scheduled task execute"
|
||||
DefaultScalingPolicyExecute = "scaling policy execute"
|
||||
DefaultSnapshotPolicyExecute = "snapshot policy execute"
|
||||
)
|
||||
|
||||
func (sm *SSubscriptionManager) InitializeData() error {
|
||||
initSNames := sets.NewString(
|
||||
DefaultResourceCreateDelete,
|
||||
DefaultResourceChangeConfig,
|
||||
DefaultResourceUpdate,
|
||||
DefaultResourceReleaseDue1Day,
|
||||
DefaultResourceReleaseDue3Day,
|
||||
DefaultScheduledTaskExecute,
|
||||
DefaultScalingPolicyExecute,
|
||||
DefaultSnapshotPolicyExecute,
|
||||
)
|
||||
q := sm.Query()
|
||||
subscriptions := make([]SSubscription, 0, initSNames.Len())
|
||||
err := db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
for i := range subscriptions {
|
||||
ss := &subscriptions[i]
|
||||
initSNames.Delete(ss.Name)
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, name := range initSNames.UnsortedList() {
|
||||
ss := new(SSubscription)
|
||||
ss.Name = name
|
||||
switch name {
|
||||
case DefaultResourceCreateDelete:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceChangeConfig:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionChangeConfig)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceUpdate:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionUpdate)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceReleaseDue1Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 1
|
||||
case DefaultResourceReleaseDue3Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 3
|
||||
case DefaultScheduledTaskExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultScalingPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultSnapshotPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
}
|
||||
err := sm.TableSpec().Insert(ctx, ss)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to insert %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input notify.SubscriptionListInput) (*sqlchemy.SQuery, error) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []notify.SubscriptionDetails {
|
||||
sRows := sm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]notify.SubscriptionDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneResourceDetails = sRows[i]
|
||||
ss := objs[i].(*SSubscription)
|
||||
rows[i].Resources = ss.getResources()
|
||||
srs, err := ss.subscriptionReceiverDiss()
|
||||
if err != nil {
|
||||
log.Errorf("unable to get subscriptionReceivers: %v", err)
|
||||
}
|
||||
for j := range srs {
|
||||
sr := &srs[j]
|
||||
switch sr.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
rows[i].Receivers.Receivers = append(rows[i].Receivers.Receivers, notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.ReceiverName,
|
||||
})
|
||||
case ReceiverRole:
|
||||
rows[i].Receivers.ReceivingRoles = append(rows[i].Receivers.ReceivingRoles, notify.ReceivingRoleIDAndName{
|
||||
IDAndName: notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.RoleName,
|
||||
},
|
||||
Scope: sr.RoleScope,
|
||||
})
|
||||
case ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot:
|
||||
rows[i].Robot = sr.ReceiverType
|
||||
case ReceiverWeebhook:
|
||||
rows[i].Webhook = sr.ReceiverType
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverDis struct {
|
||||
SSubscriptionReceiver
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
func (s *SSubscription) subscriptionReceiverDiss() ([]SSubscriptionReceiverDis, error) {
|
||||
q := SubscriptionReceiverManager.Query().Equals("subscription_id", s.Id)
|
||||
rq := ReceiverManager.Query("id", "name").SubQuery()
|
||||
roq := db.RoleCacheManager.Query("id", "name").SubQuery()
|
||||
q = q.LeftJoin(rq, sqlchemy.Equals(q.Field("receiver"), rq.Field("id")))
|
||||
q = q.LeftJoin(roq, sqlchemy.Equals(q.Field("receiver"), roq.Field("id")))
|
||||
// It looks strange, but the order of append cannot be changed
|
||||
q.AppendField(q.QueryFields()...)
|
||||
q.AppendField(rq.Field("name", "receiver_name"))
|
||||
q.AppendField(roq.Field("name", "role_name"))
|
||||
srs := make([]SSubscriptionReceiverDis, 0)
|
||||
err := q.All(&srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch All")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return nil, httperrors.NewForbiddenError("prohibit creation")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return input, httperrors.NewForbiddenError("update prohibited")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("prohibit deletion")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-receiver")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetReceiverInput) (jsonutils.JSONObject, error) {
|
||||
// check input
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
// check receiving roles
|
||||
validScopes := []string{string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain), string(rbacutils.ScopeProject)}
|
||||
for i := range input.ReceivingRoles {
|
||||
role := input.ReceivingRoles[i]
|
||||
index := i
|
||||
errgo.Go(func() error {
|
||||
if len(role.Scope) == 0 {
|
||||
return httperrors.NewInputParameterError("empty scope for role %q", role.Role)
|
||||
}
|
||||
if !utils.IsInStringArray(role.Scope, validScopes) {
|
||||
return httperrors.NewInputParameterError("invalid scope %q for role %q, need %s, %s or %s", role.Scope, role.Role, rbacutils.ScopeSystem, rbacutils.ScopeDomain, rbacutils.ScopeProject)
|
||||
}
|
||||
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, role.Role)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable find role %q", role)
|
||||
}
|
||||
input.ReceivingRoles[index].Role = roleCache.GetId()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err := errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers, err := ReceiverManager.FetchByIdOrNames(ctx, input.Receivers...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch Receivers")
|
||||
}
|
||||
reSet := sets.NewString(input.Receivers...)
|
||||
reIds := make([]string, len(receivers))
|
||||
for i := range receivers {
|
||||
reSet.Delete(receivers[i].GetId())
|
||||
reSet.Delete(receivers[i].GetName())
|
||||
reIds[i] = receivers[i].GetId()
|
||||
}
|
||||
if reSet.Len() > 0 {
|
||||
return nil, httperrors.NewInputParameterError("receivers %q not found", strings.Join(reSet.UnsortedList(), ", "))
|
||||
}
|
||||
input.Receivers = reIds
|
||||
|
||||
// deal with subscriptionReceivers
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, ReceiverNormal, ReceiverRole)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
reSet = sets.NewString(input.Receivers...)
|
||||
reRoleSet := make(map[notify.ReceivingRole]struct{})
|
||||
for _, role := range input.ReceivingRoles {
|
||||
reRoleSet[role] = struct{}{}
|
||||
}
|
||||
for i := range srs {
|
||||
rs := &srs[i]
|
||||
switch rs.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
if !reSet.Has(rs.Receiver) {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
reSet.Delete(rs.Receiver)
|
||||
case ReceiverRole:
|
||||
role := rs.receivingRole()
|
||||
if _, ok := reRoleSet[role]; !ok {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
delete(reRoleSet, role)
|
||||
}
|
||||
}
|
||||
for _, re := range reSet.UnsortedList() {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, re, ReceiverNormal, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create receiver %s", re)
|
||||
}
|
||||
}
|
||||
for role := range reRoleSet {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, role.Role, ReceiverRole, role.Scope)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create role %s", role)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-robot")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetRobotInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Robot, ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown robot %q", input.Robot)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-webhook")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetWebhookInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Webhook, ReceiverWeebhook)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown webhook %q", input.Webhook)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) setSingleReceiver(ctx context.Context, re string, reTypes ...string) error {
|
||||
receiverRobot := reTypes
|
||||
if !utils.IsInStringArray(re, receiverRobot) {
|
||||
return errors.ErrNotFound
|
||||
}
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, receiverRobot...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
// create one
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, "", re, "")
|
||||
return err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return fmt.Errorf("multi robot receiver")
|
||||
}
|
||||
// update
|
||||
sr := &srs[0]
|
||||
_, err = db.Update(sr, func() error {
|
||||
sr.ReceiverType = re
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SSubscription) addResources(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
v := converter.resourceValue(resource)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Resources += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) addAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
v := converter.actionValue(action)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Actions += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) getResources() []string {
|
||||
vs := bitmap.Uint64ToIntArray(s.Resources)
|
||||
resources := make([]string, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
resources = append(resources, converter.resource(v))
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func (s *SSubscription) getActions() []notify.SAction {
|
||||
vs := bitmap.Uint2IntArray(s.Actions)
|
||||
actions := make([]notify.SAction, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
actions = append(actions, converter.action(v))
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) SubsciptionByEvent(eventStr string, advanceDays int) ([]SSubscription, error) {
|
||||
event, err := parseEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to parse event %q", event)
|
||||
}
|
||||
resourceV := converter.resourceValue(event.ResourceType())
|
||||
actionV := converter.actionValue(event.Action())
|
||||
q := sm.Query().Equals("advance_days", advanceDays)
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("resources"), 1<<resourceV), 0))
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("actions"), 1<<actionV), 0))
|
||||
var subscriptions []SSubscription
|
||||
err = db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
q.DebugQuery()
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
converter = &sConverter{
|
||||
resourceValueMap: make(map[string]int, 5),
|
||||
resourceList: make([]string, 0, 5),
|
||||
actionList: make([]notify.SAction, 0, 5),
|
||||
actionValueMap: make(map[notify.SAction]int, 5),
|
||||
}
|
||||
converter.registerResource(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SECGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK,
|
||||
)
|
||||
converter.registerAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
notify.ActionUpdate,
|
||||
notify.ActionRebuildRoot,
|
||||
notify.ActionResetPassword,
|
||||
notify.ActionChangeConfig,
|
||||
notify.ActionExpiredRelease,
|
||||
notify.ActionExecute,
|
||||
)
|
||||
}
|
||||
|
||||
var converter *sConverter
|
||||
|
||||
type sConverter struct {
|
||||
resourceValueMap map[string]int
|
||||
resourceList []string
|
||||
actionValueMap map[notify.SAction]int
|
||||
actionList []notify.SAction
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerResource(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
if _, ok := rc.resourceValueMap[resource]; ok {
|
||||
return
|
||||
}
|
||||
rc.resourceList = append(rc.resourceList, resource)
|
||||
rc.resourceValueMap[resource] = len(rc.resourceList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
if _, ok := rc.actionValueMap[action]; ok {
|
||||
return
|
||||
}
|
||||
rc.actionList = append(rc.actionList, action)
|
||||
rc.actionValueMap[action] = len(rc.actionList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) resourceValue(resource string) int {
|
||||
v, ok := rc.resourceValueMap[resource]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) resource(resourceValue int) string {
|
||||
if resourceValue < 0 || resourceValue >= len(rc.resourceList) {
|
||||
return ""
|
||||
}
|
||||
return rc.resourceList[resourceValue]
|
||||
}
|
||||
|
||||
func (rc *sConverter) actionValue(action notify.SAction) int {
|
||||
v, ok := rc.actionValueMap[action]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) action(actionValue int) notify.SAction {
|
||||
if actionValue < 0 || actionValue >= len(rc.actionList) {
|
||||
return notify.SAction("")
|
||||
}
|
||||
return rc.actionList[actionValue]
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var SubscriptionReceiverManager *SSubscriptionReceiverManager
|
||||
|
||||
func init() {
|
||||
SubscriptionReceiverManager = &SSubscriptionReceiverManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscriptionReceiver{},
|
||||
"subscriptionreceiver_tbl",
|
||||
"subscriptionreceiver",
|
||||
"subscriptionreceivers",
|
||||
),
|
||||
}
|
||||
SubscriptionReceiverManager.SetVirtualObject(ReceiverNotificationManager)
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriptionReceiver struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
SubscriptionID string `width:"128" charset:"ascii" nullable:"fase" index:"true"`
|
||||
// role id or receiver id or other and the value type is determined by the ReceiverType
|
||||
Receiver string `width:"128" charset:"ascii" nullable:"false"`
|
||||
ReceiverType string `width:"16" charset:"ascii" nullable:"false" index:"true"`
|
||||
RoleScope string `width:"8" charset:"ascii" nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
ReceiverRole = "role"
|
||||
ReceiverNormal = "normal"
|
||||
ReceiverFeishuRobot = notify.FEISHU_ROBOT
|
||||
ReceiverDingtalkRobot = notify.DINGTALK_ROBOT
|
||||
ReceiverWorkwxRobot = notify.WORKWX_ROBOT
|
||||
ReceiverWeebhook = "webhook"
|
||||
|
||||
ScopeSystem = "system"
|
||||
ScopeDomain = "domain"
|
||||
ScopeProject = "project"
|
||||
)
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) robot(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ReceiverFeishuRobot, ReceiverDingtalkRobot, ReceiverWorkwxRobot)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) webhook(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ssid, ReceiverWeebhook)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findSingleReceiver(ssid string, receiverTypes ...string) (string, error) {
|
||||
srs, err := srm.findReceivers(ssid, receiverTypes...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return "", errors.Error("multi receiver")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
return "", errors.ErrNotFound
|
||||
}
|
||||
return srs[0].ReceiverType, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findReceivers(ssid string, receiverTypes ...string) ([]SSubscriptionReceiver, error) {
|
||||
if len(receiverTypes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
q := srm.Query().Equals("subscription_id", ssid)
|
||||
if len(receiverTypes) == 1 {
|
||||
q = q.Equals("receiver_type", receiverTypes[0])
|
||||
} else {
|
||||
q = q.In("receiver_type", receiverTypes)
|
||||
}
|
||||
srs := make([]SSubscriptionReceiver, 0)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
// TODO: Use cache to increase speed
|
||||
func (srm *SSubscriptionReceiverManager) getReceivers(ctx context.Context, ssid string, projectDomainId string, projectId string) ([]string, error) {
|
||||
srs, err := srm.findReceivers(ssid, ReceiverRole, ReceiverNormal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers := make([]string, 0, len(srs))
|
||||
roleMap := make(map[string][]string, 3)
|
||||
receivermap := make(map[string]*[]string, 3)
|
||||
for _, sr := range srs {
|
||||
if sr.ReceiverType == ReceiverNormal {
|
||||
receivers = append(receivers, sr.Receiver)
|
||||
} else if sr.ReceiverType == ReceiverRole {
|
||||
roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Receiver)
|
||||
receivermap[sr.RoleScope] = &[]string{}
|
||||
}
|
||||
}
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
for scope, roles := range roleMap {
|
||||
receivers := receivermap[scope]
|
||||
errgo.Go(func() error {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("roles", jsonutils.NewStringArray(roles))
|
||||
query.Set("effective", jsonutils.JSONTrue)
|
||||
switch scope {
|
||||
case ScopeSystem:
|
||||
case ScopeDomain:
|
||||
if len(projectDomainId) == 0 {
|
||||
return fmt.Errorf("need projectDomainId")
|
||||
}
|
||||
query.Set("project_domain_id", jsonutils.NewString(projectDomainId))
|
||||
case ScopeProject:
|
||||
if len(projectId) == 0 {
|
||||
return fmt.Errorf("need projectId")
|
||||
}
|
||||
query.Add(jsonutils.NewString(projectId), "scope", "project", "id")
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, "", "")
|
||||
log.Debugf("query for role-assignments: %s", query.String())
|
||||
listRet, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to list RoleAssignments")
|
||||
}
|
||||
log.Debugf("return value for role-assignments: %s", jsonutils.Marshal(listRet))
|
||||
for i := range listRet.Data {
|
||||
ras := listRet.Data[i]
|
||||
user, err := ras.Get("user")
|
||||
if err == nil {
|
||||
id, err := user.GetString("id")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
|
||||
}
|
||||
*receivers = append(*receivers, id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err = errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, res := range receivermap {
|
||||
receivers = append(receivers, *res...)
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) create(ctx context.Context, ssid, receiver, receiverType, roleScope string) (*SSubscriptionReceiver, error) {
|
||||
sr := &SSubscriptionReceiver{
|
||||
SubscriptionID: ssid,
|
||||
Receiver: receiver,
|
||||
ReceiverType: receiverType,
|
||||
RoleScope: roleScope,
|
||||
}
|
||||
err := srm.TableSpec().Insert(ctx, sr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to insert")
|
||||
}
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (sr *SSubscriptionReceiver) receivingRole() notify.ReceivingRole {
|
||||
return notify.ReceivingRole{
|
||||
Role: sr.Receiver,
|
||||
Scope: sr.RoleScope,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
// 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"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"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) {
|
||||
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 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"`
|
||||
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 *STopicManager) InitializeData() error {
|
||||
initSNames := sets.NewString(
|
||||
DefaultResourceCreateDelete,
|
||||
DefaultResourceChangeConfig,
|
||||
DefaultResourceUpdate,
|
||||
DefaultResourceReleaseDue1Day,
|
||||
DefaultResourceReleaseDue3Day,
|
||||
DefaultScheduledTaskExecute,
|
||||
DefaultScalingPolicyExecute,
|
||||
DefaultSnapshotPolicyExecute,
|
||||
)
|
||||
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,
|
||||
)
|
||||
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.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
|
||||
}
|
||||
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())
|
||||
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 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 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.TOPIC_RESOURCE_SERVER,
|
||||
notify.TOPIC_RESOURCE_SCALINGGROUP,
|
||||
notify.TOPIC_RESOURCE_SCALINGPOLICY,
|
||||
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_SECGROUP,
|
||||
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_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]
|
||||
}
|
||||
@@ -39,7 +39,6 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.SubContactManager,
|
||||
db.SharedResourceManager,
|
||||
models.VerificationManager,
|
||||
models.SubscriptionReceiverManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
@@ -51,8 +50,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)
|
||||
|
||||
Reference in New Issue
Block a user