| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package hcso
- import (
- "context"
- "fmt"
- "time"
- "github.com/pkg/errors"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/util/billing"
- billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- "yunion.io/x/cloudmux/pkg/multicloud/huawei"
- )
- type SBackupStrategy struct {
- KeepDays int
- StartTime string
- }
- type SDatastore struct {
- Type string
- Version string
- }
- type SHa struct {
- ReplicationMode string
- }
- type SNonde struct {
- AvailabilityZone string
- Id string
- Name string
- Role string
- Staus string
- }
- type SVolume struct {
- Size int
- Type string
- }
- type SRelatedInstance struct {
- Id string
- Type string
- }
- type SDBInstance struct {
- multicloud.SDBInstanceBase
- huawei.HuaweiTags
- region *SRegion
- flavorCache []SDBInstanceFlavor
- BackupStrategy SBackupStrategy
- Created string //time.Time
- Datastore SDatastore
- DbUserName string
- DIskEncryptionId string
- FlavorRef string
- Ha SHa
- Id string
- MaintenanceWindow string
- Name string
- Nodes []SNonde
- Port int
- PrivateIps []string
- PublicIps []string
- Region string
- RelatedInstance []SRelatedInstance
- SecurityGroupId string
- Status string
- SubnetId string
- SwitchStrategy string
- TimeZone string
- Type string
- Updated string //time.Time
- Volume SVolume
- VpcId string
- EnterpriseProjectId string
- }
- func (region *SRegion) GetDBInstances() ([]SDBInstance, error) {
- params := map[string]string{}
- dbinstances := []SDBInstance{}
- err := doListAllWithOffset(region.ecsClient.DBInstance.List, params, &dbinstances)
- return dbinstances, err
- }
- func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
- if len(instanceId) == 0 {
- return nil, cloudprovider.ErrNotFound
- }
- instance := SDBInstance{region: region}
- err := DoGet(region.ecsClient.DBInstance.Get, instanceId, nil, &instance)
- return &instance, err
- }
- func (rds *SDBInstance) GetName() string {
- return rds.Name
- }
- func (rds *SDBInstance) GetId() string {
- return rds.Id
- }
- func (rds *SDBInstance) GetGlobalId() string {
- return rds.GetId()
- }
- // 值为“BUILD”,表示实例正在创建。
- // 值为“ACTIVE”,表示实例正常。
- // 值为“FAILED”,表示实例异常。
- // 值为“FROZEN”,表示实例冻结。
- // 值为“MODIFYING”,表示实例正在扩容。
- // 值为“REBOOTING”,表示实例正在重启。
- // 值为“RESTORING”,表示实例正在恢复。
- // 值为“MODIFYING INSTANCE TYPE”,表示实例正在转主备。
- // 值为“SWITCHOVER”,表示实例正在主备切换。
- // 值为“MIGRATING”,表示实例正在迁移。
- // 值为“BACKING UP”,表示实例正在进行备份。
- // 值为“MODIFYING DATABASE PORT”,表示实例正在修改数据库端口。
- // 值为“STORAGE FULL”,表示实例磁盘空间满。
- func (rds *SDBInstance) GetStatus() string {
- switch rds.Status {
- case "BUILD", "MODIFYING", "MODIFYING INSTANCE TYPE", "SWITCHOVER", "MODIFYING DATABASE PORT":
- return api.DBINSTANCE_DEPLOYING
- case "ACTIVE":
- return api.DBINSTANCE_RUNNING
- case "FAILED", "FROZEN", "STORAGE FULL":
- return api.DBINSTANCE_UNKNOWN
- case "REBOOTING":
- return api.DBINSTANCE_REBOOTING
- case "RESTORING":
- return api.DBINSTANCE_RESTORING
- case "MIGRATING":
- return api.DBINSTANCE_MIGRATING
- case "BACKING UP":
- return api.DBINSTANCE_BACKING_UP
- }
- return rds.Status
- }
- func (rds *SDBInstance) GetBillingType() string {
- return billing_api.BILLING_TYPE_POSTPAID
- }
- func (rds *SDBInstance) GetSecurityGroupIds() ([]string, error) {
- return []string{rds.SecurityGroupId}, nil
- }
- func (rds *SDBInstance) fetchFlavor() error {
- if len(rds.flavorCache) > 0 {
- return nil
- }
- flavors, err := rds.region.GetDBInstanceFlavors(rds.Datastore.Type, rds.Datastore.Version)
- if err != nil {
- return err
- }
- rds.flavorCache = flavors
- return nil
- }
- func (rds *SDBInstance) GetExpiredAt() time.Time {
- return time.Time{}
- }
- func (rds *SDBInstance) GetStorageType() string {
- return rds.Volume.Type
- }
- func (rds *SDBInstance) GetCreatedAt() time.Time {
- t, err := time.Parse("2006-01-02T15:04:05Z0700", rds.Created)
- if err != nil {
- return time.Time{}
- }
- return t
- }
- func (rds *SDBInstance) GetEngine() string {
- return rds.Datastore.Type
- }
- func (rds *SDBInstance) GetEngineVersion() string {
- return rds.Datastore.Version
- }
- func (rds *SDBInstance) GetInstanceType() string {
- return rds.FlavorRef
- }
- func (rds *SDBInstance) GetCategory() string {
- switch rds.Type {
- case "Single":
- return api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE
- case "Ha":
- return api.HUAWEI_DBINSTANCE_CATEGORY_HA
- case "Replica":
- return api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA
- }
- return rds.Type
- }
- func (rds *SDBInstance) GetVcpuCount() int {
- err := rds.fetchFlavor()
- if err != nil {
- log.Errorf("failed to fetch flavors: %v", err)
- return 0
- }
- for _, flavor := range rds.flavorCache {
- if flavor.SpecCode == rds.FlavorRef {
- return flavor.Vcpus
- }
- }
- return 0
- }
- func (rds *SDBInstance) GetVmemSizeMB() int {
- err := rds.fetchFlavor()
- if err != nil {
- log.Errorf("failed to fetch flavors: %v", err)
- return 0
- }
- for _, flavor := range rds.flavorCache {
- if flavor.SpecCode == rds.FlavorRef {
- return flavor.Ram * 1024
- }
- }
- return 0
- }
- func (rds *SDBInstance) GetDiskSizeGB() int {
- return rds.Volume.Size
- }
- func (rds *SDBInstance) GetPort() int {
- return rds.Port
- }
- func (rds *SDBInstance) GetMaintainTime() string {
- return rds.MaintenanceWindow
- }
- func (rds *SDBInstance) GetIVpcId() string {
- return rds.VpcId
- }
- func (rds *SDBInstance) GetProjectId() string {
- return rds.EnterpriseProjectId
- }
- func (rds *SDBInstance) Refresh() error {
- instance, err := rds.region.GetDBInstance(rds.Id)
- if err != nil {
- return err
- }
- return jsonutils.Update(rds, instance)
- }
- func (rds *SDBInstance) GetZone1Id() string {
- return rds.GetZoneIdByRole("master")
- }
- func (rds *SDBInstance) GetZoneIdByRole(role string) string {
- for _, node := range rds.Nodes {
- if node.Role == role {
- zone, err := rds.region.getZoneById(node.AvailabilityZone)
- if err != nil {
- log.Errorf("failed to found zone %s for rds %s error: %v", node.AvailabilityZone, rds.Name, err)
- return ""
- }
- return zone.GetGlobalId()
- }
- }
- return ""
- }
- func (rds *SDBInstance) GetZone2Id() string {
- return rds.GetZoneIdByRole("slave")
- }
- func (rds *SDBInstance) GetZone3Id() string {
- return ""
- }
- type SRdsNetwork struct {
- SubnetId string
- IP string
- }
- func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
- ret := []cloudprovider.SDBInstanceNetwork{}
- for _, ip := range rds.PrivateIps {
- network := cloudprovider.SDBInstanceNetwork{
- IP: ip,
- NetworkId: rds.SubnetId,
- }
- ret = append(ret, network)
- }
- return ret, nil
- }
- func (rds *SDBInstance) GetInternalConnectionStr() string {
- for _, ip := range rds.PrivateIps {
- return ip
- }
- return ""
- }
- func (rds *SDBInstance) GetConnectionStr() string {
- for _, ip := range rds.PublicIps {
- return ip
- }
- return ""
- }
- func (region *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.ICloudDBInstance, error) {
- dbinstance, err := region.GetDBInstance(instanceId)
- if err != nil {
- log.Errorf("failed to get dbinstance by id %s error: %v", instanceId, err)
- }
- return dbinstance, err
- }
- func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
- instances, err := region.GetDBInstances()
- if err != nil {
- return nil, errors.Wrapf(err, "region.GetDBInstances()")
- }
- idbinstances := []cloudprovider.ICloudDBInstance{}
- for i := 0; i < len(instances); i++ {
- instances[i].region = region
- idbinstances = append(idbinstances, &instances[i])
- }
- return idbinstances, nil
- }
- func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
- parameters, err := rds.region.GetDBInstanceParameters(rds.Id)
- if err != nil {
- return nil, err
- }
- iparameters := []cloudprovider.ICloudDBInstanceParameter{}
- for i := 0; i < len(parameters); i++ {
- iparameters = append(iparameters, ¶meters[i])
- }
- return iparameters, nil
- }
- func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
- databases, err := rds.region.GetDBInstanceDatabases(rds.Id)
- if err != nil {
- return nil, errors.Wrap(err, "rds.region.GetDBInstanceDatabases(rds.Id)")
- }
- idatabase := []cloudprovider.ICloudDBInstanceDatabase{}
- for i := 0; i < len(databases); i++ {
- databases[i].instance = rds
- idatabase = append(idatabase, &databases[i])
- }
- return idatabase, nil
- }
- func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
- accounts, err := rds.region.GetDBInstanceAccounts(rds.Id)
- if err != nil {
- return nil, errors.Wrap(err, "rds.region.GetDBInstanceAccounts(rds.Id)")
- }
- user := "root"
- if rds.GetEngine() == api.DBINSTANCE_TYPE_SQLSERVER {
- user = "rduser"
- }
- accounts = append(accounts, SDBInstanceAccount{
- Name: user,
- instance: rds,
- })
- iaccounts := []cloudprovider.ICloudDBInstanceAccount{}
- for i := 0; i < len(accounts); i++ {
- accounts[i].instance = rds
- iaccounts = append(iaccounts, &accounts[i])
- }
- return iaccounts, nil
- }
- func (rds *SDBInstance) Delete() error {
- return rds.region.DeleteDBInstance(rds.Id)
- }
- func (region *SRegion) DeleteDBInstance(instanceId string) error {
- _, err := region.ecsClient.DBInstance.Delete(instanceId, nil)
- return err
- }
- func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
- zoneIds := []string{}
- zones, err := region.GetIZones()
- if err != nil {
- return nil, err
- }
- for _, zone := range zones {
- zoneIds = append(zoneIds, zone.GetId())
- }
- if len(desc.SecgroupIds) == 0 {
- return nil, fmt.Errorf("Missing secgroupId")
- }
- params := map[string]interface{}{
- "region": region.ID,
- "name": desc.Name,
- "datastore": map[string]string{
- "type": desc.Engine,
- "version": desc.EngineVersion,
- },
- "password": desc.Password,
- "volume": map[string]interface{}{
- "type": desc.StorageType,
- "size": desc.DiskSizeGB,
- },
- "vpc_id": desc.VpcId,
- "subnet_id": desc.NetworkId,
- "security_group_id": desc.SecgroupIds[0],
- }
- if len(desc.ProjectId) > 0 {
- params["enterprise_project_id"] = desc.ProjectId
- }
- if len(desc.MasterInstanceId) > 0 {
- params["replica_of_id"] = desc.MasterInstanceId
- delete(params, "security_group_id")
- }
- if len(desc.RdsId) > 0 && len(desc.BackupId) > 0 {
- params["restore_point"] = map[string]interface{}{
- "backup_id": desc.BackupId,
- "instance_id": desc.RdsId,
- "type": "backup",
- }
- }
- switch desc.Category {
- case api.HUAWEI_DBINSTANCE_CATEGORY_HA:
- switch desc.Engine {
- case api.DBINSTANCE_TYPE_MYSQL, api.DBINSTANCE_TYPE_POSTGRESQL:
- params["ha"] = map[string]string{
- "mode": "Ha",
- "replication_mode": "async",
- }
- case api.DBINSTANCE_TYPE_SQLSERVER:
- params["ha"] = map[string]string{
- "mode": "Ha",
- "replication_mode": "sync",
- }
- }
- case api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE:
- case api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA:
- }
- if desc.BillingCycle != nil {
- periodType := "month"
- periodNum := desc.BillingCycle.GetMonths()
- if desc.BillingCycle.GetYears() > 0 {
- periodType = "year"
- periodNum = desc.BillingCycle.GetYears()
- }
- params["charge_info"] = map[string]interface{}{
- "charge_mode": "prePaid",
- "period_type": periodType,
- "period_num": periodNum,
- "is_auto_renew": false,
- }
- }
- params["flavor_ref"] = desc.InstanceType
- params["availability_zone"] = desc.ZoneId
- resp, err := region.ecsClient.DBInstance.Create(jsonutils.Marshal(params))
- if err != nil {
- return nil, errors.Wrapf(err, "Create")
- }
- instance := &SDBInstance{region: region}
- err = resp.Unmarshal(instance, "instance")
- if err != nil {
- return nil, errors.Wrap(err, `resp.Unmarshal(&instance, "instance")`)
- }
- if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
- err = cloudprovider.Wait(10*time.Second, 20*time.Minute, func() (bool, error) {
- job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
- if err != nil {
- return false, nil
- }
- status, _ := job.GetString("status")
- process, _ := job.GetString("process")
- log.Debugf("create dbinstance job %s status: %s process: %s", jobId, status, process)
- if status == "Completed" {
- return true, nil
- }
- if status == "Failed" {
- return false, fmt.Errorf("create failed")
- }
- return false, nil
- })
- }
- return instance, err
- }
- func (rds *SDBInstance) Reboot() error {
- return rds.region.RebootDBInstance(rds.Id)
- }
- func (rds *SDBInstance) OpenPublicConnection() error {
- return fmt.Errorf("Huawei current not support this operation")
- //return rds.region.PublicConnectionAction(rds.Id, "openRC")
- }
- func (rds *SDBInstance) ClosePublicConnection() error {
- return fmt.Errorf("Huawei current not support this operation")
- //return rds.region.PublicConnectionAction(rds.Id, "closeRC")
- }
- func (region *SRegion) PublicConnectionAction(instanceId string, action string) error {
- resp, err := region.ecsClient.DBInstance.PerformAction2(action, instanceId, nil, "")
- if err != nil {
- return errors.Wrapf(err, "rds.%s", action)
- }
- if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
- err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
- job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
- if err != nil {
- log.Errorf("failed to get job %s info error: %v", jobId, err)
- return false
- }
- status, _ := job.GetString("status")
- process, _ := job.GetString("process")
- if status == "Completed" {
- return true
- }
- log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, status, process)
- return false
- })
- }
- return nil
- }
- func (region *SRegion) RebootDBInstance(instanceId string) error {
- params := jsonutils.Marshal(map[string]interface{}{
- "restart": map[string]string{},
- })
- resp, err := region.ecsClient.DBInstance.PerformAction2("action", instanceId, params, "")
- if err != nil {
- return err
- }
- if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
- err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
- job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
- if err != nil {
- log.Errorf("failed to get job %s info error: %v", jobId, err)
- return false
- }
- status, _ := job.GetString("status")
- process, _ := job.GetString("process")
- if status == "Completed" {
- return true
- }
- log.Debugf("reboot dbinstance job %s status: %s process: %s", jobId, status, process)
- return false
- })
- }
- return err
- }
- func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
- return rds.region.CreateDBInstanceAccount(rds.Id, conf.Name, conf.Password)
- }
- func (region *SRegion) CreateDBInstanceAccount(instanceId, account, password string) error {
- params := map[string]string{
- "name": account,
- "password": password,
- }
- _, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/db_user", instanceId), jsonutils.Marshal(params), "")
- return err
- }
- func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
- return rds.region.CreateDBInstanceDatabase(rds.Id, conf.Name, conf.CharacterSet)
- }
- func (region *SRegion) CreateDBInstanceDatabase(instanceId, database, characterSet string) error {
- params := map[string]string{
- "name": database,
- "character_set": characterSet,
- }
- _, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/database", instanceId), jsonutils.Marshal(params), "")
- return err
- }
- func (rds *SDBInstance) ChangeConfig(cxt context.Context, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
- return rds.region.ChangeDBInstanceConfig(rds.Id, desc.InstanceType, desc.DiskSizeGB)
- }
- func (region *SRegion) ChangeDBInstanceConfig(instanceId string, instanceType string, diskSizeGb int) error {
- instance, err := region.GetIDBInstanceById(instanceId)
- if err != nil {
- return errors.Wrapf(err, "region.GetIDBInstanceById(%s)", instanceId)
- }
- if len(instanceType) > 0 {
- params := map[string]map[string]string{
- "resize_flavor": map[string]string{
- "spec_code": instanceType,
- },
- }
- _, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
- if err != nil {
- return errors.Wrap(err, "resize_flavor")
- }
- cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
- }
- if diskSizeGb > 0 {
- params := map[string]map[string]int{
- "enlarge_volume": map[string]int{
- "size": diskSizeGb,
- },
- }
- _, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
- if err != nil {
- return errors.Wrap(err, "enlarge_volume")
- }
- cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
- }
- return nil
- }
- func (rds *SDBInstance) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
- if len(conf.OriginDBInstanceExternalId) == 0 {
- conf.OriginDBInstanceExternalId = rds.Id
- }
- return rds.region.RecoveryDBInstanceFromBackup(rds.Id, conf.OriginDBInstanceExternalId, conf.BackupId, conf.Databases)
- }
- func (region *SRegion) RecoveryDBInstanceFromBackup(target, origin string, backupId string, databases map[string]string) error {
- source := map[string]interface{}{
- "type": "backup",
- "backup_id": backupId,
- }
- if len(origin) > 0 {
- source["instance_id"] = origin
- }
- if len(databases) > 0 {
- source["database_name"] = databases
- }
- params := map[string]interface{}{
- "source": source,
- "target": map[string]string{
- "instance_id": target,
- },
- }
- _, err := region.ecsClient.DBInstance.PerformAction("", "recovery", jsonutils.Marshal(params))
- if err != nil {
- return errors.Wrap(err, "dbinstance.recovery")
- }
- return nil
- }
- func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error {
- return cloudprovider.ErrNotSupported
- }
|