mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-21 00:24:07 +08:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6254b64d5d | ||
|
|
43f8ac0027 | ||
|
|
36bd1baafe | ||
|
|
98ca80e630 | ||
|
|
a2613e6bce | ||
|
|
a8b14bb739 | ||
|
|
4850c4f8e2 | ||
|
|
64dec4254f | ||
|
|
ada90754f9 | ||
|
|
fb3456c42d | ||
|
|
cefbeb7a14 | ||
|
|
1fc01ebe94 | ||
|
|
ff0606f166 | ||
|
|
83dd534241 | ||
|
|
a7771885cf | ||
|
|
88fa743e4e | ||
|
|
0e917bb275 | ||
|
|
cd76ce27c3 | ||
|
|
114de75e7e | ||
|
|
047509ebb8 | ||
|
|
71e79e5a7b | ||
|
|
4c62508b01 | ||
|
|
e5b637906c | ||
|
|
dd7d34402f | ||
|
|
157bfb696b | ||
|
|
464e0450f9 | ||
|
|
a4b5562bd0 | ||
|
|
f0354d0c9b | ||
|
|
c3c3c00759 | ||
|
|
83adeaae4e | ||
|
|
d7f05e983f | ||
|
|
5d075bbaaf | ||
|
|
86fda0f7d0 | ||
|
|
53c8c16052 | ||
|
|
46ba9cd424 | ||
|
|
ee59f2a410 | ||
|
|
5cfbb416b3 | ||
|
|
a711f832e6 | ||
|
|
f31216be0e | ||
|
|
bc0106902a | ||
|
|
2dbfc80fac | ||
|
|
4fa95bf608 | ||
|
|
af0813cd09 | ||
|
|
3b5602e1ea | ||
|
|
e4f7d3233c | ||
|
|
287f98ac86 | ||
|
|
bb44f1cdfb | ||
|
|
644ab6f925 | ||
|
|
e775e798aa | ||
|
|
331d9d2a67 | ||
|
|
d5ed96a651 | ||
|
|
4ce85f310b | ||
|
|
3f6e669324 | ||
|
|
3f4b2163f3 | ||
|
|
78b6f85662 | ||
|
|
3124113e2f | ||
|
|
38b34239cd | ||
|
|
370b35852c | ||
|
|
f7c8bbc00b | ||
|
|
f64c6c54da | ||
|
|
c4db4bc43e | ||
|
|
1afe6ef41b |
@@ -47,7 +47,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-bottom: 10px;">您正在验证邮箱,请在验证码输入框中输入:{{.code}},已完成验证。</td>
|
||||
<td style="padding-bottom: 10px;">您正在验证邮箱,请在验证码输入框中输入:{{.code}},以完成验证。</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
@@ -0,0 +1 @@
|
||||
{{.os_type}} image {{.name}} upload completed
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<table style="width: 650px; margin-bottom: 20px;" border="0" cellpadding="0" cellspacing="0" align="center">
|
||||
<tr style="height: 50px; background: #333; overflow: hidden;">
|
||||
<td>
|
||||
<table style="margin-left: 20px;">
|
||||
<tr>
|
||||
<td>
|
||||
<img src="data:{{.login_logo_format}};base64,{{.login_logo}}" alt="" style="color: #fff; height: 32px; vertical-align: middle;">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table style="float: right;">
|
||||
<tr>
|
||||
<td style="padding-left: 20px; padding-right: 20px;">
|
||||
<a href="" style="text-decoration: none; color: #20A0FF; font-size: 12px; display: none;">Product</a>
|
||||
</td>
|
||||
<td style="padding-left: 20px; padding-right: 20px;">
|
||||
<a href="" style="text-decoration: none; color: #20A0FF; font-size: 12px; display: none;">Solution</a>
|
||||
</td>
|
||||
<td style="padding-left: 20px; padding-right: 20px;">
|
||||
<a href="" style="text-decoration: none; color: #20A0FF; font-size: 12px; display: none;">Service</a>
|
||||
</td>
|
||||
<td style="padding-left: 20px; padding-right: 20px;">
|
||||
<a href="" style="text-decoration: none; color: #20A0FF; font-size: 12px; display: none;">Help and Documentation</a>
|
||||
</td>
|
||||
<td style="padding-left: 20px; padding-right: 20px;">
|
||||
<a href="" style="text-decoration: none; color: #20A0FF; font-size: 12px; display: none;">About</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 100%;" colspan="2">
|
||||
<table style="padding: 20px 10px; width: 100%;">
|
||||
<tr>
|
||||
<td style="padding-bottom: 13px;">
|
||||
Dear {{.name}}:
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-bottom: 13px;">You are verifying your email, please enter the following code on the email verification page:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-bottom: 10px; font-size: large">{{.code}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="width: 96%;">
|
||||
<td colspan="2" style="border-top: 1px dashed #ccc; color: #333; font-size: 12px; padding-bottom: 10px; font-weight: 100;">If you are not operating by yourself, please log in to the platform in time and change your password to ensure the security of your account.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="background: #333; text-align: right; padding-right: 20px; font-size: 12px; color: #fff; height: 50px;">Copyrights © {{.copyright}}. All rights reserved.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
Image {{.name}} upload completed
|
||||
@@ -0,0 +1 @@
|
||||
{{.copyright}} Verify
|
||||
@@ -25,4 +25,6 @@ func init() {
|
||||
cmd.List(new(options.SuggestSysAlertListOptions))
|
||||
cmd.Show(new(options.SSuggestAlertShowOptions))
|
||||
cmd.Perform("ignore", new(options.SuggestAlertIgnoreOptions))
|
||||
cmd_ := shell.NewResourceCmd(monitor.SuggestSysAlertCostManager)
|
||||
cmd_.Get("", new(options.SuggestAlertCostOptions))
|
||||
}
|
||||
|
||||
7060
locales/locales.go
7060
locales/locales.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -501,34 +501,34 @@ func (h *AuthHandlers) doLogin(ctx context.Context, w http.ResponseWriter, req *
|
||||
if body.Contains("tenantId") { // switch project
|
||||
token, authToken, err = doTenantLogin(ctx, req, body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "doTenantLogin")
|
||||
return err
|
||||
}
|
||||
userInfo, err = fetchUserInfoFromToken(ctx, req, token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetchUserInfoFromToken")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// user/password authenticate
|
||||
// SSO authentication
|
||||
token, err = h.doCredentialLogin(ctx, req, body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "doCredentialLogin")
|
||||
return err
|
||||
}
|
||||
userInfo, err = fetchUserInfoFromToken(ctx, req, token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetchUserInfoFromToken")
|
||||
return err
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
isTotpInit, err := isUserTotpCredInitialed(s, token.GetUserId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "isUserTotpCredInitialed")
|
||||
return err
|
||||
}
|
||||
isIdpLogin := body.Contains("idp_driver")
|
||||
authToken = clientman.NewAuthToken(token.GetTokenString(), isUserEnableTotp(userInfo), isTotpInit, isIdpLogin)
|
||||
}
|
||||
|
||||
if !isUserAllowWebconsole(userInfo) {
|
||||
return errors.Wrap(httperrors.ErrForbidden, "user forbidden login from web")
|
||||
return httperrors.NewForbiddenError("user forbidden login from web")
|
||||
}
|
||||
|
||||
saveAuthCookie(w, authToken, token)
|
||||
@@ -996,7 +996,7 @@ func (h *AuthHandlers) getPermissionDetails(ctx context.Context, w http.Response
|
||||
|
||||
_, query, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
var name string
|
||||
@@ -1030,7 +1030,7 @@ func (h *AuthHandlers) doCreatePolicies(ctx context.Context, w http.ResponseWrit
|
||||
// }
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
s := auth.GetSession(ctx, t, FetchRegion(req), "")
|
||||
@@ -1114,7 +1114,7 @@ func (h *AuthHandlers) resetUserPassword(ctx context.Context, w http.ResponseWri
|
||||
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1150,7 +1150,7 @@ func (h *AuthHandlers) resetUserPassword(ctx context.Context, w http.ResponseWri
|
||||
return
|
||||
}
|
||||
}
|
||||
httperrors.InputParameterError(ctx, w, "密码错误")
|
||||
httperrors.InputParameterError(ctx, w, "wrong password")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ func validateTotpRecoverySecrets(s *mcclient.ClientSession, uid string, question
|
||||
func initTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
t, authToken, err := fetchAuthInfo(ctx, req)
|
||||
if err != nil {
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail %s", err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
|
||||
return
|
||||
}
|
||||
if authToken.IsTotpInitialized() {
|
||||
@@ -232,14 +232,14 @@ func initTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Reque
|
||||
func validatePasscodeHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
t, authToken, err := fetchAuthInfo(ctx, req)
|
||||
if err != nil {
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail %s", err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -271,14 +271,14 @@ func validatePasscodeHandler(ctx context.Context, w http.ResponseWriter, req *ht
|
||||
func resetTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
t, _, err := fetchAuthInfo(ctx, req)
|
||||
if err != nil {
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail %s", err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ func resetTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Requ
|
||||
func listTotpRecoveryQuestions(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
t, _, err := fetchAuthInfo(ctx, req)
|
||||
if err != nil {
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail %s", err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -335,14 +335,14 @@ func listTotpRecoveryQuestions(ctx context.Context, w http.ResponseWriter, req *
|
||||
func resetTotpRecoveryQuestions(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
t, _, err := fetchAuthInfo(ctx, req)
|
||||
if err != nil {
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail %s", err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, req)
|
||||
if body == nil {
|
||||
httperrors.InvalidInputError(ctx, w, "body is empty")
|
||||
httperrors.InvalidInputError(ctx, w, "request body is empty")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ func (h *AuthHandlers) handleSsoLogin(ctx context.Context, w http.ResponseWriter
|
||||
case "POST":
|
||||
formData, err := appsrv.Fetch(req)
|
||||
if err != nil {
|
||||
httperrors.InputParameterError(ctx, w, "fetch formdata error: %s", err)
|
||||
httperrors.InputParameterError(ctx, w, "fetch form data error: %s", err)
|
||||
}
|
||||
body, err = jsonutils.ParseQueryString(string(formData))
|
||||
if err != nil {
|
||||
|
||||
@@ -42,6 +42,8 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
const contentTypeSpreadsheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
|
||||
const (
|
||||
HOST_MAC = "*MAC地址"
|
||||
HOST_NAME = "*名称"
|
||||
@@ -154,8 +156,8 @@ func (mh *MiscHandler) DoBatchHostRegister(ctx context.Context, w http.ResponseW
|
||||
|
||||
fileHeader := hostfiles[0].Header
|
||||
contentType := fileHeader.Get("Content-Type")
|
||||
if contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
|
||||
e := httperrors.NewInputParameterError("Wrong content type %s, required application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", contentType)
|
||||
if contentType != contentTypeSpreadsheet {
|
||||
e := httperrors.NewInputParameterError("Wrong content type %s, want %s", contentType, contentTypeSpreadsheet)
|
||||
httperrors.JsonClientError(ctx, w, e)
|
||||
return
|
||||
}
|
||||
@@ -199,7 +201,7 @@ func (mh *MiscHandler) DoBatchHostRegister(ctx context.Context, w http.ResponseW
|
||||
}
|
||||
|
||||
if !titlesOk {
|
||||
httperrors.InputParameterError(ctx, w, "template file is invalid.please check.")
|
||||
httperrors.InputParameterError(ctx, w, "template file is invalid. please check.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -302,8 +304,8 @@ func (mh *MiscHandler) DoBatchUserRegister(ctx context.Context, w http.ResponseW
|
||||
|
||||
fileHeader := userfiles[0].Header
|
||||
contentType := fileHeader.Get("Content-Type")
|
||||
if contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
|
||||
e := httperrors.NewInputParameterError("Wrong content type %s, required application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", contentType)
|
||||
if contentType != contentTypeSpreadsheet {
|
||||
e := httperrors.NewInputParameterError("Wrong content type %s, want %s", contentType, contentTypeSpreadsheet)
|
||||
httperrors.JsonClientError(ctx, w, e)
|
||||
return
|
||||
}
|
||||
@@ -328,7 +330,7 @@ func (mh *MiscHandler) DoBatchUserRegister(ctx context.Context, w http.ResponseW
|
||||
// skipped header row
|
||||
rows := xlsx.GetRows("users")
|
||||
if len(rows) <= 1 {
|
||||
e := httperrors.NewInputParameterError("empty file")
|
||||
e := httperrors.NewInputParameterError("empty file content")
|
||||
httperrors.JsonClientError(ctx, w, e)
|
||||
return
|
||||
} else if len(rows) > BATCH_USER_REGISTER_QUANTITY_LIMITATION {
|
||||
@@ -431,7 +433,7 @@ func (mh *MiscHandler) getDownloadsHandler(ctx context.Context, w http.ResponseW
|
||||
params := appctx.AppContextParams(ctx)
|
||||
template, ok := params["<template_id>"]
|
||||
if !ok || len(template) == 0 {
|
||||
httperrors.InvalidInputError(ctx, w, "not found")
|
||||
httperrors.InvalidInputError(ctx, w, "template_id")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -142,11 +142,11 @@ func fetchIdList(ctx context.Context, query jsonutils.JSONObject, w http.Respons
|
||||
if e == nil && len(idlist) > 0 {
|
||||
queryDict := query.(*jsonutils.JSONDict)
|
||||
queryDict.Remove("id")
|
||||
log.Debugf("Get idlist: %s", idlist)
|
||||
log.Debugf("Get id list: %s", idlist)
|
||||
return jsonutils.JSONArray2StringArray(idlist)
|
||||
} else {
|
||||
log.Debugf("Cannot find idlist in query: %s", query)
|
||||
httperrors.InvalidInputError(ctx, w, "No idlist found")
|
||||
log.Debugf("Cannot find id list in query: %s", query)
|
||||
httperrors.InvalidInputError(ctx, w, "No id list found")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type AlertResourceType string
|
||||
|
||||
const (
|
||||
// AlertResourceTypeNode means onecloud system infrastructure controller or host node
|
||||
AlertResourceTypeNode AlertResourceType = "node"
|
||||
AlertResourceTypeNode AlertResourceType = "host"
|
||||
// AlertResourceTypeCloudaccount means cloudaccount resource
|
||||
AlertResourceTypeCloudaccount AlertResourceType = "cloudaccount"
|
||||
// AlertResourceTypeVM means virtual machine guest resource
|
||||
|
||||
@@ -120,10 +120,11 @@ type CommonAlertDetails struct {
|
||||
}
|
||||
|
||||
type CommonAlertMetricDetails struct {
|
||||
Comparator string `json:"comparator"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
ConditionType string `json:"condition_type"`
|
||||
ThresholdStr string `json:"threshold_str"`
|
||||
Comparator string `json:"comparator"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
WithinRange []float64 `json:"within_range"`
|
||||
ConditionType string `json:"condition_type"`
|
||||
ThresholdStr string `json:"threshold_str"`
|
||||
// metric points'value的运算方式
|
||||
Reduce string `json:"reduce"`
|
||||
DB string `json:"db"`
|
||||
|
||||
@@ -30,8 +30,8 @@ var (
|
||||
MetricUnit = []string{METRIC_UNIT_PERCENT, METRIC_UNIT_BPS, METRIC_UNIT_MBPS, METRIC_UNIT_BYTEPS, "count/s",
|
||||
METRIC_UNIT_COUNT, METRIC_UNIT_MS, METRIC_UNIT_BYTE, METRIC_UNIT_RMB}
|
||||
ResTypeScoreMap = map[string]int{
|
||||
METRIC_RES_TYPE_HOST: 1,
|
||||
METRIC_RES_TYPE_GUEST: 2,
|
||||
METRIC_RES_TYPE_GUEST: 1,
|
||||
METRIC_RES_TYPE_HOST: 2,
|
||||
METRIC_RES_TYPE_OSS: 3,
|
||||
METRIC_RES_TYPE_RDS: 4,
|
||||
METRIC_RES_TYPE_REDIS: 5,
|
||||
|
||||
@@ -62,6 +62,9 @@ const (
|
||||
TEMPLATE_TYPE_CONTENT = "content"
|
||||
TEMPLATE_TYPE_REMOTE = "remote"
|
||||
|
||||
TEMPLATE_LANG_EN = "en"
|
||||
TEMPLATE_LANG_CN = "cn"
|
||||
|
||||
CTYPE_ROBOT_YES = "yes"
|
||||
CTYPE_ROBOT_ONLY = "only"
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
var (
|
||||
cloudeventSystemResources = []string{}
|
||||
cloudeventDomainResources = []string{
|
||||
"cloudevent",
|
||||
"cloudevents",
|
||||
}
|
||||
cloudeventUserResources = []string{}
|
||||
)
|
||||
|
||||
@@ -127,16 +127,6 @@ func (manager *SSamluserManager) ValidateCreateData(ctx context.Context, userCre
|
||||
return input, err
|
||||
}
|
||||
group := _group.(*SCloudgroup)
|
||||
sq := CloudgroupManager.Query("id").Equals("provider", group.Provider).SubQuery()
|
||||
q := manager.Query().Equals("owner_id", input.OwnerId).In("cloudgroup_id", sq)
|
||||
groups := []SCloudgroup{}
|
||||
err = db.FetchModelObjects(CloudgroupManager, q, &groups)
|
||||
if err != nil {
|
||||
return input, httperrors.NewGeneralError(errors.Wrapf(err, "db.FetchModelObjects"))
|
||||
}
|
||||
if len(groups) > 0 {
|
||||
return input, httperrors.NewConflictError("user %s has already in other %s group", input.Name, group.Provider)
|
||||
}
|
||||
_account, err := validators.ValidateModel(userCred, CloudaccountManager, &input.CloudaccountId)
|
||||
if err != nil {
|
||||
return input, err
|
||||
@@ -148,6 +138,17 @@ func (manager *SSamluserManager) ValidateCreateData(ctx context.Context, userCre
|
||||
if account.Provider != group.Provider {
|
||||
return input, httperrors.NewConflictError("account %s and group %s not with same provider", account.Name, group.Name)
|
||||
}
|
||||
|
||||
sq := CloudgroupManager.Query("id").Equals("provider", group.Provider).SubQuery()
|
||||
q := manager.Query().Equals("owner_id", input.OwnerId).Equals("cloudaccount_id", account.Id).In("cloudgroup_id", sq)
|
||||
groups := []SCloudgroup{}
|
||||
err = db.FetchModelObjects(CloudgroupManager, q, &groups)
|
||||
if err != nil {
|
||||
return input, httperrors.NewGeneralError(errors.Wrapf(err, "db.FetchModelObjects"))
|
||||
}
|
||||
if len(groups) > 0 {
|
||||
return input, httperrors.NewConflictError("user %s has already in other %s group", input.Name, group.Provider)
|
||||
}
|
||||
input.Status = api.SAML_USER_STATUS_AVAILABLE
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -1324,6 +1324,13 @@ func (manager *SGuestManager) validateCreateData(
|
||||
}
|
||||
log.Debugf("ROOT DISK: %#v", rootDiskConfig)
|
||||
input.Disks[0] = rootDiskConfig
|
||||
if sku != nil {
|
||||
if len(rootDiskConfig.OsArch) >= 0 && len(sku.CpuArch) >= 0 {
|
||||
if strings.Contains(rootDiskConfig.OsArch, sku.CpuArch) {
|
||||
return nil, httperrors.NewConflictError("root disk image(%s) and sku(%s) architecture mismatch", rootDiskConfig.OsArch, sku.CpuArch)
|
||||
}
|
||||
}
|
||||
}
|
||||
//data.Set("disk.0", jsonutils.Marshal(rootDiskConfig))
|
||||
|
||||
for i := 0; i < len(dataDiskDefs); i += 1 {
|
||||
|
||||
@@ -184,9 +184,9 @@ func (man *SAwsCachedLbbgManager) GetCachedBackendGroups(backendGroupId string)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (man *SAwsCachedLbbgManager) getLoadbalancerBackendgroupsByRegion(regionId string) ([]SAwsCachedLbbg, error) {
|
||||
func (man *SAwsCachedLbbgManager) getLoadbalancerBackendgroupsByRegion(managerId string, regionId string) ([]SAwsCachedLbbg, error) {
|
||||
lbbgs := []SAwsCachedLbbg{}
|
||||
q := man.Query().Equals("cloudregion_id", regionId).IsFalse("pending_deleted")
|
||||
q := man.Query().Equals("cloudregion_id", regionId).Equals("manager_id", managerId).IsFalse("pending_deleted")
|
||||
if err := db.FetchModelObjects(man, q, &lbbgs); err != nil {
|
||||
log.Errorf("failed to get lbbgs for region: %s error: %v", regionId, err)
|
||||
return nil, err
|
||||
@@ -204,7 +204,7 @@ func (man *SAwsCachedLbbgManager) SyncLoadbalancerBackendgroups(ctx context.Cont
|
||||
remoteLbbgs := []cloudprovider.ICloudLoadbalancerBackendGroup{}
|
||||
syncResult := compare.SyncResult{}
|
||||
|
||||
dbLbbgs, err := man.getLoadbalancerBackendgroupsByRegion(region.GetId())
|
||||
dbLbbgs, err := man.getLoadbalancerBackendgroupsByRegion(provider.GetId(), region.GetId())
|
||||
if err != nil {
|
||||
syncResult.Error(err)
|
||||
return nil, nil, syncResult
|
||||
|
||||
@@ -716,6 +716,19 @@ func (lb *SLoadbalancer) getMoreDetails(out api.LoadbalancerDetails) (api.Loadba
|
||||
}
|
||||
|
||||
func (lb *SLoadbalancer) ValidateDeleteCondition(ctx context.Context) error {
|
||||
err := lb.validatePurgeCondition(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lb.DisableDelete.IsTrue() {
|
||||
return httperrors.NewInvalidStatusError("loadbalancer is locked, cannot delete")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lb *SLoadbalancer) validatePurgeCondition(ctx context.Context) error {
|
||||
region := lb.GetRegion()
|
||||
if region != nil {
|
||||
if err := region.GetDriver().ValidateDeleteLoadbalancerCondition(ctx, lb); err != nil {
|
||||
@@ -723,10 +736,6 @@ func (lb *SLoadbalancer) ValidateDeleteCondition(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if lb.DisableDelete.IsTrue() {
|
||||
return httperrors.NewInvalidStatusError("loadbalancer is locked, cannot delete")
|
||||
}
|
||||
|
||||
return lb.SModelBase.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
@@ -790,11 +799,9 @@ func (lb *SLoadbalancer) Delete(ctx context.Context, userCred mcclient.TokenCred
|
||||
|
||||
func (man *SLoadbalancerManager) getLoadbalancersByRegion(region *SCloudregion, provider *SCloudprovider) ([]SLoadbalancer, error) {
|
||||
lbs := []SLoadbalancer{}
|
||||
vpcs := VpcManager.Query().SubQuery()
|
||||
q := man.Query()
|
||||
q = q.Join(vpcs, sqlchemy.Equals(q.Field("vpc_id"), vpcs.Field("id")))
|
||||
q = q.Filter(sqlchemy.Equals(vpcs.Field("cloudregion_id"), region.Id))
|
||||
q = q.Filter(sqlchemy.Equals(vpcs.Field("manager_id"), provider.Id))
|
||||
q = q.Equals("manager_id", provider.Id)
|
||||
q = q.Equals("cloudregion_id", region.Id)
|
||||
q = q.IsFalse("pending_deleted")
|
||||
if err := db.FetchModelObjects(man, q, &lbs); err != nil {
|
||||
log.Errorf("failed to get lbs for region: %v provider: %v error: %v", region, provider, err)
|
||||
@@ -816,6 +823,12 @@ func (man *SLoadbalancerManager) getLoadbalancersByExternalIds(externalIds []str
|
||||
}
|
||||
|
||||
func (man *SLoadbalancerManager) getLocalLoadbalancers(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, lbs []cloudprovider.ICloudLoadbalancer) ([]SLoadbalancer, error) {
|
||||
// current external ID
|
||||
extIds := []string{}
|
||||
for i := range lbs {
|
||||
extIds = append(extIds, lbs[i].GetGlobalId())
|
||||
}
|
||||
|
||||
part1, err := man.getLoadbalancersByRegion(region, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -824,15 +837,13 @@ func (man *SLoadbalancerManager) getLocalLoadbalancers(ctx context.Context, user
|
||||
localLbs := map[string]SLoadbalancer{}
|
||||
for i := range part1 {
|
||||
localLbs[part1[i].Id] = part1[i]
|
||||
if len(part1[i].GetExternalId()) > 0 {
|
||||
extIds = append(extIds, part1[i].GetExternalId())
|
||||
}
|
||||
}
|
||||
|
||||
externalIds := []string{}
|
||||
for i := range lbs {
|
||||
externalIds = append(externalIds, lbs[i].GetGlobalId())
|
||||
}
|
||||
|
||||
if len(externalIds) > 0 {
|
||||
part2, err := man.getLoadbalancersByExternalIds(externalIds)
|
||||
if len(extIds) > 0 {
|
||||
part2, err := man.getLoadbalancersByExternalIds(extIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -842,7 +853,7 @@ func (man *SLoadbalancerManager) getLocalLoadbalancers(ctx context.Context, user
|
||||
}
|
||||
}
|
||||
|
||||
ret := []SLoadbalancer{}
|
||||
ret := make([]SLoadbalancer, 0)
|
||||
for id, _ := range localLbs {
|
||||
ret = append(ret, localLbs[id])
|
||||
}
|
||||
@@ -860,7 +871,7 @@ func (man *SLoadbalancerManager) SyncLoadbalancers(ctx context.Context, userCred
|
||||
remoteLbs := []cloudprovider.ICloudLoadbalancer{}
|
||||
syncResult := compare.SyncResult{}
|
||||
|
||||
dbLbs, err := man.getLocalLoadbalancers(ctx, userCred, provider, region, remoteLbs)
|
||||
dbLbs, err := man.getLocalLoadbalancers(ctx, userCred, provider, region, lbs)
|
||||
if err != nil {
|
||||
syncResult.Error(err)
|
||||
return nil, nil, syncResult
|
||||
@@ -1007,7 +1018,7 @@ func (lb *SLoadbalancer) syncRemoveCloudLoadbalancer(ctx context.Context, userCr
|
||||
lockman.LockObject(ctx, lb)
|
||||
defer lockman.ReleaseObject(ctx, lb)
|
||||
|
||||
err := lb.ValidateDeleteCondition(ctx)
|
||||
err := lb.validatePurgeCondition(ctx)
|
||||
if err != nil { // cannot delete
|
||||
return lb.SetStatus(userCred, api.LB_STATUS_UNKNOWN, "sync to delete")
|
||||
} else {
|
||||
|
||||
@@ -88,8 +88,9 @@ type SServerSku struct {
|
||||
PrepaidStatus string `width:"32" charset:"utf8" nullable:"true" list:"user" create:"admin_optional" default:"available"` // 预付费资源状态 available|soldout
|
||||
PostpaidStatus string `width:"32" charset:"utf8" nullable:"true" list:"user" create:"admin_optional" default:"available"` // 按需付费资源状态 available|soldout
|
||||
|
||||
CpuCoreCount int `nullable:"false" list:"user" create:"admin_required"`
|
||||
MemorySizeMB int `nullable:"false" list:"user" create:"admin_required"`
|
||||
CpuArch string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"` // CPU 架构 x86|xarm
|
||||
CpuCoreCount int `nullable:"false" list:"user" create:"admin_required"`
|
||||
MemorySizeMB int `nullable:"false" list:"user" create:"admin_required"`
|
||||
|
||||
OsName string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin" default:"Any"` // Windows|Linux|Any
|
||||
|
||||
@@ -834,7 +835,7 @@ func (manager *SServerSkuManager) ListItemFilter(
|
||||
conditions = append(
|
||||
conditions,
|
||||
sqlchemy.AND(
|
||||
sqlchemy.GE(q.Field("memory_size_mb"), s),
|
||||
sqlchemy.GT(q.Field("memory_size_mb"), s),
|
||||
sqlchemy.LE(q.Field("memory_size_mb"), e),
|
||||
),
|
||||
)
|
||||
@@ -1171,6 +1172,7 @@ func (self *SServerSku) syncWithCloudSku(ctx context.Context, userCred mcclient.
|
||||
self.InstanceTypeCategory = extSku.InstanceTypeCategory
|
||||
self.PrepaidStatus = extSku.PrepaidStatus
|
||||
self.PostpaidStatus = extSku.PostpaidStatus
|
||||
self.CpuArch = extSku.CpuArch
|
||||
self.SysDiskType = extSku.SysDiskType
|
||||
self.DataDiskTypes = extSku.DataDiskTypes
|
||||
return nil
|
||||
|
||||
@@ -106,7 +106,7 @@ func (self *GuestDeployTask) OnDeployGuestComplete(ctx context.Context, obj db.I
|
||||
}
|
||||
if !_log {
|
||||
// 如果 deploy 有其他事件,统一记在这里。
|
||||
logclient.AddActionLogWithStartable(self, guest, "misc部署", "", self.UserCred, true)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_DEPLOY, action, self.UserCred, true)
|
||||
}
|
||||
|
||||
if self.HasParentTask() {
|
||||
|
||||
@@ -88,6 +88,7 @@ func (d *SKVMGuestDisk) MountKvmRootfs() fsdriver.IRootFsDriver {
|
||||
func (d *SKVMGuestDisk) mountKvmRootfs(readonly bool) fsdriver.IRootFsDriver {
|
||||
partitions := d.deployer.GetPartitions()
|
||||
for i := 0; i < len(partitions); i++ {
|
||||
log.Infof("detect partition %s", partitions[i].GetPartDev())
|
||||
mountFunc := partitions[i].Mount
|
||||
if readonly {
|
||||
mountFunc = partitions[i].MountPartReadOnly
|
||||
|
||||
@@ -164,6 +164,7 @@ func (d *NBDDriver) setupLVMS() (bool, error) {
|
||||
for _, part := range d.partitions {
|
||||
vgname := d.findLVMPartitions(part.GetPartDev())
|
||||
if len(vgname) > 0 {
|
||||
log.Infof("find vg %s from %s", vgname, part.GetPartDev())
|
||||
lvm := NewKVMGuestLVMPartition(part.GetPartDev(), vgname)
|
||||
d.lvms = append(d.lvms, lvm)
|
||||
if lvm.SetupDevice() {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/stringutils"
|
||||
@@ -132,13 +133,11 @@ func (p *SKVMGuestLVMPartition) FindPartitions() []*kvmpart.SKVMGuestDiskPartiti
|
||||
if !p.isVgActive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir("/dev/" + p.vgname)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := []*kvmpart.SKVMGuestDiskPartition{}
|
||||
for _, f := range files {
|
||||
partPath := fmt.Sprintf("/dev/%s/%s", p.vgname, f.Name())
|
||||
@@ -168,11 +167,15 @@ func (p *SKVMGuestLVMPartition) PutdownDevice() bool {
|
||||
}
|
||||
|
||||
func (p *SKVMGuestLVMPartition) isVgActive() bool {
|
||||
if fileutils2.Exists("/dev/" + p.vgname) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
for i := 0; i < 3; i++ {
|
||||
if fileutils2.Exists("/dev/" + p.vgname) {
|
||||
log.Infof("vg %s is active", p.vgname)
|
||||
return true
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
log.Infof("vg %s is not active", p.vgname)
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *SKVMGuestLVMPartition) vgActivate(activate bool) bool {
|
||||
@@ -185,6 +188,9 @@ func (p *SKVMGuestLVMPartition) vgActivate(activate bool) bool {
|
||||
log.Errorf("%s", output)
|
||||
return false
|
||||
}
|
||||
if out, err := procutils.NewCommand("vgchange", "--refresh").Output(); err != nil {
|
||||
log.Errorf("vgchange refresh failed: %s, %s", out, err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,15 @@ import (
|
||||
const (
|
||||
RoleAdmin = "admin"
|
||||
RoleFA = "fa"
|
||||
RoleDomainFA = "domainfa"
|
||||
RoleProjectFA = "projectfa"
|
||||
RoleSA = "sa"
|
||||
RoleProjectOwner = "project_owner"
|
||||
RoleMember = "member"
|
||||
RoleDomainAdmin = "domainadmin"
|
||||
RoleProjectEditor = "project_editor"
|
||||
RoleProjectViewer = "project_viewer"
|
||||
|
||||
RoleMember = "member"
|
||||
)
|
||||
|
||||
type sPolicyDefinition struct {
|
||||
@@ -42,6 +46,7 @@ type SRoleDefiniton struct {
|
||||
Description string
|
||||
Policies []string
|
||||
Project string
|
||||
IsPublic bool
|
||||
|
||||
DescriptionCN string
|
||||
}
|
||||
@@ -287,7 +292,8 @@ var (
|
||||
Desc: "resources of metering and billing service",
|
||||
Scope: rbacutils.ScopeProject,
|
||||
Services: map[string][]string{
|
||||
"meter": nil,
|
||||
"meter": nil,
|
||||
"suggestion": nil,
|
||||
"notify": {
|
||||
"receivers",
|
||||
},
|
||||
@@ -423,7 +429,8 @@ var (
|
||||
Policies: []string{
|
||||
"sysadmin",
|
||||
},
|
||||
Project: "system",
|
||||
Project: "system",
|
||||
IsPublic: false,
|
||||
},
|
||||
{
|
||||
Name: RoleDomainAdmin,
|
||||
@@ -432,6 +439,7 @@ var (
|
||||
Policies: []string{
|
||||
"domainadmin",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
{
|
||||
Name: RoleProjectOwner,
|
||||
@@ -440,24 +448,37 @@ var (
|
||||
Policies: []string{
|
||||
"projectadmin",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
{
|
||||
Name: RoleFA,
|
||||
DescriptionCN: "财务管理员",
|
||||
DescriptionCN: "系统财务管理员",
|
||||
Description: "System finance administrator",
|
||||
Policies: []string{
|
||||
"sysmeteradmin",
|
||||
"sysdashboard",
|
||||
},
|
||||
IsPublic: false,
|
||||
},
|
||||
{
|
||||
Name: RoleMember,
|
||||
DescriptionCN: "项目只读成员",
|
||||
Description: "Project read-only member",
|
||||
Name: RoleDomainFA,
|
||||
DescriptionCN: "域财务管理员",
|
||||
Description: "Domain finance administrator",
|
||||
Policies: []string{
|
||||
"projectviewer",
|
||||
"domainmeteradmin",
|
||||
"domaindashboard",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
{
|
||||
Name: RoleProjectFA,
|
||||
DescriptionCN: "项目财务管理员",
|
||||
Description: "Project finance administrator",
|
||||
Policies: []string{
|
||||
"projectmeteradmin",
|
||||
"projectdashboard",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
{
|
||||
Name: RoleProjectEditor,
|
||||
@@ -467,6 +488,17 @@ var (
|
||||
"projecteditor",
|
||||
"projectdashboard",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
{
|
||||
Name: RoleProjectViewer,
|
||||
DescriptionCN: "项目只读成员",
|
||||
Description: "Project read-only member",
|
||||
Policies: []string{
|
||||
"projectviewer",
|
||||
"projectdashboard",
|
||||
},
|
||||
IsPublic: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -113,7 +113,7 @@ func (manager *SCredentialManager) InitializeData() error {
|
||||
|
||||
func (manager *SCredentialManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if !data.Contains("type") {
|
||||
return nil, httperrors.NewInputParameterError("missing input feild type")
|
||||
return nil, httperrors.NewInputParameterError("missing input field type")
|
||||
}
|
||||
projectId, _ := data.GetString("project_id")
|
||||
userId := ownerId.GetUserId()
|
||||
|
||||
@@ -286,7 +286,7 @@ func (domain *SDomain) ValidatePurgeCondition(ctx context.Context) error {
|
||||
}
|
||||
groupCnt, _ := domain.GetGroupCount()
|
||||
if groupCnt > 0 {
|
||||
return httperrors.NewInvalidStatusError("group is in use by group")
|
||||
return httperrors.NewInvalidStatusError("domain is in use by group")
|
||||
}
|
||||
projCnt, _ := domain.GetProjectCount()
|
||||
if projCnt > 0 {
|
||||
|
||||
@@ -344,7 +344,7 @@ func (ident *SIdentityProvider) PerformConfig(ctx context.Context, userCred mccl
|
||||
action := input.Action
|
||||
changed, err := saveConfigs(userCred, action, ident, opts, nil, nil, api.SensitiveDomainConfigMap)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("saveConfig fail %s", err)
|
||||
return nil, httperrors.NewInternalServerError("saveConfigs fail %s", err)
|
||||
}
|
||||
if changed {
|
||||
ident.MarkDisconnected(ctx, userCred, fmt.Errorf("change config"))
|
||||
|
||||
@@ -207,7 +207,7 @@ func (service *SService) PerformConfig(ctx context.Context, userCred mcclient.To
|
||||
changed, err = saveConfigs(userCred, action, service, opts, nil, api.MergeServiceConfigOptions(api.CommonWhitelistOptionMap, api.ServiceBlacklistOptionMap), nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("saveConfig fail %s", err)
|
||||
return nil, httperrors.NewInternalServerError("saveConfigs fail %s", err)
|
||||
}
|
||||
if changed {
|
||||
diff := SService{ConfigVersion: 1}
|
||||
|
||||
@@ -24,6 +24,7 @@ var (
|
||||
SuggestSysAlertManager *SSuggestSysAlertManager
|
||||
SuggestSysRuleConfigManager *SSuggestSysRuleConfigManager
|
||||
InfluxdbShemaManager *SInfluxdbShemaManager
|
||||
SuggestSysAlertCostManager *SSuggestSysAlertManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -31,6 +32,7 @@ func init() {
|
||||
SuggestSysAlertManager = NewSuggestSysAlertManager()
|
||||
SuggestSysRuleConfigManager = NewSuggestSysRuleConfigManager()
|
||||
InfluxdbShemaManager = NewInfluxdbShemaManager()
|
||||
SuggestSysAlertCostManager = NewSuggestSysAlertCostManager()
|
||||
for _, m := range []modulebase.IBaseManager{
|
||||
SuggestSysRuleManager,
|
||||
SuggestSysAlertManager,
|
||||
@@ -75,6 +77,15 @@ func NewSuggestSysAlertManager() *SSuggestSysAlertManager {
|
||||
}
|
||||
}
|
||||
|
||||
func NewSuggestSysAlertCostManager() *SSuggestSysAlertManager {
|
||||
man := modules.NewSuggestionManager("suggestsysalert", "suggestsysalerts",
|
||||
[]string{},
|
||||
[]string{})
|
||||
return &SSuggestSysAlertManager{
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSuggestSysRuleConfigManager() *SSuggestSysRuleConfigManager {
|
||||
man := modules.NewSuggestionManager("suggestsysruleconfig", "suggestsysruleconfigs",
|
||||
[]string{"id", "name", "type", "resource_type", "enabled", "ignore_alert"},
|
||||
|
||||
@@ -22,7 +22,8 @@ import (
|
||||
|
||||
type SuggestSysAlertListOptions struct {
|
||||
options.BaseListOptions
|
||||
Type string `help:"Type of suggest rule" choices:"EIP_UNUSED|"`
|
||||
Type string `help:"Type of suggest rule"`
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
func (o *SuggestSysAlertListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
@@ -55,3 +56,17 @@ func (opt *SuggestAlertIgnoreOptions) Params() (jsonutils.JSONObject, error) {
|
||||
func (o *SuggestAlertIgnoreOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
type SuggestAlertCostOptions struct {
|
||||
ID string `help:"ID or name of the alert" json:"-"`
|
||||
options.BaseListOptions
|
||||
Currency string `json:"currency"`
|
||||
}
|
||||
|
||||
func (o *SuggestAlertCostOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
func (o *SuggestAlertCostOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
@@ -111,11 +111,18 @@ func (c *NoDataQueryCondition) getOnecloudResources(evalContext *alerting.EvalCo
|
||||
query := jsonutils.NewDict()
|
||||
query.Add(jsonutils.NewStringArray([]string{"running", "ready"}), "status")
|
||||
query.Add(jsonutils.NewString("true"), "admin")
|
||||
if len(c.Query.Model.Tags) != 0 {
|
||||
query, err = c.convertTagsQuery(evalContext, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NoDataQueryCondition convertTagsQuery error")
|
||||
}
|
||||
}
|
||||
switch evalContext.Rule.RuleDescription[0].ResType {
|
||||
case monitor.METRIC_RES_TYPE_HOST:
|
||||
query.Set("host-type", jsonutils.NewString(hostconsts.TELEGRAF_TAG_KEY_HYPERVISOR))
|
||||
allResources, err = ListAllResources(&mc_mds.Hosts, query)
|
||||
case monitor.METRIC_RES_TYPE_GUEST:
|
||||
allResources, err = ListAllResources(&mc_mds.Servers, query)
|
||||
case monitor.METRIC_RES_TYPE_RDS:
|
||||
allResources, err = ListAllResources(&mc_mds.DBInstance, query)
|
||||
case monitor.METRIC_RES_TYPE_REDIS:
|
||||
@@ -135,6 +142,29 @@ func (c *NoDataQueryCondition) getOnecloudResources(evalContext *alerting.EvalCo
|
||||
return allResources, nil
|
||||
}
|
||||
|
||||
func (c *NoDataQueryCondition) convertTagsQuery(evalContext *alerting.EvalContext,
|
||||
query *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
alertDetails, err := c.GetCommonAlertDetails(evalContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, _ := range c.Query.Model.Tags {
|
||||
filterCount := 0
|
||||
if c.Query.Model.Tags[i].Operator == "=" {
|
||||
if tag, ok := monitor.MEASUREMENT_TAG_KEYWORD[alertDetails.ResType]; ok {
|
||||
if c.Query.Model.Tags[i].Key == tag {
|
||||
query.Set("name", jsonutils.NewString(c.Query.Model.Tags[i].Value))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
query.Set(fmt.Sprintf("filter.%d", filterCount),
|
||||
jsonutils.NewString(fmt.Sprintf("%s.notin(%s)", c.Query.Model.Tags[i].Key, c.Query.Model.Tags[i].Value)))
|
||||
filterCount++
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
||||
func ListAllResources(manager modulebase.Manager, params *jsonutils.JSONDict) ([]jsonutils.JSONObject, error) {
|
||||
if params == nil {
|
||||
params = jsonutils.NewDict()
|
||||
|
||||
@@ -194,12 +194,10 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio
|
||||
func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb.TimeSeries,
|
||||
meta *tsdb.QueryResultMeta, value *float64, valStrArr []string) (*monitor.EvalMatch, error) {
|
||||
evalMatch := new(monitor.EvalMatch)
|
||||
alert, err := models.CommonAlertManager.GetAlert(context.Rule.Id)
|
||||
alertDetails, err := c.GetCommonAlertDetails(context)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetAlert to NewEvalMatch error")
|
||||
}
|
||||
settings, _ := alert.GetSettings()
|
||||
alertDetails := alert.GetCommonAlertMetricDetailsFromAlertCondition(c.Index, &settings.Conditions[c.Index])
|
||||
evalMatch.Metric = fmt.Sprintf("%s.%s", alertDetails.Measurement, alertDetails.Field)
|
||||
queryKeyInfo := ""
|
||||
if len(alertDetails.MeasurementDisplayName) > 0 && len(alertDetails.FieldDescription.DisplayName) > 0 {
|
||||
@@ -226,6 +224,16 @@ func (c *QueryCondition) NewEvalMatch(context *alerting.EvalContext, series tsdb
|
||||
return evalMatch, nil
|
||||
}
|
||||
|
||||
func (c *QueryCondition) GetCommonAlertDetails(context *alerting.EvalContext) (*monitor.CommonAlertMetricDetails, error) {
|
||||
alert, err := models.CommonAlertManager.GetAlert(context.Rule.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetAlert to NewEvalMatch error")
|
||||
}
|
||||
settings, _ := alert.GetSettings()
|
||||
alertDetails := alert.GetCommonAlertMetricDetailsFromAlertCondition(c.Index, &settings.Conditions[c.Index])
|
||||
return alertDetails, nil
|
||||
}
|
||||
|
||||
func (c *QueryCondition) jointPointStr(series tsdb.TimeSeries, value string, valStrArr []string) string {
|
||||
str := ""
|
||||
for i := 0; i < len(valStrArr); i++ {
|
||||
|
||||
@@ -134,6 +134,20 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) (*float64, []string) {
|
||||
if value > 0 {
|
||||
allNull = false
|
||||
}
|
||||
case "P95":
|
||||
var values []float64
|
||||
for _, v := range series.Points {
|
||||
if v.IsValid() {
|
||||
allNull = false
|
||||
values = append(values, v.Value())
|
||||
}
|
||||
}
|
||||
if len(values) >= 1 {
|
||||
sort.Float64s(values)
|
||||
length := len(values)
|
||||
index := math.Floor(float64(length) * 0.95)
|
||||
value = values[int64(index)]
|
||||
}
|
||||
}
|
||||
|
||||
if allNull {
|
||||
|
||||
@@ -128,7 +128,17 @@ func (c *EvalContext) GetCallbackURLPrefix() string {
|
||||
return ""
|
||||
}
|
||||
url, _ := config.GetString("config", "default", "api_server")
|
||||
return url + "/alertrecord"
|
||||
defaultWebUri := "alertrecord"
|
||||
matchTag := map[string]string{}
|
||||
if c.Firing {
|
||||
matchTag = c.EvalMatches[0].Tags
|
||||
} else {
|
||||
matchTag = c.AlertOkEvalMatches[0].Tags
|
||||
}
|
||||
if uri, ok := matchTag["web_url"]; ok {
|
||||
defaultWebUri = uri
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", url, defaultWebUri)
|
||||
}
|
||||
|
||||
// GetNewState returns the new state from the alert rule evaluation.
|
||||
|
||||
@@ -101,18 +101,18 @@ func init() {
|
||||
|
||||
// mem
|
||||
RegistryMetricCreateInput("mem", "Memory", monitor.METRIC_RES_TYPE_HOST,
|
||||
monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
|
||||
monitor.METRIC_DATABASE_TELE, 2, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("used_percent", "Used memory rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
newMetricFieldCreateInput("available_percent", "Available memory rate", monitor.METRIC_UNIT_PERCENT, 2),
|
||||
newMetricFieldCreateInput("used", "Used memory", monitor.METRIC_UNIT_BYTE, 3),
|
||||
newMetricFieldCreateInput("free", "Free memory", monitor.METRIC_UNIT_BYTE, 4),
|
||||
newMetricFieldCreateInput("available", "Available memory", monitor.METRIC_UNIT_BYTE, 4),
|
||||
newMetricFieldCreateInput("active", "The amount of active memory", monitor.METRIC_UNIT_BYTE, 5),
|
||||
newMetricFieldCreateInput("inactive", "The amount of inactive memory", monitor.METRIC_UNIT_BYTE, 6),
|
||||
newMetricFieldCreateInput("cached", "Cache memory", monitor.METRIC_UNIT_BYTE, 7),
|
||||
newMetricFieldCreateInput("buffered", "Buffer memory", monitor.METRIC_UNIT_BYTE, 7),
|
||||
newMetricFieldCreateInput("slab", "Number of kernel caches", monitor.METRIC_UNIT_BYTE, 8),
|
||||
newMetricFieldCreateInput("total", "Total memory", monitor.METRIC_UNIT_BYTE, 9),
|
||||
newMetricFieldCreateInput("available", "Available memory", monitor.METRIC_UNIT_BYTE, 9),
|
||||
newMetricFieldCreateInput("total", "Total memory", monitor.METRIC_UNIT_BYTE, 10),
|
||||
})
|
||||
|
||||
// net
|
||||
|
||||
@@ -336,12 +336,25 @@ func (manager *SAlertRecordManager) getNowAlertingRecord(ctx context.Context, us
|
||||
query = manager.FilterByOwner(query, userCred, rbacutils.String2Scope(scope))
|
||||
query = query.GE("created_at", startTime.UTC().Format(timeutils.MysqlTimeFormat))
|
||||
query = query.Equals("state", monitor.AlertStateAlerting)
|
||||
sQuery := CommonAlertManager.Query("id").Equals("state", monitor.AlertStateAlerting).IsNull("used_by").SubQuery()
|
||||
query = query.In("alert_id", sQuery).IsNotNull("res_type").IsNotEmpty("res_type").GroupBy("alert_id")
|
||||
query = query.IsNotNull("res_type").IsNotEmpty("res_type").Desc("created_at")
|
||||
|
||||
alertsQuery := CommonAlertManager.Query("id").Equals("state", monitor.AlertStateAlerting).IsNull("used_by")
|
||||
alertsQuery = CommonAlertManager.FilterByOwner(alertsQuery, userCred, rbacutils.String2Scope(scope))
|
||||
alerts := make([]SCommonAlert, 0)
|
||||
records := make([]SAlertRecord, 0)
|
||||
err := db.FetchModelObjects(manager, query, &records)
|
||||
err := db.FetchModelObjects(CommonAlertManager, alertsQuery, &alerts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, alert := range alerts {
|
||||
tmp := *query
|
||||
recordModel, err := db.NewModelObject(manager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := (&tmp).Equals("alert_id", alert.GetId()).First(recordModel); err == nil {
|
||||
records = append(records, *(recordModel.(*SAlertRecord)))
|
||||
}
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ func (m *SAlertResourceManager) createOrUpdateFromRecord(
|
||||
if res == nil {
|
||||
return m.createFromRecord(ctx, userCred, ownerId, drv, record, match)
|
||||
} else {
|
||||
return res.updateFromRecord(ctx, userCred, record, match)
|
||||
return res.updateFromRecord(ctx, userCred, drv, record, match)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +297,8 @@ func (res *SAlertResource) getAttachedAlerts() ([]SCommonAlert, error) {
|
||||
return alerts, nil
|
||||
}
|
||||
|
||||
func (res *SAlertResource) updateFromRecord(ctx context.Context, userCred mcclient.TokenCredential, record *SAlertRecord, match monitor.EvalMatch) error {
|
||||
func (res *SAlertResource) updateFromRecord(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
drv IAlertResourceDriver, record *SAlertRecord, match monitor.EvalMatch) error {
|
||||
jObj, err := res.GetJointAlert(record.AlertId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "get joint alert by id %s", record.AlertId)
|
||||
@@ -310,6 +311,12 @@ func (res *SAlertResource) updateFromRecord(ctx context.Context, userCred mcclie
|
||||
if err := jObj.UpdateData(record, &match); err != nil {
|
||||
return errors.Wrapf(err, "update joint object by matches %v", match)
|
||||
}
|
||||
if _, err := db.Update(res, func() error {
|
||||
res.Type = string(drv.GetType())
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -226,9 +226,7 @@ func (alert *SCommonAlert) CustomizeCreate(
|
||||
query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject,
|
||||
) error {
|
||||
if err := alert.SAlert.CustomizeCreate(ctx, userCred, ownerId, query, data); err != nil {
|
||||
return err
|
||||
}
|
||||
alert.State = string(monitor.AlertStateUnknown)
|
||||
input := new(monitor.CommonAlertCreateInput)
|
||||
if err := data.Unmarshal(input); err != nil {
|
||||
return err
|
||||
|
||||
@@ -191,7 +191,7 @@ func (self *SUnifiedMonitorManager) AllowPerformQuery(ctx context.Context, userC
|
||||
|
||||
func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
tmp := jsonutils.DeepCopy(data)
|
||||
self.handleDataPreSignature(tmp)
|
||||
self.handleDataPreSignature(ctx, tmp)
|
||||
if err := ValidateQuerySignature(tmp); err != nil {
|
||||
return nil, errors.Wrap(err, "ValidateQuerySignature")
|
||||
}
|
||||
@@ -235,19 +235,28 @@ func (self *SUnifiedMonitorManager) PerformQuery(ctx context.Context, userCred m
|
||||
return jsonutils.Marshal(rtn), nil
|
||||
}
|
||||
|
||||
func (self *SUnifiedMonitorManager) handleDataPreSignature(data jsonutils.JSONObject) {
|
||||
func (self *SUnifiedMonitorManager) handleDataPreSignature(ctx context.Context, data jsonutils.JSONObject) {
|
||||
scope, _ := data.GetString("scope")
|
||||
isIdentityName, _ := data.Bool("identity_name")
|
||||
switch scope {
|
||||
case "system":
|
||||
case "domain":
|
||||
domain, err := data.GetString("project_domain")
|
||||
if err == nil {
|
||||
domainObj, _ := db.DefaultDomainFetcher(ctx, domain)
|
||||
if isIdentityName {
|
||||
domain = domainObj.Name
|
||||
}
|
||||
data.(*jsonutils.JSONDict).Remove("project_domain")
|
||||
data.(*jsonutils.JSONDict).Set("domain_id", jsonutils.NewString(domain))
|
||||
}
|
||||
default:
|
||||
project, err := data.GetString("project")
|
||||
if err == nil {
|
||||
tenant, _ := db.DefaultProjectFetcher(ctx, project)
|
||||
if isIdentityName {
|
||||
project = tenant.Name
|
||||
}
|
||||
data.(*jsonutils.JSONDict).Remove("project")
|
||||
data.(*jsonutils.JSONDict).Set("project_id", jsonutils.NewString(project))
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ const (
|
||||
|
||||
AWS_GLOBAL_ARN_PREFIX = "arn:aws:iam::aws:policy/"
|
||||
AWS_CHINA_ARN_PREFIX = "arn:aws-cn:iam::aws:policy/"
|
||||
|
||||
DEFAULT_S3_REGION_ID = "us-east-1"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -304,6 +306,11 @@ func (client *SAwsClient) fetchBuckets() error {
|
||||
}
|
||||
|
||||
location := *output.LocationConstraint
|
||||
if len(location) == 0 {
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
|
||||
// Buckets in Region us-east-1 have a LocationConstraint of null.
|
||||
location = DEFAULT_S3_REGION_ID
|
||||
}
|
||||
region, err := client.getIRegionByRegionId(location)
|
||||
if err != nil {
|
||||
log.Errorf("client.getIRegionByRegionId %s fail %s", location, err)
|
||||
|
||||
@@ -992,8 +992,11 @@ func (region *SRegion) CreateIBucket(name string, storageClassStr string, acl st
|
||||
}
|
||||
input := &s3.CreateBucketInput{}
|
||||
input.SetBucket(name)
|
||||
input.CreateBucketConfiguration = &s3.CreateBucketConfiguration{}
|
||||
input.CreateBucketConfiguration.SetLocationConstraint(region.GetId())
|
||||
if region.GetId() != DEFAULT_S3_REGION_ID {
|
||||
location := region.GetId()
|
||||
input.CreateBucketConfiguration = &s3.CreateBucketConfiguration{}
|
||||
input.CreateBucketConfiguration.SetLocationConstraint(location)
|
||||
}
|
||||
_, err = s3cli.CreateBucket(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "CreateBucket")
|
||||
|
||||
@@ -142,7 +142,7 @@ func (self *SAzureClient) getClient(resource TAzureResource) (*autorest.Client,
|
||||
return nil, errors.Wrapf(err, "azureenv.EnvironmentFromName(%s)", self.envName)
|
||||
}
|
||||
|
||||
httpClient := self.cpcfg.HttpClient()
|
||||
httpClient := self.cpcfg.AdaptiveTimeoutHttpClient()
|
||||
client.Sender = httpClient
|
||||
|
||||
self.env = env
|
||||
@@ -200,16 +200,20 @@ func (self *SAzureClient) jsonRequest(method, path string, body jsonutils.JSONOb
|
||||
if ae, ok := err.(*AzureResponseError); ok {
|
||||
switch ae.AzureError.Code {
|
||||
case "SubscriptionNotRegistered":
|
||||
err = self.ServiceRegister("Microsoft.Network")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "self.registerService(Microsoft.Network)")
|
||||
service := self.getService(path)
|
||||
if len(service) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
re := self.ServiceRegister("Microsoft.Network")
|
||||
if re != nil {
|
||||
return nil, errors.Wrapf(re, "self.registerService(Microsoft.Network)")
|
||||
}
|
||||
continue
|
||||
case "MissingSubscriptionRegistration":
|
||||
for _, serviceType := range ae.AzureError.Details {
|
||||
err = self.ServiceRegister(serviceType.Target)
|
||||
re := self.ServiceRegister(serviceType.Target)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "self.registerService(%s)", serviceType.Target)
|
||||
return nil, errors.Wrapf(re, "self.registerService(%s)", serviceType.Target)
|
||||
}
|
||||
}
|
||||
continue
|
||||
@@ -367,6 +371,25 @@ func (self *SAzureClient) list(resource string, params url.Values, retVal interf
|
||||
return jsonutils.Update(retVal, result)
|
||||
}
|
||||
|
||||
func (self *SAzureClient) getService(path string) string {
|
||||
for _, service := range []string{
|
||||
"microsoft.compute",
|
||||
"microsoft.classiccompute",
|
||||
"microsoft.network",
|
||||
"microsoft.classicnetwork",
|
||||
"microsoft.storage",
|
||||
"microsoft.classicstorage",
|
||||
"microsoft.billing",
|
||||
"microsoft.insights",
|
||||
"microsoft.authorization",
|
||||
} {
|
||||
if strings.Contains(strings.ToLower(path), service) {
|
||||
return service
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SAzureClient) _apiVersion(resource string, params url.Values) string {
|
||||
version := params.Get("api-version")
|
||||
if len(version) > 0 {
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -155,6 +156,23 @@ type VirtualMachineProperties struct {
|
||||
VmId string `json:"vmId,omitempty"`
|
||||
}
|
||||
|
||||
type SExtensionResourceProperties struct {
|
||||
AutoUpgradeMinorVersion bool
|
||||
ProvisioningState string
|
||||
Publisher string
|
||||
Type string
|
||||
TypeHandlerVersion string
|
||||
}
|
||||
|
||||
type SExtensionResource struct {
|
||||
Id string
|
||||
Name string
|
||||
Type string
|
||||
Location string
|
||||
|
||||
Properties SExtensionResourceProperties
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
multicloud.SInstanceBase
|
||||
host *SHost
|
||||
@@ -166,6 +184,8 @@ type SInstance struct {
|
||||
Location string
|
||||
vmSize *SVMSize
|
||||
Tags map[string]string
|
||||
|
||||
Resources []SExtensionResource
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
|
||||
@@ -500,15 +520,22 @@ func (region *SRegion) resetOvsEnv(instanceId string) error {
|
||||
func (region *SRegion) deleteExtension(instanceId, extensionName string) error {
|
||||
return region.del(fmt.Sprintf("%s/extensions/%s", instanceId, extensionName))
|
||||
}
|
||||
func (region *SRegion) resetLoginInfo(osType, instanceId string, setting map[string]string) error {
|
||||
func (region *SRegion) resetLoginInfo(osType, instanceId string, setting map[string]interface{}) error {
|
||||
// https://github.com/Azure/azure-linux-extensions/blob/master/VMAccess/README.md
|
||||
handlerVersion := "1.5"
|
||||
properties := map[string]interface{}{
|
||||
"Publisher": "Microsoft.OSTCExtensions",
|
||||
"Type": "VMAccessForLinux",
|
||||
"TypeHandlerVersion": "1.4",
|
||||
"Settings": setting,
|
||||
"TypeHandlerVersion": handlerVersion,
|
||||
"Settings": map[string]string{},
|
||||
"protectedSettings": setting,
|
||||
|
||||
"autoUpgradeMinorVersion": true,
|
||||
}
|
||||
if osType == osprofile.OS_TYPE_WINDOWS {
|
||||
properties["TypeHandlerVersion"] = "2.0"
|
||||
// https://github.com/Azure/azure-cli/blob/dev/src/azure-cli/azure/cli/command_modules/vm/custom.py
|
||||
handlerVersion = "2.4"
|
||||
properties["TypeHandlerVersion"] = handlerVersion
|
||||
properties["Publisher"] = "Microsoft.Compute"
|
||||
properties["Type"] = "VMAccessAgent"
|
||||
}
|
||||
@@ -516,8 +543,20 @@ func (region *SRegion) resetLoginInfo(osType, instanceId string, setting map[str
|
||||
"Location": region.Name,
|
||||
"Properties": properties,
|
||||
}
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%s)", instanceId)
|
||||
}
|
||||
for _, extension := range instance.Resources {
|
||||
if extension.Name == "enablevmaccess" {
|
||||
if version.GT(extension.Properties.TypeHandlerVersion, handlerVersion) {
|
||||
properties["TypeHandlerVersion"] = extension.Properties.TypeHandlerVersion
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
resource := fmt.Sprintf("%s/extensions/enablevmaccess", instanceId)
|
||||
_, err := region.put(resource, jsonutils.Marshal(params))
|
||||
_, err = region.put(resource, jsonutils.Marshal(params))
|
||||
if err != nil {
|
||||
switch osType {
|
||||
case osprofile.OS_TYPE_WINDOWS:
|
||||
@@ -536,11 +575,39 @@ func (region *SRegion) resetLoginInfo(osType, instanceId string, setting map[str
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "GetInstance(%s)", instanceId)
|
||||
}
|
||||
for _, extension := range instance.Resources {
|
||||
if extension.Name == "enablevmaccess" {
|
||||
if extension.Properties.ProvisioningState == "Succeeded" {
|
||||
return true, nil
|
||||
}
|
||||
log.Debugf("enablevmaccess status %s expect Succeeded", extension.Properties.ProvisioningState)
|
||||
if extension.Properties.ProvisioningState == "Failed" {
|
||||
if instance.Properties.InstanceView != nil {
|
||||
for _, info := range instance.Properties.InstanceView.Extensions {
|
||||
if info.Name == "enablevmaccess" && len(info.Statuses) > 0 {
|
||||
return false, fmt.Errorf("details: %s", jsonutils.Marshal(info.Statuses))
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, fmt.Errorf("reset passwod failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "wait for enablevmaccess error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) resetPublicKey(osType, instanceId string, username, publicKey string) error {
|
||||
setting := map[string]string{
|
||||
setting := map[string]interface{}{
|
||||
"username": username,
|
||||
"ssh_key": publicKey,
|
||||
}
|
||||
@@ -548,7 +615,7 @@ func (region *SRegion) resetPublicKey(osType, instanceId string, username, publi
|
||||
}
|
||||
|
||||
func (region *SRegion) resetPassword(osType, instanceId, username, password string) error {
|
||||
setting := map[string]string{
|
||||
setting := map[string]interface{}{
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
|
||||
@@ -777,6 +777,20 @@ func (self *SInstance) DeleteVM(ctx context.Context) error {
|
||||
return errors.Wrapf(err, "region.DeleteVM(%s)", self.InstanceId)
|
||||
}
|
||||
if self.GetBillingType() == billing_api.BILLING_TYPE_PREPAID { // 预付费的需要删除两次
|
||||
cloudprovider.Wait(time.Second*10, time.Minute*5, func() (bool, error) {
|
||||
err = self.Refresh()
|
||||
if err != nil {
|
||||
log.Warningf("refresh instance %s(%s) error: %v", self.InstanceName, self.InstanceId, err)
|
||||
return false, nil
|
||||
}
|
||||
// 需要等待第一次删除后,状态变为SHUTDOWN,才可以进行第二次删除,否则会报:
|
||||
// Code=OperationDenied.InstanceOperationInProgress, Message=该实例`['ins-mxqturgj']`
|
||||
if self.InstanceState == "SHUTDOWN" {
|
||||
return true, nil
|
||||
}
|
||||
log.Debugf("wait %s(%s) status be SHUTDOWN, current is %s", self.InstanceName, self.InstanceId, self.InstanceState)
|
||||
return false, nil
|
||||
})
|
||||
err := self.host.zone.region.DeleteVM(self.InstanceId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "region.DeleteVM(%s)", self.InstanceId)
|
||||
|
||||
@@ -28,19 +28,37 @@ type INotifyService interface {
|
||||
StopAll()
|
||||
UpdateServices(ctx context.Context, userCred mcclient.TokenCredential, isStart bool)
|
||||
RestartService(ctx context.Context, config SConfig, serviceName string)
|
||||
Send(ctx context.Context, contactType, contact, topic, msg, priority string) error
|
||||
Send(ctx context.Context, p SSendParams) error
|
||||
ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error)
|
||||
BatchSend(ctx context.Context, contacts []string, contactType, topic, message, priority string) ([]*apis.FailedRecord, error)
|
||||
BatchSend(ctx context.Context, p SBatchSendParams) ([]*apis.FailedRecord, error)
|
||||
ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error)
|
||||
}
|
||||
|
||||
type SSendParams struct {
|
||||
ContactType string
|
||||
Contact string
|
||||
Topic string
|
||||
Message string
|
||||
Priority string
|
||||
Lang string
|
||||
}
|
||||
|
||||
type SBatchSendParams struct {
|
||||
ContactType string
|
||||
Contacts []string
|
||||
Topic string
|
||||
Message string
|
||||
Priority string
|
||||
Lang string
|
||||
}
|
||||
|
||||
type IServiceConfigStore interface {
|
||||
GetConfig(serviceName string) (SConfig, error)
|
||||
SetConfig(serviceName string, config SConfig) error
|
||||
}
|
||||
|
||||
type ITemplateStore interface {
|
||||
NotifyFilter(contactType, topic, msg string) (params apis.SendParams, err error)
|
||||
NotifyFilter(contactType, topic, msg, lang string) (params apis.SendParams, err error)
|
||||
}
|
||||
|
||||
type SConfig map[string]string
|
||||
|
||||
@@ -128,7 +128,11 @@ func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred
|
||||
if len(input.Topic) < 10 {
|
||||
length = len(input.Topic)
|
||||
}
|
||||
input.Name = fmt.Sprintf("%s-%s-%s", input.Topic[:length], input.ContactType, nowStr)
|
||||
name := fmt.Sprintf("%s-%s-%s", input.Topic[:length], input.ContactType, nowStr)
|
||||
input.Name, err = db.GenerateName(nm, ownerId, name)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "unable to generate name for %s", name)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -227,7 +231,7 @@ func (n *SNotification) ReceiveDetails() ([]api.ReceiveDetail, error) {
|
||||
|
||||
func (n *SNotification) getMoreDetails(ctx context.Context, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
|
||||
// get title adn content
|
||||
p, err := TemplateManager.NotifyFilter(n.ContactType, n.Topic, n.Message)
|
||||
p, err := TemplateManager.NotifyFilter(n.ContactType, n.Topic, n.Message, getTemplateLangFromCtx(ctx))
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
@@ -96,6 +99,7 @@ type SReceiver struct {
|
||||
|
||||
Email string `width:"64" nullable:"false" create:"optional" update:"user" get:"user" list:"user"`
|
||||
Mobile string `width:"16" nullable:"false" create:"optional" update:"user" get:"user" list:"user"`
|
||||
Lang string `width:"8" charset:"ascii" nullable:"false" list:"user" update:"user"`
|
||||
|
||||
// swagger:ignore
|
||||
EnabledEmail tristate.TriState `nullable:"false" default:"false" update:"user"`
|
||||
@@ -276,12 +280,14 @@ func (rm *SReceiverManager) ValidateCreateData(ctx context.Context, userCred mcc
|
||||
return input, httperrors.NewInputParameterError("invalid email")
|
||||
}
|
||||
// validate mobile
|
||||
if ok := regutils.MatchMobile(input.Mobile); len(input.Mobile) > 0 && !ok {
|
||||
if ok := LaxMobileRegexp.MatchString(input.Mobile); len(input.Mobile) > 0 && !ok {
|
||||
return input, httperrors.NewInputParameterError("invalid mobile")
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
var LaxMobileRegexp = regexp.MustCompile(`[0-9]{6,14}`)
|
||||
|
||||
func (r *SReceiver) IsEnabledContactType(ct string) (bool, error) {
|
||||
if utils.IsInStringArray(ct, AllOkContactTypes) {
|
||||
return true, nil
|
||||
@@ -678,7 +684,7 @@ func (r *SReceiver) ValidateUpdateData(ctx context.Context, userCred mcclient.To
|
||||
return input, httperrors.NewInputParameterError("invalid email")
|
||||
}
|
||||
// validate mobile
|
||||
if ok := len(input.Mobile) == 0 || regutils.MatchMobile(input.Mobile); !ok {
|
||||
if ok := len(input.Mobile) == 0 || LaxMobileRegexp.MatchString(input.Mobile); !ok {
|
||||
return input, httperrors.NewInputParameterError("invalid mobile")
|
||||
}
|
||||
return input, nil
|
||||
@@ -907,6 +913,51 @@ func (r *SReceiver) PerformDisable(ctx context.Context, userCred mcclient.TokenC
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *SReceiver) Sync(ctx context.Context) error {
|
||||
session := auth.GetAdminSessionWithInternal(ctx, "", "")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("scope", jsonutils.NewString("system"))
|
||||
params.Set("system", jsonutils.JSONTrue)
|
||||
data, err := modules.UsersV3.GetById(session, r.Id, params)
|
||||
if err != nil {
|
||||
jerr := err.(*httputils.JSONClientError)
|
||||
if jerr.Code == 404 {
|
||||
err := r.Delete(ctx, session.GetToken())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to delete receiver %s", r.Id)
|
||||
}
|
||||
return errors.Wrapf(errors.ErrNotFound, "no such receiver %s", r.Id)
|
||||
}
|
||||
return err
|
||||
}
|
||||
uname, _ := data.GetString("name")
|
||||
domainId, _ := data.GetString("domain_id")
|
||||
lang, _ := data.GetString("lang")
|
||||
_, err = db.Update(r, func() error {
|
||||
r.Name = uname
|
||||
r.DomainId = domainId
|
||||
r.Lang = lang
|
||||
return nil
|
||||
})
|
||||
return errors.Wrap(err, "unable to update")
|
||||
}
|
||||
|
||||
func (r *SReceiver) GetTemplateLang(ctx context.Context) (string, error) {
|
||||
if len(r.Lang) == 0 {
|
||||
err := r.Sync(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
log.Infof("lang: %s", r.Lang)
|
||||
lang, err := language.Parse(r.Lang)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to prase language %q", r.Lang)
|
||||
}
|
||||
tLang := notifyclientI18nTable.LookupByLang(lang, tempalteLang)
|
||||
return tLang, nil
|
||||
}
|
||||
|
||||
// Implemente interface EventHandler
|
||||
func (rm *SReceiverManager) OnAdd(obj *jsonutils.JSONDict) {
|
||||
// do nothing
|
||||
@@ -926,12 +977,14 @@ func (rm *SReceiverManager) OnUpdate(oldObj, newObj *jsonutils.JSONDict) {
|
||||
receiver := &receivers[0]
|
||||
uname, _ := newObj.GetString("name")
|
||||
domainId, _ := newObj.GetString("domain_id")
|
||||
if receiver.Name == uname && receiver.DomainId == domainId {
|
||||
lang, _ := newObj.GetString("lang")
|
||||
if receiver.Name == uname && receiver.DomainId == domainId && receiver.Lang == lang {
|
||||
return
|
||||
}
|
||||
_, err = db.Update(receiver, func() error {
|
||||
receiver.Name = uname
|
||||
receiver.DomainId = domainId
|
||||
receiver.Lang = lang
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -85,6 +85,7 @@ func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receiver.SetModelManager(ReceiverManager, &receiver)
|
||||
return &receiver, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
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/i18n"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
@@ -72,6 +73,7 @@ type STemplate struct {
|
||||
// title | content | remote
|
||||
TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user" update:"user"`
|
||||
Lang string `width:"8" charset:"ascii" nullable:"false" list:"user" update:"user"`
|
||||
Example string `nullable:"false" created:"required" get:"user" list:"user" update:"user"`
|
||||
}
|
||||
|
||||
@@ -88,32 +90,35 @@ func (tm *STemplateManager) defaultTemplate() ([]STemplate, error) {
|
||||
templates := make([]STemplate, 0, 4)
|
||||
|
||||
for _, templateType := range []string{"title", "content"} {
|
||||
contactType, topic := CONTACTTYPE_ALL, ""
|
||||
titleTemplatePath := fmt.Sprintf("%s/%s", templatePath, templateType)
|
||||
files, err := ioutil.ReadDir(titleTemplatePath)
|
||||
if err != nil {
|
||||
return templates, errors.Wrapf(err, "Read Dir '%s'", titleTemplatePath)
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
spliteName := strings.Split(file.Name(), ".")
|
||||
topic = spliteName[0]
|
||||
if len(spliteName) > 1 {
|
||||
contactType = spliteName[1]
|
||||
}
|
||||
fullPath := filepath.Join(titleTemplatePath, file.Name())
|
||||
content, err := ioutil.ReadFile(fullPath)
|
||||
for _, lang := range []string{api.TEMPLATE_LANG_CN, api.TEMPLATE_LANG_EN} {
|
||||
contactType, topic := CONTACTTYPE_ALL, ""
|
||||
titleTemplatePath := fmt.Sprintf("%s/%s@%s", templatePath, templateType, lang)
|
||||
files, err := ioutil.ReadDir(titleTemplatePath)
|
||||
if err != nil {
|
||||
return templates, err
|
||||
return templates, errors.Wrapf(err, "Read Dir '%s'", titleTemplatePath)
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
spliteName := strings.Split(file.Name(), ".")
|
||||
topic = spliteName[0]
|
||||
if len(spliteName) > 1 {
|
||||
contactType = spliteName[1]
|
||||
}
|
||||
fullPath := filepath.Join(titleTemplatePath, file.Name())
|
||||
content, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return templates, err
|
||||
}
|
||||
templates = append(templates, STemplate{
|
||||
ContactType: contactType,
|
||||
Topic: topic,
|
||||
Lang: lang,
|
||||
TemplateType: templateType,
|
||||
Content: string(content),
|
||||
})
|
||||
}
|
||||
templates = append(templates, STemplate{
|
||||
ContactType: contactType,
|
||||
Topic: topic,
|
||||
TemplateType: templateType,
|
||||
Content: string(content),
|
||||
})
|
||||
}
|
||||
}
|
||||
return templates, nil
|
||||
@@ -144,15 +149,43 @@ var (
|
||||
ForceInitType = []string{
|
||||
api.EMAIL,
|
||||
}
|
||||
notifyclientI18nTable = i18n.Table{}
|
||||
defaultLang = api.TEMPLATE_LANG_CN
|
||||
tempalteLang = "lang"
|
||||
)
|
||||
|
||||
func init() {
|
||||
notifyclientI18nTable.Set(tempalteLang, i18n.NewTableEntry().EN(api.TEMPLATE_LANG_EN).CN(api.TEMPLATE_LANG_CN))
|
||||
}
|
||||
|
||||
func getTemplateLangFromCtx(ctx context.Context) string {
|
||||
return notifyclientI18nTable.Lookup(ctx, tempalteLang)
|
||||
}
|
||||
|
||||
func (tm *STemplateManager) InitializeData() error {
|
||||
// init lang
|
||||
q := tm.Query().IsEmpty("lang")
|
||||
var noLangTemplates []STemplate
|
||||
err := db.FetchModelObjects(tm, q, &noLangTemplates)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to fetch templates")
|
||||
}
|
||||
for i := range noLangTemplates {
|
||||
t := &noLangTemplates[i]
|
||||
_, err := db.Update(t, func() error {
|
||||
t.Lang = defaultLang
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
templates, err := tm.defaultTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, template := range templates {
|
||||
q := tm.Query().Equals("contact_type", template.ContactType).Equals("topic", template.Topic).Equals("template_type", template.TemplateType)
|
||||
q := tm.Query().Equals("contact_type", template.ContactType).Equals("topic", template.Topic).Equals("template_type", template.TemplateType).Equals("lang", template.Lang)
|
||||
count, _ := q.CountWithError()
|
||||
if count > 0 && !utils.IsInStringArray(template.ContactType, ForceInitType) {
|
||||
continue
|
||||
@@ -195,10 +228,15 @@ func (tm *STemplateManager) InitializeData() error {
|
||||
|
||||
// NotifyFilter will return the title and content generated by corresponding template.
|
||||
// Local cache about common template will be considered in case of performance issues.
|
||||
func (tm *STemplateManager) NotifyFilter(contactType, topic, msg string) (params apis.SendParams, err error) {
|
||||
func (tm *STemplateManager) NotifyFilter(contactType, topic, msg, lang string) (params apis.SendParams, err error) {
|
||||
params.Topic = topic
|
||||
if len(lang) == 0 {
|
||||
params.Title = topic
|
||||
params.Message = msg
|
||||
return
|
||||
}
|
||||
templates := make([]STemplate, 0, 3)
|
||||
q := tm.Query().Equals("topic", strings.ToUpper(topic)).In("contact_type", []string{CONTACTTYPE_ALL, contactType})
|
||||
q := tm.Query().Equals("topic", strings.ToUpper(topic)).Equals("lang", lang).In("contact_type", []string{CONTACTTYPE_ALL, contactType})
|
||||
err = db.FetchModelObjects(tm, q, &templates)
|
||||
if errors.Cause(err) == sql.ErrNoRows || len(templates) == 0 {
|
||||
// no such template, return as is
|
||||
|
||||
@@ -31,7 +31,7 @@ type NotifyOption struct {
|
||||
InitNotificationScope int `help:"initialize data of notification with in InitNotificationScope hours" default:"100"`
|
||||
MaxSyncNotification int `help:"The max number of notification sync from old data source" default:"1000"`
|
||||
|
||||
VerifyExpireInterval int `help:"expire interval of verify message; minutes" default:"5"`
|
||||
VerifyExpireInterval int `help:"expire interval of verify message; minutes" default:"2"`
|
||||
VerifyValidInterval int `help:"valid interval of verify message; miniutes" default:"20"`
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/notify"
|
||||
notifyv2 "yunion.io/x/onecloud/pkg/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/models"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
@@ -107,22 +108,22 @@ func (self *SRpcService) StopAll() {
|
||||
}
|
||||
|
||||
// Send call the corresponding rpc server to send messager.
|
||||
func (self *SRpcService) Send(ctx context.Context, contactType, contact, topic, msg, priority string) error {
|
||||
func (self *SRpcService) Send(ctx context.Context, p notify.SSendParams) error {
|
||||
|
||||
args, err := self.templateStore.NotifyFilter(contactType, topic, msg)
|
||||
args, err := self.templateStore.NotifyFilter(p.ContactType, p.Topic, p.Message, p.Lang)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "templateStore.NotifyFilter")
|
||||
}
|
||||
|
||||
args.Contact = contact
|
||||
args.Priority = priority
|
||||
args.Contact = p.Contact
|
||||
args.Priority = p.Priority
|
||||
|
||||
f := func(service *apis.SendNotificationClient) (interface{}, error) {
|
||||
log.Debugf("send one")
|
||||
return service.Send(ctx, &args)
|
||||
}
|
||||
|
||||
_, err = self.execute(ctx, f, contactType)
|
||||
_, err = self.execute(ctx, f, p.ContactType)
|
||||
if err != nil {
|
||||
s, ok := status.FromError(err)
|
||||
if !ok {
|
||||
@@ -133,14 +134,14 @@ func (self *SRpcService) Send(ctx context.Context, contactType, contact, topic,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRpcService) BatchSend(ctx context.Context, contacts []string, contactType, topic, message, priority string) ([]*apis.FailedRecord, error) {
|
||||
args, err := self.templateStore.NotifyFilter(contactType, topic, message)
|
||||
func (self *SRpcService) BatchSend(ctx context.Context, p notify.SBatchSendParams) ([]*apis.FailedRecord, error) {
|
||||
args, err := self.templateStore.NotifyFilter(p.ContactType, p.Topic, p.Message, p.Lang)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "templateStore.NotifyFilter")
|
||||
}
|
||||
|
||||
batchSendParams := apis.BatchSendParams{
|
||||
Contacts: contacts,
|
||||
Contacts: p.Contacts,
|
||||
Title: args.Title,
|
||||
Message: args.Message,
|
||||
Priority: args.Priority,
|
||||
@@ -151,7 +152,7 @@ func (self *SRpcService) BatchSend(ctx context.Context, contacts []string, conta
|
||||
return service.BatchSend(ctx, &batchSendParams)
|
||||
}
|
||||
|
||||
ret, err := self.execute(ctx, f, contactType)
|
||||
ret, err := self.execute(ctx, f, p.ContactType)
|
||||
if err != nil {
|
||||
s, ok := status.FromError(err)
|
||||
if !ok {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
apis "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -71,6 +72,8 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
|
||||
// build contactMap
|
||||
contactMap := make(map[string]*models.SReceiverNotification)
|
||||
contactMapEn := make(map[string]*models.SReceiverNotification)
|
||||
contactmapCn := make(map[string]*models.SReceiverNotification)
|
||||
for i := range rnsWithReceiver {
|
||||
if len(rnsWithReceiver[i].ReceiverID) == 0 {
|
||||
contactMap[rnsWithReceiver[i].Contact] = rnsWithReceiver[i]
|
||||
@@ -114,44 +117,73 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
sendFail(rnsWithReceiver[i], reason)
|
||||
continue
|
||||
}
|
||||
contactMap[contact] = rnsWithReceiver[i]
|
||||
lang, err := receiver.GetTemplateLang(ctx)
|
||||
if err != nil {
|
||||
reason := fmt.Sprintf("fail to GetTemplateLang: %s", err.Error())
|
||||
sendFail(rnsWithReceiver[i], reason)
|
||||
continue
|
||||
}
|
||||
switch lang {
|
||||
case "":
|
||||
contactMap[contact] = rnsWithReceiver[i]
|
||||
case apis.TEMPLATE_LANG_EN:
|
||||
contactMapEn[contact] = rnsWithReceiver[i]
|
||||
case apis.TEMPLATE_LANG_CN:
|
||||
contactmapCn[contact] = rnsWithReceiver[i]
|
||||
}
|
||||
}
|
||||
|
||||
for i := range rnsWithoutReceiver {
|
||||
contactMap[rnsWithoutReceiver[i].Contact] = rnsWithoutReceiver[i]
|
||||
}
|
||||
|
||||
// set status before send
|
||||
now := time.Now()
|
||||
contacts := make([]string, 0, len(contactMap))
|
||||
for c, rn := range contactMap {
|
||||
rn.BeforeSend(ctx, now)
|
||||
contacts = append(contacts, c)
|
||||
}
|
||||
|
||||
// send
|
||||
ret, err := models.NotifyService.BatchSend(ctx, contacts, notification.ContactType, notification.Topic, notification.Message, notification.Priority)
|
||||
if err != nil {
|
||||
for _, rn := range contactMap {
|
||||
rn.AfterSend(ctx, false, err.Error())
|
||||
var contactLen int
|
||||
for lang, contactMap := range map[string]map[string]*models.SReceiverNotification{
|
||||
"": contactMap,
|
||||
apis.TEMPLATE_LANG_CN: contactmapCn,
|
||||
apis.TEMPLATE_LANG_EN: contactMapEn,
|
||||
} {
|
||||
if len(contactMap) == 0 {
|
||||
continue
|
||||
}
|
||||
// set status before send
|
||||
now := time.Now()
|
||||
contacts := make([]string, 0, len(contactMap))
|
||||
for c, rn := range contactMap {
|
||||
rn.BeforeSend(ctx, now)
|
||||
contacts = append(contacts, c)
|
||||
}
|
||||
failedRecord = append(failedRecord, fmt.Sprintf("others: %s", err.Error()))
|
||||
self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
|
||||
return
|
||||
}
|
||||
|
||||
// check result
|
||||
for _, fd := range ret {
|
||||
rn := contactMap[fd.Contact]
|
||||
rn.AfterSend(ctx, false, fd.Reason)
|
||||
failedRecord = append(failedRecord, fmt.Sprintf("%s: %s", rn.ReceiverID, fd.Reason))
|
||||
delete(contactMap, fd.Contact)
|
||||
contactLen += len(contacts)
|
||||
|
||||
p := notify.SBatchSendParams{
|
||||
Contacts: contacts,
|
||||
ContactType: notification.ContactType,
|
||||
Topic: notification.Topic,
|
||||
Message: notification.Message,
|
||||
Priority: notification.Priority,
|
||||
Lang: lang,
|
||||
}
|
||||
// send
|
||||
fds, err := models.NotifyService.BatchSend(ctx, p)
|
||||
if err != nil {
|
||||
for _, rn := range contactMap {
|
||||
sendFail(rn, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
// check result
|
||||
for _, fd := range fds {
|
||||
rn := contactMap[fd.Contact]
|
||||
sendFail(rn, fd.Reason)
|
||||
delete(contactMap, fd.Contact)
|
||||
}
|
||||
// after send for successful notify
|
||||
for _, rn := range contactMap {
|
||||
rn.AfterSend(ctx, true, "")
|
||||
}
|
||||
}
|
||||
// after send for successful notify
|
||||
for _, rn := range contactMap {
|
||||
rn.AfterSend(ctx, true, "")
|
||||
}
|
||||
if len(failedRecord) > 0 && len(failedRecord) == len(contacts) {
|
||||
if len(failedRecord) > 0 && len(failedRecord) == contactLen {
|
||||
self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -66,7 +67,19 @@ func (self *VerificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
default:
|
||||
// no way
|
||||
}
|
||||
err = models.NotifyService.Send(ctx, contactType, contact, "verify", message, "")
|
||||
tLang, err := receiver.GetTemplateLang(ctx)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, receiver, fmt.Sprintf("unable to GetTemplateLang for receiver %q: %v", receiver.Id, err))
|
||||
}
|
||||
sendP := notify.SSendParams{
|
||||
ContactType: contactType,
|
||||
Contact: contact,
|
||||
Message: message,
|
||||
Topic: "verify",
|
||||
Priority: "",
|
||||
Lang: tLang,
|
||||
}
|
||||
err = models.NotifyService.Send(ctx, sendP)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, receiver, err.Error())
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user