aliyun.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package regiondrivers
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "time"
  20. "unicode"
  21. "yunion.io/x/cloudmux/pkg/cloudprovider"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/billing"
  25. "yunion.io/x/pkg/util/regutils"
  26. "yunion.io/x/pkg/utils"
  27. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  28. api "yunion.io/x/onecloud/pkg/apis/compute"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  32. "yunion.io/x/onecloud/pkg/compute/models"
  33. "yunion.io/x/onecloud/pkg/httperrors"
  34. "yunion.io/x/onecloud/pkg/mcclient"
  35. "yunion.io/x/onecloud/pkg/util/choices"
  36. "yunion.io/x/onecloud/pkg/util/seclib2"
  37. )
  38. type SAliyunRegionDriver struct {
  39. SManagedVirtualizationRegionDriver
  40. }
  41. func init() {
  42. driver := SAliyunRegionDriver{}
  43. models.RegisterRegionDriver(&driver)
  44. }
  45. func (self *SAliyunRegionDriver) GetProvider() string {
  46. return api.CLOUD_PROVIDER_ALIYUN
  47. }
  48. func (self *SAliyunRegionDriver) ValidateCreateLoadbalancerData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.LoadbalancerCreateInput) (*api.LoadbalancerCreateInput, error) {
  49. if len(input.LoadbalancerSpec) == 0 {
  50. input.LoadbalancerSpec = api.LB_ALIYUN_SPEC_SHAREABLE
  51. }
  52. if !utils.IsInStringArray(input.LoadbalancerSpec, api.LB_ALIYUN_SPECS) {
  53. return nil, httperrors.NewInputParameterError("invalid loadbalancer_spec %s", input.LoadbalancerSpec)
  54. }
  55. if input.ChargeType == api.LB_CHARGE_TYPE_BY_BANDWIDTH {
  56. if input.EgressMbps < 1 || input.EgressMbps > 5000 {
  57. return nil, httperrors.NewInputParameterError("egress_mbps shoud be 1-5000 mbps")
  58. }
  59. }
  60. if input.AddressType == api.LB_ADDR_TYPE_INTRANET && input.ChargeType == api.LB_CHARGE_TYPE_BY_BANDWIDTH {
  61. return nil, httperrors.NewUnsupportOperationError("intranet loadbalancer not support bandwidth charge type")
  62. }
  63. return self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerData(ctx, userCred, ownerId, input)
  64. }
  65. func (self *SAliyunRegionDriver) GetBackendStatusForAdd() []string {
  66. return []string{api.VM_RUNNING}
  67. }
  68. func (self *SAliyunRegionDriver) ValidateCreateLoadbalancerBackendGroupData(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, input *api.LoadbalancerBackendGroupCreateInput) (*api.LoadbalancerBackendGroupCreateInput, error) {
  69. switch input.Type {
  70. case "", api.LB_BACKENDGROUP_TYPE_NORMAL:
  71. break
  72. case api.LB_BACKENDGROUP_TYPE_MASTER_SLAVE:
  73. if len(input.Backends) != 2 {
  74. return nil, httperrors.NewInputParameterError("master slave backendgorup must contain two backend")
  75. }
  76. default:
  77. return nil, httperrors.NewInputParameterError("Unsupport backendgorup type %s", input.Type)
  78. }
  79. for _, backend := range input.Backends {
  80. if len(backend.ExternalId) == 0 {
  81. return nil, httperrors.NewInputParameterError("invalid guest %s", backend.Name)
  82. }
  83. if backend.Weight < 0 || backend.Weight > 100 {
  84. return nil, httperrors.NewInputParameterError("Aliyun instance weight must be in the range of 0 ~ 100")
  85. }
  86. }
  87. return input, nil
  88. }
  89. func (self *SAliyunRegionDriver) ValidateCreateLoadbalancerBackendData(ctx context.Context, userCred mcclient.TokenCredential,
  90. lb *models.SLoadbalancer, lbbg *models.SLoadbalancerBackendGroup,
  91. input *api.LoadbalancerBackendCreateInput) (*api.LoadbalancerBackendCreateInput, error) {
  92. return self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerBackendData(ctx, userCred, lb, lbbg, input)
  93. }
  94. func (self *SAliyunRegionDriver) ValidateUpdateLoadbalancerBackendData(ctx context.Context, userCred mcclient.TokenCredential, lbbg *models.SLoadbalancerBackendGroup, input *api.LoadbalancerBackendUpdateInput) (*api.LoadbalancerBackendUpdateInput, error) {
  95. switch lbbg.Type {
  96. case api.LB_BACKENDGROUP_TYPE_DEFAULT:
  97. if input.Port != nil {
  98. return nil, httperrors.NewInputParameterError("%s backend group not support change port", lbbg.Type)
  99. }
  100. case api.LB_BACKENDGROUP_TYPE_NORMAL:
  101. return input, nil
  102. case api.LB_BACKENDGROUP_TYPE_MASTER_SLAVE:
  103. if input.Port != nil || input.Weight != nil {
  104. return input, httperrors.NewInputParameterError("%s backend group not support change port or weight", lbbg.Type)
  105. }
  106. default:
  107. return nil, httperrors.NewInputParameterError("Unknown backend group type %s", lbbg.Type)
  108. }
  109. return input, nil
  110. }
  111. func (self *SAliyunRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.LoadbalancerListenerRuleCreateInput) (*api.LoadbalancerListenerRuleCreateInput, error) {
  112. return input, nil
  113. }
  114. func (self *SAliyunRegionDriver) ValidateUpdateLoadbalancerListenerRuleData(ctx context.Context, userCred mcclient.TokenCredential, input *api.LoadbalancerListenerRuleUpdateInput) (*api.LoadbalancerListenerRuleUpdateInput, error) {
  115. return input, nil
  116. }
  117. func (self *SAliyunRegionDriver) ValidateCreateLoadbalancerListenerData(ctx context.Context, userCred mcclient.TokenCredential,
  118. ownerId mcclient.IIdentityProvider, input *api.LoadbalancerListenerCreateInput,
  119. lb *models.SLoadbalancer, lbbg *models.SLoadbalancerBackendGroup) (*api.LoadbalancerListenerCreateInput, error) {
  120. if input.ClientReqeustTimeout < 1 || input.ClientReqeustTimeout > 600 {
  121. input.ClientReqeustTimeout = 10
  122. }
  123. if input.ClientIdleTimeout < 1 || input.ClientIdleTimeout > 600 {
  124. input.ClientIdleTimeout = 90
  125. }
  126. if input.BackendConnectTimeout < 1 || input.BackendConnectTimeout > 180 {
  127. input.BackendConnectTimeout = 5
  128. }
  129. if input.BackendIdleTimeout < 1 || input.BackendIdleTimeout > 600 {
  130. input.BackendIdleTimeout = 90
  131. }
  132. if len(input.HealthCheckDomain) > 80 {
  133. return nil, httperrors.NewInputParameterError("health_check_domain must be in the range of 1 ~ 80")
  134. }
  135. return input, nil
  136. }
  137. func (self *SAliyunRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx context.Context, userCred mcclient.TokenCredential,
  138. lblis *models.SLoadbalancerListener, input *api.LoadbalancerListenerUpdateInput) (*api.LoadbalancerListenerUpdateInput, error) {
  139. lb, err := lblis.GetLoadbalancer()
  140. if err != nil {
  141. return nil, errors.Wrapf(err, "GetLoadbalancer")
  142. }
  143. if input.Scheduler != nil && utils.IsInStringArray(*input.Scheduler, []string{api.LB_SCHEDULER_SCH, api.LB_SCHEDULER_TCH, api.LB_SCHEDULER_QCH}) {
  144. if len(lb.LoadbalancerSpec) == 0 {
  145. return nil, httperrors.NewInputParameterError("The specified Scheduler %v is invalid for performance sharing loadbalancer", input.Scheduler)
  146. }
  147. region, err := lb.GetRegion()
  148. if err != nil {
  149. return nil, errors.Wrapf(err, "GetRegion")
  150. }
  151. supportRegions := []string{}
  152. for region := range map[string]string{
  153. "ap-northeast-1": "东京",
  154. "ap-southeast-2": "悉尼",
  155. "ap-southeast-3": "吉隆坡",
  156. "ap-southeast-5": "雅加达",
  157. "eu-frankfurt": "法兰克福",
  158. "na-siliconvalley": "硅谷",
  159. "us-east-1": "弗吉利亚",
  160. "me-east-1": "迪拜",
  161. "cn-huhehaote": "呼和浩特",
  162. } {
  163. supportRegions = append(supportRegions, "Aliyun/"+region)
  164. }
  165. if !utils.IsInStringArray(region.ExternalId, supportRegions) {
  166. return nil, httperrors.NewUnsupportOperationError("cloudregion %s(%s) not support %v scheduler", region.Name, region.Id, input.Scheduler)
  167. }
  168. }
  169. if input.HealthCheckDomain != nil && len(*input.HealthCheckDomain) > 80 {
  170. return nil, httperrors.NewInputParameterError("health_check_domain must be in the range of 1 ~ 80")
  171. }
  172. return input, nil
  173. }
  174. func (self *SAliyunRegionDriver) ValidateCreateSnapshotData(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, storage *models.SStorage, input *api.SnapshotCreateInput) error {
  175. if strings.HasPrefix(input.Name, "auto") || strings.HasPrefix(input.Name, "http://") || strings.HasPrefix(input.Name, "https://") {
  176. return httperrors.NewBadRequestError(
  177. "Snapshot for %s name can't start with auto, http:// or https://", self.GetProvider())
  178. }
  179. return nil
  180. }
  181. func (self *SAliyunRegionDriver) IsSecurityGroupBelongVpc() bool {
  182. return true
  183. }
  184. func (self *SAliyunRegionDriver) ValidateDBInstanceRecovery(ctx context.Context, userCred mcclient.TokenCredential, instance *models.SDBInstance, backup *models.SDBInstanceBackup, input api.SDBInstanceRecoveryConfigInput) error {
  185. if !utils.IsInStringArray(instance.Engine, []string{api.DBINSTANCE_TYPE_MYSQL, api.DBINSTANCE_TYPE_SQLSERVER}) {
  186. return httperrors.NewNotSupportedError("Aliyun %s not support recovery", instance.Engine)
  187. }
  188. if instance.Engine == api.DBINSTANCE_TYPE_MYSQL {
  189. if backup.DBInstanceId != instance.Id {
  190. return httperrors.NewUnsupportOperationError("Aliyun %s only support recover from it self backups", instance.Engine)
  191. }
  192. if !((utils.IsInStringArray(instance.EngineVersion, []string{"8.0", "5.7"}) &&
  193. instance.StorageType == api.ALIYUN_DBINSTANCE_STORAGE_TYPE_LOCAL_SSD &&
  194. instance.Category == api.ALIYUN_DBINSTANCE_CATEGORY_HA) || (instance.EngineVersion == "5.6" && instance.Category == api.ALIYUN_DBINSTANCE_CATEGORY_HA)) {
  195. return httperrors.NewUnsupportOperationError("Aliyun %s only 8.0 and 5.7 high_availability local_ssd or 5.6 high_availability support recovery from it self backups", instance.Engine)
  196. }
  197. }
  198. if len(input.Databases) == 0 {
  199. return httperrors.NewMissingParameterError("databases")
  200. }
  201. return nil
  202. }
  203. func (self *SAliyunRegionDriver) ValidateCreateDBInstanceData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.DBInstanceCreateInput, skus []models.SDBInstanceSku, network *models.SNetwork) (api.DBInstanceCreateInput, error) {
  204. if input.BillingType == billing_api.BILLING_TYPE_PREPAID && len(input.MasterInstanceId) > 0 {
  205. return input, httperrors.NewInputParameterError("slave dbinstance not support prepaid billing type")
  206. }
  207. if network != nil {
  208. wire, _ := network.GetWire()
  209. if wire == nil {
  210. return input, httperrors.NewGeneralError(fmt.Errorf("failed to found wire for network %s(%s)", network.Name, network.Id))
  211. }
  212. zone, _ := wire.GetZone()
  213. if zone == nil {
  214. return input, httperrors.NewGeneralError(fmt.Errorf("failed to found zone for wire %s(%s)", wire.Name, wire.Id))
  215. }
  216. match := false
  217. for _, sku := range skus {
  218. if utils.IsInStringArray(zone.Id, []string{sku.Zone1, sku.Zone2, sku.Zone3}) {
  219. match = true
  220. break
  221. }
  222. }
  223. if !match {
  224. return input, httperrors.NewInputParameterError("failed to match any skus in the network %s(%s) zone %s(%s)", network.Name, network.Id, zone.Name, zone.Id)
  225. }
  226. }
  227. var master *models.SDBInstance
  228. var slaves []models.SDBInstance
  229. var err error
  230. if len(input.MasterInstanceId) > 0 {
  231. _master, _ := models.DBInstanceManager.FetchById(input.MasterInstanceId)
  232. master = _master.(*models.SDBInstance)
  233. slaves, err = master.GetSlaveDBInstances()
  234. if err != nil {
  235. return input, httperrors.NewGeneralError(err)
  236. }
  237. switch master.Engine {
  238. case api.DBINSTANCE_TYPE_MYSQL:
  239. switch master.EngineVersion {
  240. case "5.6":
  241. break
  242. case "5.7", "8.0":
  243. if master.Category != api.ALIYUN_DBINSTANCE_CATEGORY_HA {
  244. return input, httperrors.NewInputParameterError("Not support create readonly dbinstance for MySQL %s %s", master.EngineVersion, master.Category)
  245. }
  246. if master.StorageType != api.ALIYUN_DBINSTANCE_STORAGE_TYPE_LOCAL_SSD {
  247. return input, httperrors.NewInputParameterError("Not support create readonly dbinstance for MySQL %s %s with storage type %s, only support %s", master.EngineVersion, master.Category, master.StorageType, api.ALIYUN_DBINSTANCE_STORAGE_TYPE_LOCAL_SSD)
  248. }
  249. default:
  250. return input, httperrors.NewInputParameterError("Not support create readonly dbinstance for MySQL %s", master.EngineVersion)
  251. }
  252. case api.DBINSTANCE_TYPE_SQLSERVER:
  253. if master.Category != api.ALIYUN_DBINSTANCE_CATEGORY_ALWAYSON || master.EngineVersion != "2017_ent" {
  254. return input, httperrors.NewInputParameterError("SQL Server only support create readonly dbinstance for 2017_ent")
  255. }
  256. if len(slaves) >= 7 {
  257. return input, httperrors.NewInputParameterError("SQL Server cannot have more than seven read-only dbinstances")
  258. }
  259. default:
  260. return input, httperrors.NewInputParameterError("Not support create readonly dbinstance with master dbinstance engine %s", master.Engine)
  261. }
  262. }
  263. switch input.Engine {
  264. case api.DBINSTANCE_TYPE_MYSQL:
  265. if input.VmemSizeMb/1024 >= 64 && len(slaves) >= 10 {
  266. return input, httperrors.NewInputParameterError("Master dbinstance memory ≥64GB, up to 10 read-only instances are allowed to be created")
  267. } else if input.VmemSizeMb/1024 < 64 && len(slaves) >= 5 {
  268. return input, httperrors.NewInputParameterError("Master dbinstance memory <64GB, up to 5 read-only instances are allowed to be created")
  269. }
  270. case api.DBINSTANCE_TYPE_SQLSERVER:
  271. if input.Category == api.ALIYUN_DBINSTANCE_CATEGORY_ALWAYSON {
  272. vpc, _ := network.GetVpc()
  273. count, err := vpc.GetNetworkCount()
  274. if err != nil {
  275. return input, httperrors.NewGeneralError(err)
  276. }
  277. if count < 2 {
  278. return input, httperrors.NewInputParameterError("At least two networks are required under vpc %s(%s) with aliyun %s(%s)", vpc.Name, vpc.Id, input.Engine, input.Category)
  279. }
  280. }
  281. }
  282. if len(input.Name) > 0 {
  283. if strings.HasPrefix(input.Description, "http://") || strings.HasPrefix(input.Description, "https://") {
  284. return input, httperrors.NewInputParameterError("Description can not start with http:// or https://")
  285. }
  286. }
  287. return input, nil
  288. }
  289. func (self *SAliyunRegionDriver) IsSupportedBillingCycle(bc billing.SBillingCycle, resource string) bool {
  290. switch resource {
  291. case models.DBInstanceManager.KeywordPlural(),
  292. models.ElasticcacheManager.KeywordPlural(),
  293. models.NatGatewayManager.KeywordPlural(),
  294. models.FileSystemManager.KeywordPlural():
  295. years := bc.GetYears()
  296. months := bc.GetMonths()
  297. if (years >= 1 && years <= 3) || (months >= 1 && months <= 9) {
  298. return true
  299. }
  300. }
  301. return false
  302. }
  303. func (self *SAliyunRegionDriver) ValidateCreateDBInstanceAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, instance *models.SDBInstance, input api.DBInstanceAccountCreateInput) (api.DBInstanceAccountCreateInput, error) {
  304. if len(input.Name) < 2 || len(input.Name) > 16 {
  305. return input, httperrors.NewInputParameterError("Aliyun DBInstance account name length shoud be 2~16 characters")
  306. }
  307. DENY_KEY := map[string][]string{
  308. api.DBINSTANCE_TYPE_MYSQL: api.ALIYUN_MYSQL_DENY_KEYWORKD,
  309. api.DBINSTANCE_TYPE_SQLSERVER: api.ALIYUN_SQL_SERVER_DENY_KEYWORD,
  310. }
  311. if keys, ok := DENY_KEY[instance.Engine]; ok && utils.IsInStringArray(input.Name, keys) {
  312. return input, httperrors.NewInputParameterError("%s is reserved for aliyun %s, please use another", input.Name, instance.Engine)
  313. }
  314. for i, s := range input.Name {
  315. if !unicode.IsLetter(s) && !unicode.IsDigit(s) && s != '_' {
  316. return input, httperrors.NewInputParameterError("invalid character %s for account name", string(s))
  317. }
  318. if s == '_' && (i == 0 || i == len(input.Name)) {
  319. return input, httperrors.NewInputParameterError("account name can not start or end with _")
  320. }
  321. }
  322. for _, privilege := range input.Privileges {
  323. err := self.ValidateDBInstanceAccountPrivilege(ctx, userCred, instance, input.Name, privilege.Privilege)
  324. if err != nil {
  325. return input, err
  326. }
  327. }
  328. return input, nil
  329. }
  330. func (self *SAliyunRegionDriver) ValidateCreateDBInstanceDatabaseData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, instance *models.SDBInstance, input api.DBInstanceDatabaseCreateInput) (api.DBInstanceDatabaseCreateInput, error) {
  331. if len(input.CharacterSet) == 0 {
  332. return input, httperrors.NewMissingParameterError("character_set")
  333. }
  334. for _, account := range input.Accounts {
  335. err := self.ValidateDBInstanceAccountPrivilege(ctx, userCred, instance, account.Account, account.Privilege)
  336. if err != nil {
  337. return input, err
  338. }
  339. }
  340. return input, nil
  341. }
  342. func (self *SAliyunRegionDriver) ValidateCreateDBInstanceBackupData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, instance *models.SDBInstance, input api.DBInstanceBackupCreateInput) (api.DBInstanceBackupCreateInput, error) {
  343. return input, nil
  344. }
  345. func (self *SAliyunRegionDriver) ValidateDBInstanceAccountPrivilege(ctx context.Context, userCred mcclient.TokenCredential, instance *models.SDBInstance, account string, privilege string) error {
  346. switch privilege {
  347. case api.DATABASE_PRIVILEGE_RW:
  348. case api.DATABASE_PRIVILEGE_R:
  349. case api.DATABASE_PRIVILEGE_DDL, api.DATABASE_PRIVILEGE_DML:
  350. if instance.Engine != api.DBINSTANCE_TYPE_MYSQL && instance.Engine != api.DBINSTANCE_TYPE_MARIADB {
  351. return httperrors.NewInputParameterError("%s only support aliyun %s or %s", privilege, api.DBINSTANCE_TYPE_MARIADB, api.DBINSTANCE_TYPE_MYSQL)
  352. }
  353. case api.DATABASE_PRIVILEGE_OWNER:
  354. if instance.Engine != api.DBINSTANCE_TYPE_SQLSERVER {
  355. return httperrors.NewInputParameterError("%s only support aliyun %s", privilege, api.DBINSTANCE_TYPE_SQLSERVER)
  356. }
  357. default:
  358. return httperrors.NewInputParameterError("Unknown privilege %s", privilege)
  359. }
  360. return nil
  361. }
  362. func (self *SAliyunRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
  363. if !utils.IsInStringArray(input.Engine, []string{"redis", "memcache"}) {
  364. return nil, httperrors.NewInputParameterError("invalid engine %s", input.Engine)
  365. }
  366. return self.SManagedVirtualizationRegionDriver.ValidateCreateElasticcacheData(ctx, userCred, ownerId, input)
  367. }
  368. func (self *SAliyunRegionDriver) ValidateCreateElasticcacheAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
  369. elasticCacheV := validators.NewModelIdOrNameValidator("elasticcache", "elasticcache", ownerId)
  370. accountTypeV := validators.NewStringChoicesValidator("account_type", choices.NewChoices("normal", "admin")).Default("normal")
  371. accountPrivilegeV := validators.NewStringChoicesValidator("account_privilege", choices.NewChoices("read", "write", "repl"))
  372. keyV := map[string]validators.IValidator{
  373. "elasticcache": elasticCacheV,
  374. "account_type": accountTypeV,
  375. "account_privilege": accountPrivilegeV.Default("read"),
  376. }
  377. for _, v := range keyV {
  378. if err := v.Validate(ctx, data); err != nil {
  379. return nil, err
  380. }
  381. }
  382. passwd, _ := data.GetString("password")
  383. err := seclib2.ValidatePassword(passwd)
  384. if err != nil {
  385. return nil, httperrors.NewWeakPasswordError()
  386. }
  387. if accountPrivilegeV.Value == "repl" && elasticCacheV.Model.(*models.SElasticcache).EngineVersion != "4.0" {
  388. return nil, httperrors.NewInputParameterError("account_privilege %s only support redis version 4.0",
  389. accountPrivilegeV.Value)
  390. }
  391. return self.SManagedVirtualizationRegionDriver.ValidateCreateElasticcacheAccountData(ctx, userCred, ownerId, data)
  392. }
  393. func (self *SAliyunRegionDriver) RequestCreateElasticcacheAccount(ctx context.Context, userCred mcclient.TokenCredential, ea *models.SElasticcacheAccount, task taskman.ITask) error {
  394. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  395. _ec, err := db.FetchById(models.ElasticcacheManager, ea.ElasticcacheId)
  396. if err != nil {
  397. return nil, errors.Wrap(nil, "aliyunRegionDriver.CreateElasticcacheAccount.GetElasticcache")
  398. }
  399. ec := _ec.(*models.SElasticcache)
  400. iregion, err := ec.GetIRegion(ctx)
  401. if err != nil {
  402. return nil, errors.Wrap(nil, "aliyunRegionDriver.CreateElasticcacheAccount.GetIRegion")
  403. }
  404. params, err := ea.GetCreateAliyunElasticcacheAccountParams()
  405. if err != nil {
  406. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.GetCreateAliyunElasticcacheAccountParams")
  407. }
  408. iec, err := iregion.GetIElasticcacheById(ec.GetExternalId())
  409. if err != nil {
  410. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.GetIElasticcacheById")
  411. }
  412. iea, err := iec.CreateAccount(params)
  413. if err != nil {
  414. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.CreateAccount")
  415. }
  416. ea.SetModelManager(models.ElasticcacheAccountManager, ea)
  417. if err := db.SetExternalId(ea, userCred, iea.GetGlobalId()); err != nil {
  418. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.SetExternalId")
  419. }
  420. err = cloudprovider.WaitStatusWithDelay(iea, api.ELASTIC_CACHE_ACCOUNT_STATUS_AVAILABLE, 3*time.Second, 3*time.Second, 180*time.Second)
  421. if err != nil {
  422. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.WaitStatusWithDelay")
  423. }
  424. if err = ea.SyncWithCloudElasticcacheAccount(ctx, userCred, iea); err != nil {
  425. return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcacheAccount.SyncWithCloudElasticcache")
  426. }
  427. return nil, nil
  428. })
  429. return nil
  430. }
  431. func (self *SAliyunRegionDriver) RequestCreateElasticcacheBackup(ctx context.Context, userCred mcclient.TokenCredential, eb *models.SElasticcacheBackup, task taskman.ITask) error {
  432. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  433. _ec, err := db.FetchById(models.ElasticcacheManager, eb.ElasticcacheId)
  434. if err != nil {
  435. return nil, errors.Wrap(nil, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.GetElasticcache")
  436. }
  437. ec := _ec.(*models.SElasticcache)
  438. iregion, err := ec.GetIRegion(ctx)
  439. if err != nil {
  440. return nil, errors.Wrap(nil, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.GetIRegion")
  441. }
  442. iec, err := iregion.GetIElasticcacheById(ec.GetExternalId())
  443. if err != nil {
  444. return nil, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.GetIElasticcacheById")
  445. }
  446. oBackups, err := iec.GetICloudElasticcacheBackups()
  447. if err != nil {
  448. return nil, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.GetICloudElasticcacheBackups")
  449. }
  450. backupIds := []string{}
  451. for i := range oBackups {
  452. backupIds = append(backupIds, oBackups[i].GetGlobalId())
  453. }
  454. _, err = iec.CreateBackup("")
  455. if err != nil {
  456. return nil, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.CreateBackup")
  457. }
  458. var ieb cloudprovider.ICloudElasticcacheBackup
  459. cloudprovider.Wait(30*time.Second, 1800*time.Second, func() (b bool, e error) {
  460. backups, err := iec.GetICloudElasticcacheBackups()
  461. if err != nil {
  462. return false, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.WaitCreated")
  463. }
  464. for i := range backups {
  465. if !utils.IsInStringArray(backups[i].GetGlobalId(), backupIds) && backups[i].GetStatus() == api.ELASTIC_CACHE_BACKUP_STATUS_SUCCESS {
  466. ieb = backups[i]
  467. return true, nil
  468. }
  469. }
  470. return false, nil
  471. })
  472. eb.SetModelManager(models.ElasticcacheBackupManager, eb)
  473. if err := db.SetExternalId(eb, userCred, ieb.GetGlobalId()); err != nil {
  474. return nil, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.SetExternalId")
  475. }
  476. if err := eb.SyncWithCloudElasticcacheBackup(ctx, userCred, ieb); err != nil {
  477. return nil, errors.Wrap(err, "managedVirtualizationRegionDriver.CreateElasticcacheBackup.SyncWithCloudElasticcacheBackup")
  478. }
  479. return nil, nil
  480. })
  481. return nil
  482. }
  483. func (self *SAliyunRegionDriver) RequestElasticcacheAccountResetPassword(ctx context.Context, userCred mcclient.TokenCredential, ea *models.SElasticcacheAccount, task taskman.ITask) error {
  484. iregion, err := ea.GetIRegion(ctx)
  485. if err != nil {
  486. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.GetIRegion")
  487. }
  488. _ec, err := db.FetchById(models.ElasticcacheManager, ea.ElasticcacheId)
  489. if err != nil {
  490. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.FetchById")
  491. }
  492. ec := _ec.(*models.SElasticcache)
  493. iec, err := iregion.GetIElasticcacheById(ec.GetExternalId())
  494. if errors.Cause(err) == cloudprovider.ErrNotFound {
  495. return nil
  496. } else if err != nil {
  497. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.GetIElasticcacheById")
  498. }
  499. iea, err := iec.GetICloudElasticcacheAccount(ea.GetExternalId())
  500. if err != nil {
  501. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.GetICloudElasticcacheBackup")
  502. }
  503. data := task.GetParams()
  504. if data == nil {
  505. return errors.Wrap(fmt.Errorf("data is nil"), "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.GetParams")
  506. }
  507. input, err := ea.GetUpdateAliyunElasticcacheAccountParams(*data)
  508. if err != nil {
  509. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.GetUpdateAliyunElasticcacheAccountParams")
  510. }
  511. err = iea.UpdateAccount(input)
  512. if err != nil {
  513. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.UpdateAccount")
  514. }
  515. if input.Password != nil {
  516. err = ea.SavePassword(*input.Password)
  517. if err != nil {
  518. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.SavePassword")
  519. }
  520. }
  521. err = cloudprovider.WaitStatusWithDelay(iea, api.ELASTIC_CACHE_ACCOUNT_STATUS_AVAILABLE, 10*time.Second, 5*time.Second, 60*time.Second)
  522. if err != nil {
  523. return errors.Wrap(err, "aliyunRegionDriver.RequestElasticcacheAccountResetPassword.WaitStatusWithDelay")
  524. }
  525. return ea.SyncWithCloudElasticcacheAccount(ctx, userCred, iea)
  526. }
  527. func (self *SAliyunRegionDriver) IsSupportedDBInstance() bool {
  528. return true
  529. }
  530. func (self *SAliyunRegionDriver) GetRdsSupportSecgroupCount() int {
  531. return 3
  532. }
  533. func (self *SAliyunRegionDriver) IsSupportedDBInstanceAutoRenew() bool {
  534. return true
  535. }
  536. func (self *SAliyunRegionDriver) IsSupportedElasticcache() bool {
  537. return true
  538. }
  539. func (self *SAliyunRegionDriver) IsSupportedElasticcacheSecgroup() bool {
  540. return false
  541. }
  542. func (self *SAliyunRegionDriver) GetMaxElasticcacheSecurityGroupCount() int {
  543. return 0
  544. }
  545. func (self *SAliyunRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) {
  546. cidrV := validators.NewIPv4PrefixValidator("cidr_block")
  547. if err := cidrV.Validate(ctx, jsonutils.Marshal(input).(*jsonutils.JSONDict)); err != nil {
  548. return input, err
  549. }
  550. err := IsInPrivateIpRange(cidrV.Value.ToIPRange())
  551. if err != nil {
  552. return input, err
  553. }
  554. if cidrV.Value.MaskLen > 24 {
  555. return input, httperrors.NewInputParameterError("invalid cidr range %s, mask length should less than or equal to 24", cidrV.Value.String())
  556. }
  557. return input, nil
  558. }
  559. func (self *SAliyunRegionDriver) ValidateCreateNatGateway(ctx context.Context, userCred mcclient.TokenCredential, input api.NatgatewayCreateInput) (api.NatgatewayCreateInput, error) {
  560. return input, nil
  561. }
  562. func (self *SAliyunRegionDriver) IsSupportedNatGateway() bool {
  563. return true
  564. }
  565. func (self *SAliyunRegionDriver) IsSupportedNas() bool {
  566. return true
  567. }
  568. func (self *SAliyunRegionDriver) ValidateCreateWafInstanceData(ctx context.Context, userCred mcclient.TokenCredential, input api.WafInstanceCreateInput) (api.WafInstanceCreateInput, error) {
  569. if !regutils.DOMAINNAME_REG.MatchString(input.Name) {
  570. return input, httperrors.NewInputParameterError("invalid domain name %s", input.Name)
  571. }
  572. input.Type = cloudprovider.WafTypeDefault
  573. if len(input.SourceIps) == 0 && len(input.CloudResources) == 0 {
  574. return input, httperrors.NewMissingParameterError("source_ips")
  575. }
  576. return input, nil
  577. }
  578. func (self *SAliyunRegionDriver) ValidateCreateWafRuleData(ctx context.Context, userCred mcclient.TokenCredential, waf *models.SWafInstance, input api.WafRuleCreateInput) (api.WafRuleCreateInput, error) {
  579. return input, httperrors.NewUnsupportOperationError("not supported create rule")
  580. }
  581. func (self *SAliyunRegionDriver) ValidateCreateSecurityGroupInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.SSecgroupCreateInput) (*api.SSecgroupCreateInput, error) {
  582. for i := range input.Rules {
  583. rule := input.Rules[i]
  584. if rule.Priority == nil {
  585. return nil, httperrors.NewMissingParameterError("priority")
  586. }
  587. if *rule.Priority < 1 || *rule.Priority > 100 {
  588. return nil, httperrors.NewInputParameterError("invalid priority %d, range 1-100", *rule.Priority)
  589. }
  590. if len(rule.Ports) > 0 && strings.Contains(input.Rules[i].Ports, ",") {
  591. return nil, httperrors.NewInputParameterError("invalid ports %s", input.Rules[i].Ports)
  592. }
  593. }
  594. return self.SManagedVirtualizationRegionDriver.ValidateCreateSecurityGroupInput(ctx, userCred, input)
  595. }
  596. func (self *SAliyunRegionDriver) ValidateUpdateSecurityGroupRuleInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.SSecgroupRuleUpdateInput) (*api.SSecgroupRuleUpdateInput, error) {
  597. if input.Priority != nil && (*input.Priority < 1 || *input.Priority > 100) {
  598. return nil, httperrors.NewInputParameterError("invalid priority %d, range 1-100", *input.Priority)
  599. }
  600. if input.Ports != nil && strings.Contains(*input.Ports, ",") {
  601. return nil, httperrors.NewInputParameterError("invalid ports %s", *input.Ports)
  602. }
  603. return self.SManagedVirtualizationRegionDriver.ValidateUpdateSecurityGroupRuleInput(ctx, userCred, input)
  604. }