Merge pull request #17702 from gouqi11/fixTopicAdvanceForRelease

fix(notify): add update func for topic
This commit is contained in:
Zexi Li
2023-08-14 11:30:26 +08:00
committed by GitHub
10 changed files with 235 additions and 66 deletions
-3
View File
@@ -21,9 +21,6 @@ import (
)
func init() {
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))
+28
View File
@@ -0,0 +1,28 @@
// 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/cmd/climc/shell"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
options "yunion.io/x/onecloud/pkg/mcclient/options/notify"
)
func init() {
cmd := shell.NewResourceCmd(&modules.NotifyTopic).WithKeyword("notify-topic")
cmd.List(new(options.TopicListOptions))
cmd.Update(new(options.TopicUpdateOptions))
cmd.Show(new(options.TopicOptions))
}
+5 -5
View File
@@ -101,18 +101,18 @@ const (
PWD_EXPIRE_SOON_TITLE_EN = `{{- $d := .resource_details -}}
{{ $d.account }}:Your password is valid and will expire soon`
PWD_EXPIRE_SOON_CONTENT_CN = `{{- $d := .resource_details -}}
{{ $d.account }}:您的密码有效期将过,请及时登录平台更新密码。`
{{ $d.account }}:您的密码将在{{ .advance_days }} 天后失效,请及时登录平台更新密码。`
PWD_EXPIRE_SOON_CONTENT_EN = `{{- $d := .resource_details -}}
{{ $d.account }}:Your password is valid and will expire soon. Please log in to the platform in time to update your password.`
{{ $d.account }}:Your password will expire in {{ .advance_days }} days. Please log in to the platform in time to update your password.`
)
// 资源即将到期通知
const (
EXPIRED_RELEASE_TITLE_CN = `{{- $d := .resource_details -}}
{{ $d.project }}项目的
{{ .resource_type_display }}{{ $d.name }}到期前{{ .advance_days }}天通知`
{{ .resource_type_display }}{{ $d.name }}即将到期`
EXPIRED_RELEASE_TITLE_EN = `{{- $d := .resource_details -}}
{{ .advance_days }} days notice before {{ .resource_type }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} expiration`
{{ .resource_type }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} expiration`
EXPIRED_RELEASE_CONTENT_CN = `{{- $d := .resource_details -}}
您在{{ $d.project }}项目的
{{- if $d.brand -}}
@@ -126,7 +126,7 @@ const (
{{- if $d.public_dns -}}
,外网地址为{{ $d.public_dns }}:{{ $d.public_connect_port }}的
{{- end -}}
{{ .resource_type_display }}{{ $d.name }}还有{{ .advance_days }}天就要到期释放,{{ if $d.auto_renew }}到期已开启自动续费,{{ end }}如有其它变更,请尽快前往控制台处理`
{{ .resource_type_display }}{{ $d.name }}将在{{ .advance_days }}天后到期释放,{{ if $d.auto_renew }}到期已开启自动续费,{{ end }}如有其它变更,请尽快前往控制台处理`
EXPIRED_RELEASE_CONTENT_EN = `{{- $d := .resource_details -}}
Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type }} {{ $d.name }} {{ if $d.public_dns -}} with external address {{ $d.public_dns }}:{{ $d.public_connect_port }} {{ end -}} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} will expire and be released in {{ .advance_days }} days. {{ if $d.auto_renew }}It has turned on automatic renewal. {{ end }}If there are other changes, please go to the console as soon as possible.`
)
+38 -1
View File
@@ -16,6 +16,41 @@ package notify
import "yunion.io/x/onecloud/pkg/apis"
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"
DefaultResourceReleaseDue30Day = "resource release due 30 day"
DefaultResourceRelease = "resource release"
DefaultScheduledTaskExecute = "scheduled task execute"
DefaultScalingPolicyExecute = "scaling policy execute"
DefaultSnapshotPolicyExecute = "snapshot policy execute"
DefaultResourceOperationFailed = "resource operation failed"
DefaultResourceSync = "resource sync"
DefaultSystemExceptionEvent = "system exception event"
DefaultChecksumTestFailed = "checksum test failed"
DefaultUserLock = "user lock"
DefaultActionLogExceedCount = "action log exceed count"
DefaultSyncAccountStatus = "cloud account sync status"
DefaultPasswordExpireDue1Day = "password expire due 1 day"
DefaultPasswordExpireDue7Day = "password expire due 7 day"
DefaultPasswordExpire = "password expire"
DefaultNetOutOfSync = "net out of sync"
DefaultMysqlOutOfSync = "mysql out of sync"
DefaultServiceAbnormal = "service abnormal"
DefaultServerPanicked = "server panicked"
)
type TopicUpdateInput struct {
TitleCn string
TitleEn string
ContentCn string
ContentEn string
AdvanceDays []int `json:"advance_days"`
}
type TopicListInput struct {
apis.StandaloneResourceListInput
apis.EnabledResourceBaseListInput
@@ -27,7 +62,8 @@ type TopicDetails struct {
// description: resources managed
// example: ["server", "eip", "disk"]
Resources []string `json:"resource_types"`
Resources []string `json:"resource_types"`
AdvanceDays []int `json:"advance_days"`
}
type PerformEnableInput struct {
@@ -37,3 +73,4 @@ type PerformDisableInput struct {
}
type STopicGroupKeys []string
type TopicAdvanceDays []int
+34 -6
View File
@@ -16,6 +16,7 @@ package models
import (
"context"
"fmt"
"reflect"
"time"
@@ -27,11 +28,14 @@ import (
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/billing"
notifyapi "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
@@ -240,8 +244,8 @@ type IBillingModel interface {
}
func fetchExpiredModels(manager db.IModelManager, advanceDay int) ([]IBillingModel, error) {
upLimit := time.Now().AddDate(0, 0, advanceDay)
downLimit := time.Now().AddDate(0, 0, advanceDay-1)
upLimit := time.Now().AddDate(0, 0, advanceDay+1)
downLimit := time.Now().AddDate(0, 0, advanceDay)
v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0)
q := manager.Query().LE("expired_at", upLimit).GE("expired_at", downLimit)
@@ -293,7 +297,27 @@ func CheckBillingResourceExpireAt(ctx context.Context, userCred mcclient.TokenCr
DBInstanceManager,
ElasticcacheManager,
}
for _, advanceDay := range options.Options.ResourceExpiredNotifyDays {
s := auth.GetAdminSession(ctx, options.Options.Region)
resp, err := notify.NotifyTopic.List(s, jsonutils.Marshal(map[string]interface{}{
"filter": fmt.Sprintf("name.equals('%s')", notifyapi.DefaultResourceRelease),
"scope": "system",
}))
if err != nil {
log.Errorln(errors.Wrap(err, "list topics"))
return
}
topics := []notifyapi.TopicDetails{}
err = jsonutils.Update(&topics, resp.Data)
if err != nil {
log.Errorln(errors.Wrap(err, "update topic"))
return
}
if len(topics) != 1 {
log.Errorln(errors.Wrapf(errors.ErrNotSupported, "len topics :%d", len(topics)))
return
}
for _, advanceDay := range topics[0].AdvanceDays {
for _, manager := range billingResourceManagers {
expiredModels, err := manager.GetExpiredModels(advanceDay)
if err != nil {
@@ -332,10 +356,14 @@ func CheckBillingResourceExpireAt(ctx context.Context, userCred mcclient.TokenCr
if check.LastCheck.AddDate(0, 0, advanceDay).After(em.GetExpiredAt()) {
continue
}
detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
details.Set("advance_days", jsonutils.NewInt(int64(advanceDay)))
}
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
Obj: em,
Action: notifyclient.ActionExpiredRelease,
AdvanceDays: advanceDay,
ObjDetailsDecorator: detailsDecro,
Obj: em,
Action: notifyclient.ActionExpiredRelease,
AdvanceDays: advanceDay,
})
_, err := db.Update(check, func() error {
check.LastCheck = time.Now()
+31 -12
View File
@@ -19,6 +19,7 @@ import (
"crypto/sha256"
"database/sql"
"encoding/hex"
"fmt"
"time"
"yunion.io/x/jsonutils"
@@ -26,13 +27,14 @@ import (
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/apis/notify"
notifyapi "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/keystone/options"
o "yunion.io/x/onecloud/pkg/keystone/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
"yunion.io/x/onecloud/pkg/util/seclib2"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
@@ -120,11 +122,11 @@ func (manager *SPasswordManager) fetchByLocaluserId(localUserId int) ([]SPasswor
}
func validatePasswordComplexity(password string) error {
if o.Options.PasswordMinimalLength > 0 && len(password) < o.Options.PasswordMinimalLength {
if options.Options.PasswordMinimalLength > 0 && len(password) < o.Options.PasswordMinimalLength {
return errors.Wrap(httperrors.ErrWeakPassword, "too simple password")
}
if o.Options.PasswordCharComplexity > 0 {
complexity := o.Options.PasswordCharComplexity
if options.Options.PasswordCharComplexity > 0 {
complexity := options.Options.PasswordCharComplexity
if complexity > 4 {
complexity = 4
}
@@ -140,13 +142,13 @@ func (manager *SPasswordManager) validatePassword(localUserId int, password stri
if err != nil {
return errors.Wrap(err, "validatePasswordComplexity")
}
if !skipHistoryCheck && o.Options.PasswordUniqueHistoryCheck > 0 {
if !skipHistoryCheck && options.Options.PasswordUniqueHistoryCheck > 0 {
shaPass := shaPassword(password)
histPasses, err := manager.fetchByLocaluserId(localUserId)
if err != nil {
return errors.Wrap(err, "manager.fetchByLocaluserId")
}
for i := 0; i < len(histPasses) && i < o.Options.PasswordUniqueHistoryCheck; i += 1 {
for i := 0; i < len(histPasses) && i < options.Options.PasswordUniqueHistoryCheck; i += 1 {
if histPasses[i].Password == shaPass {
return errors.Error("repeated password")
}
@@ -168,8 +170,8 @@ func (manager *SPasswordManager) savePassword(localUserId int, password string,
rec.SetModelManager(PasswordManager, rec)
now := time.Now()
rec.CreatedAtInt = now.UnixNano() / 1000
if o.Options.PasswordExpirationSeconds > 0 && !isSystemAccount {
rec.ExpiresAt = now.Add(time.Second * time.Duration(o.Options.PasswordExpirationSeconds))
if options.Options.PasswordExpirationSeconds > 0 && !isSystemAccount {
rec.ExpiresAt = now.Add(time.Second * time.Duration(options.Options.PasswordExpirationSeconds))
rec.ExpiresAtInt = rec.ExpiresAt.UnixNano() / 1000
}
err = manager.TableSpec().Insert(context.TODO(), rec)
@@ -234,19 +236,36 @@ func (pwd *SPassword) NeedSendNotify(ctx context.Context, userCred mcclient.Toke
sub := expireTime.Sub(nowTime)
subDay := int(sub.Hours() / 24)
if utils.IsInArray(subDay, options.Options.PwdExpiredNotifyDays) {
s := GetDefaultClientSession(ctx, userCred, options.Options.Region)
resp, err := notify.NotifyTopic.List(s, jsonutils.Marshal(map[string]interface{}{
"filter": fmt.Sprintf("name.equals('%s')", notifyapi.DefaultPasswordExpire),
"scope": "system",
}))
if err != nil {
return errors.Wrap(err, "list topics")
}
topics := []notifyapi.TopicDetails{}
err = jsonutils.Update(&topics, resp.Data)
if err != nil {
return errors.Wrap(err, "update topic")
}
if len(topics) != 1 {
return errors.Wrapf(errors.ErrNotSupported, "len topics :%d", len(topics))
}
if utils.IsInArray(subDay, topics[0].AdvanceDays) {
localUser, err := LocalUserManager.fetchLocalUser("", "", pwd.LocalUserId)
if err != nil {
return errors.Wrap(err, "fetchLocalUser error:")
}
pwd.EventNotify(ctx, userCred, notify.ActionPasswordExpireSoon, localUser.Name, subDay)
pwd.EventNotify(ctx, userCred, notifyapi.ActionPasswordExpireSoon, localUser.Name, subDay)
}
return nil
}
// 密码即将失效消息通知
func (pwd *SPassword) EventNotify(ctx context.Context, userCred mcclient.TokenCredential, action notify.SAction, userName string, advanceDays int) {
resourceType := notify.TOPIC_RESOURCE_USER
func (pwd *SPassword) EventNotify(ctx context.Context, userCred mcclient.TokenCredential, action notifyapi.SAction, userName string, advanceDays int) {
resourceType := notifyapi.TOPIC_RESOURCE_USER
detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
details.Set("account", jsonutils.NewString(userName))
@@ -33,26 +33,6 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type TopicListOptions struct {
options.BaseListOptions
}
func (opts *TopicListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}
type TopicOptions struct {
ID string `help:"Id or Name of topic"`
}
func (so *TopicOptions) GetId() string {
return so.ID
}
func (so *TopicOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type SubscriberCreateOptions struct {
TopicId string `positional:"true"`
ResourceScope string `positional:"true" choices:"system|domain|project"`
+69
View File
@@ -0,0 +1,69 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2019 Yunion
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notify
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type TopicListOptions struct {
options.BaseListOptions
}
func (opts *TopicListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}
type TopicOptions struct {
ID string `help:"Id or Name of topic"`
}
func (so *TopicOptions) GetId() string {
return so.ID
}
func (so *TopicOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type TopicUpdateOptions struct {
ID string
AdvanceDays []int
}
func (opts *TopicUpdateOptions) Params() (jsonutils.JSONObject, error) {
d := jsonutils.NewDict()
d.Set("advance_days", jsonutils.Marshal(opts.AdvanceDays))
return d, nil
}
func (so *TopicUpdateOptions) GetId() string {
return so.ID
}
+8
View File
@@ -69,6 +69,14 @@ func (e *SEventManager) CreateEvent(ctx context.Context, event, topicId, message
}
func (e *SEventManager) GetEvent(id string) (*SEvent, error) {
if consts.OpsLogWithClickhouse {
eventModel, err := e.FetchById(id)
if err != nil {
return nil, errors.Wrap(err, "fetch event by id")
}
event := eventModel.(*SEvent)
return event, nil
}
event := &SEvent{}
event.SetModelManager(EventManager, event)
eventId, err := strconv.Atoi(id)
+22 -19
View File
@@ -73,16 +73,17 @@ func init() {
type STopic struct {
db.SEnabledStatusStandaloneResourceBase
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
Resources uint64 `nullable:"false"`
Actions uint32 `nullable:"false"`
Results tristate.TriState `default:"true"`
TitleCn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
TitleEn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
ContentCn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
ContentEn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
GroupKeys *api.STopicGroupKeys `nullable:"true"`
GroupTimes uint32 `nullable:"true"`
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
Resources uint64 `nullable:"false"`
Actions uint32 `nullable:"false"`
Results tristate.TriState `default:"true"`
TitleCn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
TitleEn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
ContentCn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
ContentEn string `length:"medium" nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
GroupKeys *api.STopicGroupKeys `nullable:"true"`
GroupTimes uint32 `nullable:"true"`
AdvanceDays []int `nullable:"true" charset:"utf8" list:"user" update:"user" create:"optional"`
WebconsoleDisable tristate.TriState
}
@@ -462,6 +463,7 @@ func (sm *STopicManager) InitializeData() error {
t.addAction(
notify.ActionPasswordExpireSoon,
)
t.AdvanceDays = []int{1, 7}
t.Type = notify.TOPIC_TYPE_SECURITY
t.Results = tristate.True
t.ContentCn = api.PWD_EXPIRE_SOON_CONTENT_CN
@@ -479,6 +481,7 @@ func (sm *STopicManager) InitializeData() error {
)
t.addAction(notify.ActionExpiredRelease)
t.Type = notify.TOPIC_TYPE_RESOURCE
t.AdvanceDays = []int{1, 7, 30}
t.Results = tristate.True
t.ContentCn = api.EXPIRED_RELEASE_CONTENT_CN
t.ContentEn = api.EXPIRED_RELEASE_CONTENT_EN
@@ -508,9 +511,6 @@ func (sm *STopicManager) InitializeData() error {
}
_, err := db.Update(topic, func() error {
if t.Type == "" {
log.Infoln("this is err Name:", t.Name)
}
topic.Name = t.Name
topic.Resources = t.Resources
topic.Actions = t.Actions
@@ -519,25 +519,28 @@ func (sm *STopicManager) InitializeData() error {
topic.WebconsoleDisable = t.WebconsoleDisable
topic.GroupKeys = t.GroupKeys
topic.GroupTimes = t.GroupTimes
if len(topic.ContentCn) == 0 {
if len(topic.AdvanceDays) == 0 {
topic.AdvanceDays = t.AdvanceDays
}
if len(topic.ContentCn) == 0 || topic.Name == DefaultPasswordExpire || topic.Name == DefaultResourceRelease {
if len(t.ContentCn) == 0 {
t.ContentCn = api.COMMON_CONTENT_CN
}
topic.ContentCn = t.ContentCn
}
if len(topic.ContentEn) == 0 {
if len(topic.ContentEn) == 0 || topic.Name == DefaultPasswordExpire || topic.Name == DefaultResourceRelease {
if len(t.ContentEn) == 0 {
t.ContentEn = api.COMMON_CONTENT_EN
}
topic.ContentEn = t.ContentEn
}
if len(topic.TitleCn) == 0 {
if len(topic.TitleCn) == 0 || topic.Name == DefaultPasswordExpire || topic.Name == DefaultResourceRelease {
if len(t.TitleCn) == 0 {
t.TitleCn = api.COMMON_TITLE_CN
}
topic.TitleCn = t.TitleCn
}
if len(topic.TitleEn) == 0 {
if len(topic.TitleEn) == 0 || topic.Name == DefaultPasswordExpire || topic.Name == DefaultResourceRelease {
if len(t.TitleEn) == 0 {
t.TitleEn = api.COMMON_TITLE_EN
}
@@ -604,8 +607,8 @@ func (sm *STopicManager) ValidateCreateData(ctx context.Context, userCred mcclie
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) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.TopicUpdateInput) (api.TopicUpdateInput, error) {
return input, nil
}
func (ss *STopic) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {