diff --git a/pkg/compute/guestdrivers/huawei.go b/pkg/compute/guestdrivers/huawei.go index 53e0d01de0..45cd8531b3 100644 --- a/pkg/compute/guestdrivers/huawei.go +++ b/pkg/compute/guestdrivers/huawei.go @@ -184,11 +184,11 @@ func (self *SHuaweiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId) - err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800) + err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800) if err != nil { return nil, err } - log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId()) + log.Debugf("VMrebuildRoot %s, and status is %s", iVM.GetGlobalId(), iVM.GetStatus()) maxWaitSecs := 300 waited := 0 diff --git a/pkg/util/huawei/consts.go b/pkg/util/huawei/consts.go index 3b5b1a14c7..683497fb9f 100644 --- a/pkg/util/huawei/consts.go +++ b/pkg/util/huawei/consts.go @@ -4,7 +4,10 @@ package huawei const DATETIME_FORMAT = "2006-01-02T15:04:05.999999999" // Task status -const TASK_SUCCESS = "SUCCESS" +const ( + TASK_SUCCESS = "SUCCESS" + TASK_FAIL = "FAIL" +) // Charging Type const ( diff --git a/pkg/util/huawei/disk.go b/pkg/util/huawei/disk.go index 78257d8649..3d72ea70e6 100644 --- a/pkg/util/huawei/disk.go +++ b/pkg/util/huawei/disk.go @@ -95,6 +95,7 @@ type SDisk struct { ConsistencygroupID string `json:"consistencygroup_id"` UpdatedAt string `json:"updated_at"` + DiskType string // 额外添加的字段用于标记是否为系统盘 /*下面这些字段也许不需要*/ ExpiredTime time.Time } @@ -233,8 +234,14 @@ func (self *SDisk) GetTemplateId() string { return self.VolumeImageMetadata.ImageID } +// Bootable 表示硬盘是否为启动盘。 +// 启动盘 != 系统盘(必须是启动盘且挂载在root device上) func (self *SDisk) GetDiskType() string { - if self.Bootable == "true" { + if self.Bootable != "true" || len(self.Attachments) == 0 { + return models.DISK_TYPE_DATA + } + + if self.DiskType == models.DISK_TYPE_SYS { return models.DISK_TYPE_SYS } else { return models.DISK_TYPE_DATA diff --git a/pkg/util/huawei/instance.go b/pkg/util/huawei/instance.go index 60909e7eac..104d9405ee 100644 --- a/pkg/util/huawei/instance.go +++ b/pkg/util/huawei/instance.go @@ -96,7 +96,6 @@ type SInstance struct { Tags []string `json:"tags"` Description string `json:"description"` Locked bool `json:"locked"` - Image Image `json:"image"` ConfigDrive string `json:"config_drive"` TenantID string `json:"tenant_id"` UserID string `json:"user_id"` @@ -156,6 +155,25 @@ func compareSet(currentSet []string, newSet []string) (add []string, remove []st return add, remove, keep } +func markDiskType(server *SInstance, disk *SDisk) { + if disk.Bootable != "true" { + return + } + + if len(disk.Attachments) == 0 { + return + } + + for _, attachment := range disk.Attachments { + if attachment.ServerID == server.GetId() && attachment.Device == server.OSEXTSRVATTRRootDeviceName { + disk.DiskType = models.DISK_TYPE_SYS + return + } + } + + return +} + func (self *SInstance) GetId() string { return self.ID } @@ -216,9 +234,9 @@ func (self *SInstance) GetMetadata() *jsonutils.JSONDict { priceKey := fmt.Sprintf("%s::%s::%s", self.host.zone.region.GetId(), self.GetInstanceType(), lowerOs) data.Add(jsonutils.NewString(priceKey), "price_key") data.Add(jsonutils.NewString(self.host.zone.GetGlobalId()), "zone_ext_id") - if len(self.Image.ID) > 0 { - if image, err := self.host.zone.region.GetImage(self.Image.ID); err != nil { - log.Errorf("Failed to find image %s for instance %s zone %s", self.Image.ID, self.GetId(), self.OSEXTAZAvailabilityZone) + if len(self.Metadata.MeteringImageID) > 0 { + if image, err := self.host.zone.region.GetImage(self.Metadata.MeteringImageID); err != nil { + log.Errorf("Failed to find image %s for instance %s zone %s", self.Metadata.MeteringImageID, self.GetId(), self.OSEXTAZAvailabilityZone) } else if meta := image.GetMetadata(); meta != nil { data.Update(meta) } @@ -281,6 +299,7 @@ func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { } disks[i].storage = storage idisks[i] = &disks[i] + markDiskType(self, &disks[i]) // todo: 通过这个字段判断可能更准确 "OS-EXT-SRV-ATTR:root_device_name": "/dev/vda" // 将系统盘放到第0个位置 if disks[i].GetDiskType() == models.DISK_TYPE_SYS { @@ -481,20 +500,40 @@ func (self *SInstance) UpdateUserData(userData string) error { // todo: 支持注入user_data func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { var err error - if self.Image.ID == imageId { - err = self.host.zone.region.RebuildRoot(ctx, self.GetId(), passwd, publicKey) + var jobId string + if self.Metadata.MeteringImageID == imageId { + jobId, err = self.host.zone.region.RebuildRoot(ctx, self.GetId(), passwd, publicKey) if err != nil { return "", err } } else { - err = self.host.zone.region.ChangeRoot(ctx, self.GetId(), imageId, passwd, publicKey) + jobId, err = self.host.zone.region.ChangeRoot(ctx, self.GetId(), imageId, passwd, publicKey) if err != nil { return "", err } } - // todo: wait job finished here - return "", nil + err = self.host.zone.region.waitTaskStatus(self.host.zone.region.ecsClient.Servers.ServiceType(), jobId, TASK_SUCCESS, 15*time.Second, 900*time.Second) + if err != nil { + log.Errorf("RebuildRoot task error %s", err) + return "", err + } + + err = self.Refresh() + if err != nil { + return "", err + } + + idisks, err := self.GetIDisks() + if err != nil { + return "", err + } + + if len(idisks) == 0 { + return "", fmt.Errorf("server %s has no volume attached.", self.GetId()) + } + + return idisks[0].GetId(), nil } func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { @@ -883,7 +922,9 @@ func (self *SRegion) UpdateVM(instanceId, name string) error { return err } -func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, publicKeyName string) error { +// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876349.html +// 返回job id +func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, publicKeyName string) (string, error) { params := jsonutils.NewDict() reinstallObj := jsonutils.NewDict() // meta := jsonutils.NewDict() @@ -893,15 +934,21 @@ func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, public } else if len(publicKeyName) > 0 { reinstallObj.Add(jsonutils.NewString(publicKeyName), "keyname") } else { - return fmt.Errorf("both password and publicKey are empty.") + return "", fmt.Errorf("both password and publicKey are empty.") } params.Add(reinstallObj, "os-reinstall") - _, err := self.ecsClient.Servers.PerformAction2("reinstallos", instanceId, params, "") - return err + ret, err := self.ecsClient.Servers.PerformAction2("reinstallos", instanceId, params, "") + if err != nil { + return "", err + } + + return ret.GetString("job_id") } -func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd, publicKeyName string) error { +// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876971.html +// 返回job id +func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd, publicKeyName string) (string, error) { params := jsonutils.NewDict() changeOsObj := jsonutils.NewDict() // meta := jsonutils.NewDict() @@ -911,14 +958,18 @@ func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd } else if len(publicKeyName) > 0 { changeOsObj.Add(jsonutils.NewString(publicKeyName), "keyname") } else { - return fmt.Errorf("both password and publicKey are empty.") + return "", fmt.Errorf("both password and publicKey are empty.") } changeOsObj.Add(jsonutils.NewString(imageId), "imageid") params.Add(changeOsObj, "os-change") - _, err := self.ecsClient.Servers.PerformAction2("changeos", instanceId, params, "") - return err + ret, err := self.ecsClient.Servers.PerformAction2("changeos", instanceId, params, "") + if err != nil { + return "", err + } + + return ret.GetString("job_id") } // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212692.html diff --git a/pkg/util/huawei/task.go b/pkg/util/huawei/task.go index 0d3128c7b9..d62904094f 100644 --- a/pkg/util/huawei/task.go +++ b/pkg/util/huawei/task.go @@ -1,7 +1,10 @@ package huawei import ( + "fmt" "time" + + "yunion.io/x/log" ) func (self *SRegion) waitTaskStatus(serviceType string, taskId string, targetStatus string, interval time.Duration, timeout time.Duration) error { @@ -13,6 +16,8 @@ func (self *SRegion) waitTaskStatus(serviceType string, taskId string, targetSta } if status == targetStatus { break + } else if status == TASK_FAIL { + return fmt.Errorf("task %s failed", taskId) } else { time.Sleep(interval) } @@ -27,7 +32,12 @@ func (self *SRegion) GetTaskStatus(serviceType string, taskId string) (string, e return "", err } - return task.GetString("status") + status, err := task.GetString("status") + if status == TASK_FAIL { + log.Debugf("task %s failed: %s", taskId, task.String()) + } + + return status, err } // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0022225398.html