diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 3c333f8946..ee345facf1 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -160,6 +160,7 @@ type ICloudVM interface { ChangeConfig(instanceId string, ncpu int, vmem int) error GetVNCInfo() (jsonutils.JSONObject, error) AttachDisk(diskId string) error + DetachDisk(diskId string) error } type ICloudNic interface { diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index f149f322ec..38b0123006 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -9,6 +9,7 @@ import ( "yunion.io/x/log" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -48,6 +49,14 @@ func (self *SAliyunGuestDriver) ChooseHostStorage(host *models.SHost, backend st return nil } +func (self *SAliyunGuestDriver) GetDetachDiskStatus() ([]string, error) { + return []string{models.VM_READY, models.VM_RUNNING}, nil +} + +func (self *SAliyunGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId()) +} + func (self *SAliyunGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { data, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, data) if err != nil { @@ -335,13 +344,44 @@ func (self *SAliyunGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Contex func (self *SAliyunGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { - if ihost, err := host.GetIHost(); err != nil { - return nil, err - } else if iVM, err := ihost.GetIVMById(guest.ExternalId); err != nil { - return nil, err - } else if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { - return nil, err + if ihost, err := host.GetIHost(); err != nil { + return nil, err + } else if iVM, err := ihost.GetIVMById(guest.ExternalId); err != nil { + return nil, err + } else { + if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { + if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { + return nil, err + } + } else { + if iDisks, err := iVM.GetIDisks(); err != nil { + return nil, err + } else { + disks := make([]models.SDisk, 0) + for _, guestdisk := range guest.GetDisks() { + disk := guestdisk.GetDisk() + disks = append(disks, *disk) + } + + added := make([]models.SDisk, 0) + commondb := make([]models.SDisk, 0) + commonext := make([]cloudprovider.ICloudDisk, 0) + removed := make([]cloudprovider.ICloudDisk, 0) + + if err := compare.CompareSets(disks, iDisks, &added, &commondb, &commonext, &removed); err != nil { + return nil, err + } + for _, disk := range removed { + if err := iVM.DetachDisk(disk.GetId()); err != nil { + return nil, err + } + } + for _, disk := range added { + if err := iVM.AttachDisk(disk.ExternalId); err != nil { + return nil, err + } + } + } } } return nil, nil diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index d98ed72b15..0c3e1ab525 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -224,18 +224,21 @@ func (self *SKVMGuestDriver) RequestDeleteDetachedDisk(ctx context.Context, disk } func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - desc := guest.GetDriver().GetJsonDescAtHost(ctx, guest, host) - body := jsonutils.NewDict() - body.Add(desc, "desc") - if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { - body.Add(jsonutils.JSONTrue, "fw_only") + if guest.Status == models.VM_RUNNING { + desc := guest.GetDriver().GetJsonDescAtHost(ctx, guest, host) + body := jsonutils.NewDict() + body.Add(desc, "desc") + if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { + body.Add(jsonutils.JSONTrue, "fw_only") + } + url := fmt.Sprintf("/servers/%s/sync", guest.Id) + header := http.Header{} + header.Add("X-Task-Id", task.GetTaskId()) + header.Add("X-Region-Version", "v2") + _, err := host.Request(task.GetUserCred(), "POST", url, header, body) + return err } - url := fmt.Sprintf("/servers/%s/sync", guest.Id) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") - _, err := host.Request(task.GetUserCred(), "POST", url, header, body) - return err + return nil } func (self *SKVMGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index afd50582dc..7480cd1a00 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1625,6 +1625,17 @@ func (self *SGuest) AllowPerformAttachdisk(ctx context.Context, userCred mcclien } func (self *SGuest) ValidateAttachDisk(ctx context.Context, disk *SDisk) error { + storage := disk.GetStorage() + if provider := storage.GetCloudprovider(); provider != nil { + host := self.GetHost() + if provider.Id != host.ManagerId { + return httperrors.NewInputParameterError("Disk %s and guest not belong to the same account", disk.Name) + } else if storage.ZoneId != host.ZoneId { + return httperrors.NewInputParameterError("Disk %s and guest not belong to the same zone", disk.Name) + } + return nil + } + if disk.isAttached() { return httperrors.NewInputParameterError("Disk %s has been attached", disk.Name) } else if len(disk.GetPathAtHost(self.GetHost())) == 0 { diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index 7f034089b6..2d7e95e49b 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -406,6 +406,10 @@ func (self *SInstance) AttachDisk(diskId string) error { return self.host.zone.region.AttachDisk(self.InstanceId, diskId) } +func (self *SInstance) DetachDisk(diskId string) error { + return self.host.zone.region.DetachDisk(self.InstanceId, diskId) +} + func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) { instances, _, err := self.GetInstances("", []string{instanceId}, 0, 1) if err != nil { @@ -667,6 +671,20 @@ func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, return fmt.Errorf("Failed to change vm config, specification not supported") } +func (self *SRegion) DetachDisk(instanceId string, diskId string) error { + params := make(map[string]string) + params["InstanceId"] = instanceId + params["DiskId"] = diskId + log.Infof("Detach instance %s disk %s", instanceId, diskId) + _, err := self.ecsRequest("DetachDisk", params) + if err != nil { + log.Errorf("DetachDisk %s to %s fail %s", diskId, instanceId, err) + return err + } + + return nil +} + func (self *SRegion) AttachDisk(instanceId string, diskId string) error { params := make(map[string]string) params["InstanceId"] = instanceId diff --git a/pkg/util/aliyun/shell/instance.go b/pkg/util/aliyun/shell/instance.go index 4a741d0b66..28b1a73d36 100644 --- a/pkg/util/aliyun/shell/instance.go +++ b/pkg/util/aliyun/shell/instance.go @@ -43,6 +43,27 @@ func init() { return nil }) + type InstanceDiskOperationOptions struct { + ID string `help:"instance ID"` + DISK string `help:"disk ID"` + } + + shellutils.R(&InstanceDiskOperationOptions{}, "instance-attach-disk", "Attach a disk to instance", func(cli *aliyun.SRegion, args *InstanceDiskOperationOptions) error { + err := cli.AttachDisk(args.ID, args.DISK) + if err != nil { + return err + } + return nil + }) + + shellutils.R(&InstanceDiskOperationOptions{}, "instance-detach-disk", "Detach a disk to instance", func(cli *aliyun.SRegion, args *InstanceDiskOperationOptions) error { + err := cli.DetachDisk(args.ID, args.DISK) + if err != nil { + return err + } + return nil + }) + type InstanceOperationOptions struct { ID string `help:"instance ID"` } diff --git a/pkg/util/esxi/virtualmachine.go b/pkg/util/esxi/virtualmachine.go index 34c1a9c682..5cf0149ee9 100644 --- a/pkg/util/esxi/virtualmachine.go +++ b/pkg/util/esxi/virtualmachine.go @@ -81,6 +81,10 @@ func (self *SVirtualMachine) UpdateVM(name string) error { return cloudprovider.ErrNotImplemented } +func (self *SVirtualMachine) DetachDisk(diskId string) error { + return cloudprovider.ErrNotImplemented +} + func (self *SVirtualMachine) AttachDisk(diskId string) error { return cloudprovider.ErrNotImplemented }