yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/provider/provider.go (about) 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 15 package provider 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/pkg/errors" 24 25 api "yunion.io/x/cloudmux/pkg/apis/compute" 26 "yunion.io/x/cloudmux/pkg/cloudprovider" 27 "yunion.io/x/onecloud/pkg/httperrors" 28 "yunion.io/x/onecloud/pkg/mcclient" 29 "yunion.io/x/cloudmux/pkg/multicloud/aliyun" 30 ) 31 32 type SAliyunProviderFactory struct { 33 cloudprovider.SPublicCloudBaseProviderFactory 34 } 35 36 func (self *SAliyunProviderFactory) GetId() string { 37 return aliyun.CLOUD_PROVIDER_ALIYUN 38 } 39 40 func (self *SAliyunProviderFactory) GetName() string { 41 return aliyun.CLOUD_PROVIDER_ALIYUN_CN 42 } 43 44 func (self *SAliyunProviderFactory) IsCloudeventRegional() bool { 45 return true 46 } 47 48 func (self *SAliyunProviderFactory) IsSupportCloudIdService() bool { 49 return true 50 } 51 52 func (self *SAliyunProviderFactory) IsSupportCreateCloudgroup() bool { 53 return true 54 } 55 56 func (factory *SAliyunProviderFactory) IsSystemCloudpolicyUnified() bool { 57 return false 58 } 59 60 func (factory *SAliyunProviderFactory) IsSupportSAMLAuth() bool { 61 return true 62 } 63 64 func (self *SAliyunProviderFactory) GetSupportedDnsZoneTypes() []cloudprovider.TDnsZoneType { 65 return []cloudprovider.TDnsZoneType{ 66 cloudprovider.PublicZone, 67 cloudprovider.PrivateZone, 68 } 69 } 70 71 func (self *SAliyunProviderFactory) GetSupportedDnsTypes() map[cloudprovider.TDnsZoneType][]cloudprovider.TDnsType { 72 return map[cloudprovider.TDnsZoneType][]cloudprovider.TDnsType{ 73 cloudprovider.PublicZone: []cloudprovider.TDnsType{ 74 cloudprovider.DnsTypeA, 75 cloudprovider.DnsTypeAAAA, 76 cloudprovider.DnsTypeCAA, 77 cloudprovider.DnsTypeCNAME, 78 cloudprovider.DnsTypeMX, 79 cloudprovider.DnsTypeNS, 80 cloudprovider.DnsTypeSRV, 81 cloudprovider.DnsTypeTXT, 82 cloudprovider.DnsTypePTR, 83 cloudprovider.DnsTypeFORWARD_URL, 84 cloudprovider.DnsTypeREDIRECT_URL, 85 }, 86 cloudprovider.PrivateZone: []cloudprovider.TDnsType{ 87 cloudprovider.DnsTypeA, 88 cloudprovider.DnsTypeAAAA, 89 cloudprovider.DnsTypeCNAME, 90 cloudprovider.DnsTypeMX, 91 cloudprovider.DnsTypeSRV, 92 cloudprovider.DnsTypeTXT, 93 cloudprovider.DnsTypePTR, 94 }, 95 } 96 } 97 98 func (self *SAliyunProviderFactory) GetSupportedDnsPolicyTypes() map[cloudprovider.TDnsZoneType][]cloudprovider.TDnsPolicyType { 99 return map[cloudprovider.TDnsZoneType][]cloudprovider.TDnsPolicyType{ 100 cloudprovider.PublicZone: []cloudprovider.TDnsPolicyType{ 101 cloudprovider.DnsPolicyTypeSimple, 102 cloudprovider.DnsPolicyTypeByCarrier, 103 cloudprovider.DnsPolicyTypeByGeoLocation, 104 cloudprovider.DnsPolicyTypeBySearchEngine, 105 }, 106 cloudprovider.PrivateZone: []cloudprovider.TDnsPolicyType{ 107 cloudprovider.DnsPolicyTypeSimple, 108 }, 109 } 110 } 111 112 func (self *SAliyunProviderFactory) GetSupportedDnsPolicyValues() map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue { 113 return map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue{ 114 cloudprovider.DnsPolicyTypeByCarrier: []cloudprovider.TDnsPolicyValue{ 115 cloudprovider.DnsPolicyValueUnicom, 116 cloudprovider.DnsPolicyValueTelecom, 117 cloudprovider.DnsPolicyValueChinaMobile, 118 cloudprovider.DnsPolicyValueCernet, 119 }, 120 cloudprovider.DnsPolicyTypeByGeoLocation: []cloudprovider.TDnsPolicyValue{ 121 cloudprovider.DnsPolicyValueOversea, 122 }, 123 cloudprovider.DnsPolicyTypeBySearchEngine: []cloudprovider.TDnsPolicyValue{ 124 cloudprovider.DnsPolicyValueBaidu, 125 cloudprovider.DnsPolicyValueGoogle, 126 cloudprovider.DnsPolicyValueBing, 127 }, 128 } 129 } 130 131 func (self *SAliyunProviderFactory) GetTTLRange(zoneType cloudprovider.TDnsZoneType, productType cloudprovider.TDnsProductType) cloudprovider.TTlRange { 132 if zoneType == cloudprovider.PublicZone { 133 if len(productType) > 0 { 134 switch productType { 135 case cloudprovider.DnsProductEnterpriseUltimate: 136 return cloudprovider.TtlRangeAliyunEnterpriseUltimate 137 case cloudprovider.DnsProductEnterpriseStandard: 138 return cloudprovider.TtlRangeAliyunEnterpriseStandard 139 case cloudprovider.DnsProductPersonalProfessional: 140 return cloudprovider.TtlRangeAliyunPersonal 141 default: 142 return cloudprovider.TtlRangeAliyunFree 143 } 144 } 145 return cloudprovider.TtlRangeAliyunFree 146 } 147 148 if zoneType == cloudprovider.PrivateZone { 149 return cloudprovider.TtlRangeAliyunPvtz 150 } 151 return cloudprovider.TTlRange{} 152 } 153 154 func (self *SAliyunProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { 155 output := cloudprovider.SCloudaccount{} 156 if len(input.AccessKeyId) == 0 { 157 return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_id") 158 } 159 if len(input.AccessKeySecret) == 0 { 160 return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_secret") 161 } 162 output.AccessUrl = input.Environment 163 output.Account = input.AccessKeyId 164 output.Secret = input.AccessKeySecret 165 return output, nil 166 } 167 168 func (self *SAliyunProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential, cloudaccount string) (cloudprovider.SCloudaccount, error) { 169 output := cloudprovider.SCloudaccount{} 170 if len(input.AccessKeyId) == 0 { 171 return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_id") 172 } 173 if len(input.AccessKeySecret) == 0 { 174 return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_secret") 175 } 176 output = cloudprovider.SCloudaccount{ 177 Account: input.AccessKeyId, 178 Secret: input.AccessKeySecret, 179 } 180 return output, nil 181 } 182 183 func validateClientCloudenv(client *aliyun.SAliyunClient) error { 184 regions := client.GetIRegions() 185 if len(regions) == 0 { 186 return nil 187 } 188 189 isFinanceAccount := false 190 for i := range regions { 191 if strings.Contains(regions[i].GetId(), "-finance") { 192 isFinanceAccount = true 193 break 194 } 195 } 196 197 if isFinanceAccount { 198 if regions[0].GetCloudEnv() != "FinanceCloud" { 199 return errors.Wrap(httperrors.ErrInvalidCredential, "aksk is aliyun finance account") 200 } 201 } else { 202 if regions[0].GetCloudEnv() == "FinanceCloud" { 203 return errors.Wrap(httperrors.ErrInvalidCredential, "aksk is not aliyun finance account") 204 } 205 } 206 207 return nil 208 } 209 210 func (self *SAliyunProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) { 211 client, err := aliyun.NewAliyunClient( 212 aliyun.NewAliyunClientConfig( 213 cfg.URL, 214 cfg.Account, 215 cfg.Secret, 216 ).CloudproviderConfig(cfg), 217 ) 218 if err != nil { 219 return nil, err 220 } 221 222 err = validateClientCloudenv(client) 223 if err != nil { 224 return nil, errors.Wrap(err, "validateClientCloudenv") 225 } 226 227 return &SAliyunProvider{ 228 SBaseProvider: cloudprovider.NewBaseProvider(self), 229 client: client, 230 }, nil 231 } 232 233 func (self *SAliyunProviderFactory) GetClientRC(info cloudprovider.SProviderInfo) (map[string]string, error) { 234 return map[string]string{ 235 "ALIYUN_ACCESS_KEY": info.Account, 236 "ALIYUN_SECRET": info.Secret, 237 "ALIYUN_REGION": aliyun.ALIYUN_DEFAULT_REGION, 238 }, nil 239 } 240 241 func init() { 242 factory := SAliyunProviderFactory{} 243 cloudprovider.RegisterFactory(&factory) 244 } 245 246 type SAliyunProvider struct { 247 cloudprovider.SBaseProvider 248 client *aliyun.SAliyunClient 249 } 250 251 func (self *SAliyunProvider) GetSysInfo() (jsonutils.JSONObject, error) { 252 regions := self.client.GetIRegions() 253 info := jsonutils.NewDict() 254 info.Add(jsonutils.NewInt(int64(len(regions))), "region_count") 255 info.Add(jsonutils.NewString(aliyun.ALIYUN_API_VERSION), "api_version") 256 return info, nil 257 } 258 259 func (self *SAliyunProvider) GetVersion() string { 260 return aliyun.ALIYUN_API_VERSION 261 } 262 263 func (self *SAliyunProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { 264 return self.client.GetSubAccounts() 265 } 266 267 func (self *SAliyunProvider) GetAccountId() string { 268 return self.client.GetAccountId() 269 } 270 271 func (self *SAliyunProvider) GetIRegions() []cloudprovider.ICloudRegion { 272 return self.client.GetIRegions() 273 } 274 275 func (self *SAliyunProvider) GetIRegionById(extId string) (cloudprovider.ICloudRegion, error) { 276 return self.client.GetIRegionById(extId) 277 } 278 279 func (self *SAliyunProvider) GetBalance() (float64, string, error) { 280 balance, err := self.client.QueryAccountBalance() 281 if err != nil { 282 return 0.0, api.CLOUD_PROVIDER_HEALTH_UNKNOWN, err 283 } 284 status := api.CLOUD_PROVIDER_HEALTH_NORMAL 285 if balance.CreditAmount+balance.MybankCreditAmount <= 0 { 286 if balance.AvailableAmount < 0 { 287 status = api.CLOUD_PROVIDER_HEALTH_ARREARS 288 } else if balance.AvailableAmount < 100 { 289 status = api.CLOUD_PROVIDER_HEALTH_INSUFFICIENT 290 } 291 } 292 return balance.AvailableAmount, status, nil 293 } 294 295 func (self *SAliyunProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) { 296 return self.client.GetIProjects() 297 } 298 299 func (self *SAliyunProvider) CreateIProject(name string) (cloudprovider.ICloudProject, error) { 300 return self.client.CreateIProject(name) 301 } 302 303 func (self *SAliyunProvider) GetStorageClasses(regionId string) []string { 304 return []string{ 305 "Standard", "IA", "Archive", 306 } 307 } 308 309 func (self *SAliyunProvider) GetBucketCannedAcls(regionId string) []string { 310 return []string{ 311 string(cloudprovider.ACLPrivate), 312 string(cloudprovider.ACLPublicRead), 313 string(cloudprovider.ACLPublicReadWrite), 314 } 315 } 316 317 func (self *SAliyunProvider) GetObjectCannedAcls(regionId string) []string { 318 return []string{ 319 string(cloudprovider.ACLPrivate), 320 string(cloudprovider.ACLPublicRead), 321 string(cloudprovider.ACLPublicReadWrite), 322 } 323 } 324 325 func (self *SAliyunProvider) GetCapabilities() []string { 326 return self.client.GetCapabilities() 327 } 328 329 func (self *SAliyunProvider) GetIamLoginUrl() string { 330 return self.client.GetIamLoginUrl() 331 } 332 333 func (self *SAliyunProvider) CreateIClouduser(conf *cloudprovider.SClouduserCreateConfig) (cloudprovider.IClouduser, error) { 334 return self.client.CreateIClouduser(conf) 335 } 336 337 func (self *SAliyunProvider) GetICloudusers() ([]cloudprovider.IClouduser, error) { 338 return self.client.GetICloudusers() 339 } 340 341 func (self *SAliyunProvider) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { 342 return self.client.GetICloudgroups() 343 } 344 345 func (self *SAliyunProvider) GetICloudgroupByName(name string) (cloudprovider.ICloudgroup, error) { 346 return self.client.GetICloudgroupByName(name) 347 } 348 349 func (self *SAliyunProvider) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) { 350 return self.client.GetIClouduserByName(name) 351 } 352 353 func (self *SAliyunProvider) CreateICloudgroup(name, desc string) (cloudprovider.ICloudgroup, error) { 354 return self.client.CreateICloudgroup(name, desc) 355 } 356 357 func (self *SAliyunProvider) GetISystemCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { 358 return self.client.GetISystemCloudpolicies() 359 } 360 361 func (self *SAliyunProvider) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { 362 return self.client.GetICustomCloudpolicies() 363 } 364 365 func (self *SAliyunProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { 366 return self.client.CreateICloudpolicy(opts) 367 } 368 369 func (self *SAliyunProvider) GetSamlEntityId() string { 370 return cloudprovider.SAML_ENTITY_ID_ALIYUN_ROLE 371 } 372 373 func (self *SAliyunProvider) GetICloudDnsZones() ([]cloudprovider.ICloudDnsZone, error) { 374 izones := []cloudprovider.ICloudDnsZone{} 375 privateZone, err := self.client.GetPrivateICloudDnsZones() 376 if err != nil { 377 return nil, errors.Wrap(err, "self.client.GetPrivateICloudDnsZones()") 378 } 379 publicZone, err := self.client.GetPublicICloudDnsZones() 380 if err != nil { 381 return nil, errors.Wrap(err, "self.client.GetPrivateICloudDnsZones()") 382 } 383 izones = append(izones, privateZone...) 384 izones = append(izones, publicZone...) 385 return izones, nil 386 } 387 func (self *SAliyunProvider) GetICloudDnsZoneById(id string) (cloudprovider.ICloudDnsZone, error) { 388 privateIzone, err := self.client.GetPrivateICloudDnsZoneById(id) 389 if err == nil { 390 return privateIzone, nil 391 } else { 392 if errors.Cause(err) != cloudprovider.ErrNotFound { 393 return nil, err 394 } 395 } 396 397 publicIzone, err := self.client.GetPublicICloudDnsZoneById(id) 398 if err == nil { 399 return publicIzone, nil 400 } else { 401 if errors.Cause(err) != cloudprovider.ErrNotFound { 402 return nil, err 403 } 404 } 405 406 return nil, cloudprovider.ErrNotFound 407 } 408 409 func (self *SAliyunProvider) CreateICloudDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (cloudprovider.ICloudDnsZone, error) { 410 if opts.ZoneType == cloudprovider.PrivateZone { 411 return self.client.CreatePrivateICloudDnsZone(opts) 412 } else { 413 return self.client.CreatePublicICloudDnsZone(opts) 414 } 415 } 416 417 func (self *SAliyunProvider) GetICloudInterVpcNetworks() ([]cloudprovider.ICloudInterVpcNetwork, error) { 418 scens, err := self.client.GetAllCens() 419 if err != nil { 420 return nil, errors.Wrap(err, "self.client.GetAllCens()") 421 } 422 423 iVpcNetworks := []cloudprovider.ICloudInterVpcNetwork{} 424 for i := range scens { 425 iVpcNetworks = append(iVpcNetworks, &scens[i]) 426 } 427 return iVpcNetworks, nil 428 429 } 430 func (self *SAliyunProvider) GetICloudInterVpcNetworkById(id string) (cloudprovider.ICloudInterVpcNetwork, error) { 431 iVpcNetwork, err := self.GetICloudInterVpcNetworks() 432 if err != nil { 433 return nil, errors.Wrap(err, "self.GetICloudInterVpcNetworks()") 434 } 435 for i := range iVpcNetwork { 436 if iVpcNetwork[i].GetId() == id { 437 return iVpcNetwork[i], nil 438 } 439 } 440 return nil, cloudprovider.ErrNotFound 441 } 442 func (self *SAliyunProvider) CreateICloudInterVpcNetwork(opts *cloudprovider.SInterVpcNetworkCreateOptions) (cloudprovider.ICloudInterVpcNetwork, error) { 443 cenId, err := self.client.CreateCen(opts) 444 if err != nil { 445 return nil, errors.Wrapf(err, "self.client.CreateCen(%s)", jsonutils.Marshal(opts).String()) 446 } 447 ivpcNetwork, err := self.GetICloudInterVpcNetworkById(cenId) 448 if err != nil { 449 return nil, errors.Wrapf(err, "self.GetICloudInterVpcNetworkById(%s)", cenId) 450 } 451 return ivpcNetwork, nil 452 } 453 454 func (self *SAliyunProvider) GetCloudRegionExternalIdPrefix() string { 455 return self.client.GetAccessEnv() + "/" 456 } 457 458 func (self *SAliyunProvider) GetICloudroles() ([]cloudprovider.ICloudrole, error) { 459 return self.client.GetICloudroles() 460 } 461 462 func (self *SAliyunProvider) GetICloudroleById(id string) (cloudprovider.ICloudrole, error) { 463 info := strings.Split(id, "role/") 464 if len(info) == 2 { 465 role, err := self.client.GetRole(info[1]) 466 if err != nil { 467 return nil, errors.Wrapf(err, "GetRole(%s)", info[1]) 468 } 469 return role, nil 470 } 471 return nil, fmt.Errorf("invalid role id %s", id) 472 } 473 474 func (self *SAliyunProvider) CreateICloudrole(opts *cloudprovider.SRoleCreateOptions) (cloudprovider.ICloudrole, error) { 475 stetement := fmt.Sprintf(`{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Federated":["%s"]},"Condition":{"StringEquals":{"saml:recipient":"https://signin.aliyun.com/saml-role/sso"}}}],"Version":"1"}`, opts.SAMLProvider) 476 role, err := self.client.CreateRole(opts.Name, stetement, opts.Desc) 477 if err != nil { 478 return nil, errors.Wrapf(err, "CreateRole") 479 } 480 return role, nil 481 } 482 483 func (self *SAliyunProvider) GetICloudroleByName(name string) (cloudprovider.ICloudrole, error) { 484 role, err := self.client.GetRole(name) 485 if err != nil { 486 return nil, errors.Wrapf(err, "GetRole(%s)", name) 487 } 488 return role, nil 489 } 490 491 func (self *SAliyunProvider) GetICloudSAMLProviders() ([]cloudprovider.ICloudSAMLProvider, error) { 492 return self.client.GetICloudSAMLProviders() 493 } 494 495 func (self *SAliyunProvider) CreateICloudSAMLProvider(opts *cloudprovider.SAMLProviderCreateOptions) (cloudprovider.ICloudSAMLProvider, error) { 496 sp, err := self.client.CreateSAMLProvider(opts.Name, opts.Metadata.String(), "") 497 if err != nil { 498 return nil, errors.Wrapf(err, "CreateSAMLProvider") 499 } 500 return sp, nil 501 } 502 503 func (self *SAliyunProvider) GetICloudCDNDomains() ([]cloudprovider.ICloudCDNDomain, error) { 504 return self.client.GetICloudCDNDomains() 505 } 506 507 func (self *SAliyunProvider) GetICloudCDNDomainByName(name string) (cloudprovider.ICloudCDNDomain, error) { 508 return self.client.GetICloudCDNDomainByName(name) 509 } 510 511 func (self *SAliyunProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 512 return self.client.GetMetrics(opts) 513 }