mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #13216 from ioito/feat/qx-nutanix-op
feat(region): support nutanix
This commit is contained in:
@@ -121,6 +121,7 @@ var HOST_TYPES = []string{
|
||||
HOST_TYPE_GOOGLE,
|
||||
HOST_TYPE_JDCLOUD,
|
||||
HOST_TYPE_CLOUDPODS,
|
||||
HOST_TYPE_NUTANIX,
|
||||
}
|
||||
|
||||
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
|
||||
|
||||
@@ -86,6 +86,13 @@ type SDiskInfo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type GuestDiskCreateOptions struct {
|
||||
SizeMb int
|
||||
UUID string
|
||||
Driver string
|
||||
StorageId string
|
||||
}
|
||||
|
||||
const (
|
||||
CLOUD_SHELL = "cloud-shell"
|
||||
CLOUD_SHELL_WITHOUT_ENCRYPT = "cloud-shell-without-encrypt"
|
||||
@@ -117,6 +124,7 @@ type SManagedVMCreateConfig struct {
|
||||
Cpu int
|
||||
MemoryMB int
|
||||
ExternalNetworkId string
|
||||
ExternalVpcId string
|
||||
IpAddr string
|
||||
Description string
|
||||
SysDisk SDiskInfo
|
||||
|
||||
@@ -371,7 +371,7 @@ type ICloudVM interface {
|
||||
AttachDisk(ctx context.Context, diskId string) error
|
||||
DetachDisk(ctx context.Context, diskId string) error
|
||||
|
||||
CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error
|
||||
CreateDisk(ctx context.Context, opts *GuestDiskCreateOptions) (string, error)
|
||||
|
||||
MigrateVM(hostid string) error
|
||||
LiveMigrateVM(hostid string) error
|
||||
|
||||
@@ -81,6 +81,10 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
if len(nics) > 0 {
|
||||
net := nics[0].GetNetwork()
|
||||
config.ExternalNetworkId = net.ExternalId
|
||||
vpc, err := net.GetVpc()
|
||||
if err == nil {
|
||||
config.ExternalVpcId = vpc.ExternalId
|
||||
}
|
||||
config.IpAddr = nics[0].IpAddr
|
||||
}
|
||||
|
||||
@@ -553,9 +557,11 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
|
||||
return nil, errors.Wrapf(err, "GetIVMById(%s)", iVM.GetGlobalId())
|
||||
}
|
||||
|
||||
err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SetSecurityGroups")
|
||||
if guest.GetDriver().GetMaxSecurityGroupCount() > 0 {
|
||||
err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SetSecurityGroups")
|
||||
}
|
||||
}
|
||||
|
||||
ret, expect := 0, len(desc.DataDisks)+1
|
||||
@@ -801,15 +807,17 @@ func (self *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Contex
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "guest.GetIVM")
|
||||
}
|
||||
opts := &cloudprovider.ServerStopOptions{}
|
||||
task.GetParams().Unmarshal(opts)
|
||||
err = ivm.StopVM(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ivm.StopVM")
|
||||
}
|
||||
err = cloudprovider.WaitStatus(ivm, api.VM_READY, time.Second*3, time.Minute*5)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "wait server stop after 5 miniutes")
|
||||
if ivm.GetStatus() != api.VM_READY {
|
||||
opts := &cloudprovider.ServerStopOptions{}
|
||||
task.GetParams().Unmarshal(opts)
|
||||
err = ivm.StopVM(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ivm.StopVM")
|
||||
}
|
||||
err = cloudprovider.WaitStatus(ivm, api.VM_READY, time.Second*3, time.Minute*5)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "wait server stop after 5 miniutes")
|
||||
}
|
||||
}
|
||||
// 公有云关机,公网ip会释放
|
||||
guest.SyncAllWithCloudVM(ctx, task.GetUserCred(), host, ivm, syncStatus)
|
||||
|
||||
@@ -86,8 +86,16 @@ func (self *SNutanixGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetUserDataType() string {
|
||||
return cloudprovider.CLOUD_SHELL
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) ChooseHostStorage(host *models.SHost, guest *models.SGuest, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
@@ -103,12 +111,21 @@ func (self *SNutanixGuestDriver) GetMaxSecurityGroupCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
subtask, err := taskman.TaskManager.NewTask(ctx, "NutanixGuestCreateDiskTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subtask.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
|
||||
@@ -120,7 +137,7 @@ func (self *SNutanixGuestDriver) CanKeepDetachDisk() bool {
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
return []string{}, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SNutanixGuestDriver) GetDeployStatus() ([]string, error) {
|
||||
|
||||
@@ -859,7 +859,7 @@ func (manager *SServerSkuManager) GetMatchedSku(regionId string, cpu int64, memM
|
||||
func (manager *SServerSkuManager) FetchSkuByNameAndProvider(name string, provider string, checkConsistency bool) (*SServerSku, error) {
|
||||
q := manager.Query().IsTrue("enabled")
|
||||
q = q.Equals("name", name)
|
||||
if utils.IsInStringArray(provider, []string{api.CLOUD_PROVIDER_ONECLOUD, api.CLOUD_PROVIDER_VMWARE}) {
|
||||
if utils.IsInStringArray(provider, []string{api.CLOUD_PROVIDER_ONECLOUD, api.CLOUD_PROVIDER_VMWARE, api.CLOUD_PROVIDER_NUTANIX}) {
|
||||
q = q.Filter(
|
||||
sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
@@ -284,7 +285,12 @@ func (self *ESXiGuestCreateDiskTask) OnInit(ctx context.Context, obj db.IStandal
|
||||
osProf := guest.GetOSProfile()
|
||||
d.Driver = osProf.DiskDriver
|
||||
}
|
||||
err = ivm.CreateDisk(ctx, disk.DiskSize, disk.Id, d.Driver)
|
||||
opts := cloudprovider.GuestDiskCreateOptions{
|
||||
SizeMb: disk.DiskSize,
|
||||
UUID: disk.Id,
|
||||
Driver: d.Driver,
|
||||
}
|
||||
_, err = ivm.CreateDisk(ctx, &opts)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(fmt.Sprintf("ivm.CreateDisk fail %s, error: %v", guest.GetName(), err)))
|
||||
return
|
||||
@@ -355,10 +361,84 @@ func (self *GuestCreateBackupDisksTask) CreateBackups(ctx context.Context, guest
|
||||
}
|
||||
}
|
||||
|
||||
type NutanixGuestCreateDiskTask struct {
|
||||
SGuestCreateDiskBaseTask
|
||||
}
|
||||
|
||||
func (self *NutanixGuestCreateDiskTask) taskFailed(ctx context.Context, err error) {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (self *NutanixGuestCreateDiskTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
guest := obj.(*models.SGuest)
|
||||
|
||||
ivm, err := guest.GetIVM()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, errors.Wrapf(err, "guest.GetIVM"))
|
||||
return
|
||||
}
|
||||
|
||||
disks, err := self.GetInputDisks()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, errors.Wrapf(err, "self.GetInputDisks"))
|
||||
return
|
||||
}
|
||||
for _, d := range disks {
|
||||
diskId := d.DiskId
|
||||
_disk, err := models.DiskManager.FetchById(diskId)
|
||||
if err != nil {
|
||||
errors.Wrapf(err, "DiskManager.FetchById(%s)", diskId)
|
||||
return
|
||||
}
|
||||
disk := _disk.(*models.SDisk)
|
||||
if disk.Status != api.DISK_INIT {
|
||||
self.taskFailed(ctx, errors.Errorf("Disk %s already created??(status=%s)", diskId, disk.Status))
|
||||
return
|
||||
}
|
||||
if len(d.Driver) == 0 {
|
||||
osProf := guest.GetOSProfile()
|
||||
d.Driver = osProf.DiskDriver
|
||||
}
|
||||
storage, err := disk.GetStorage()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, errors.Wrapf(err, "disk.GetStorage"))
|
||||
return
|
||||
}
|
||||
opts := cloudprovider.GuestDiskCreateOptions{
|
||||
SizeMb: disk.DiskSize,
|
||||
Driver: d.Driver,
|
||||
StorageId: storage.ExternalId,
|
||||
}
|
||||
externalId, err := ivm.CreateDisk(ctx, &opts)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, errors.Wrapf(err, "CreateDisk"))
|
||||
return
|
||||
}
|
||||
db.Update(disk, func() error {
|
||||
disk.ExternalId = externalId
|
||||
disk.Status = api.DISK_READY
|
||||
return nil
|
||||
})
|
||||
|
||||
err = self.attachDisk(ctx, disk, d.Driver, d.Cache, d.Mountpoint)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, errors.Wrapf(err, "attachDisk"))
|
||||
return
|
||||
}
|
||||
|
||||
storage.ClearSchedDescCache()
|
||||
db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(ctx), self.UserCred)
|
||||
db.OpsLog.LogAttachEvent(ctx, guest, disk, self.UserCred, disk.GetShortDesc(ctx))
|
||||
}
|
||||
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(GuestCreateBackupDisksTask{})
|
||||
taskman.RegisterTask(GuestCreateDiskTask{})
|
||||
taskman.RegisterTask(KVMGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(ManagedGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(ESXiGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(NutanixGuestCreateDiskTask{})
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -35,11 +35,11 @@ func init() {
|
||||
taskman.RegisterTask(NetworkSyncstatusTask{})
|
||||
}
|
||||
|
||||
func (self *NetworkSyncstatusTask) taskFail(ctx context.Context, net *models.SNetwork, reason jsonutils.JSONObject) {
|
||||
net.SetStatus(self.UserCred, api.NETWORK_STATUS_UNKNOWN, reason.String())
|
||||
db.OpsLog.LogEvent(net, db.ACT_SYNC_STATUS, reason, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, net, logclient.ACT_SYNC_STATUS, reason, self.UserCred, false)
|
||||
self.SetStageFailed(ctx, reason)
|
||||
func (self *NetworkSyncstatusTask) taskFail(ctx context.Context, net *models.SNetwork, err error) {
|
||||
net.SetStatus(self.UserCred, api.NETWORK_STATUS_UNKNOWN, err.Error())
|
||||
db.OpsLog.LogEvent(net, db.ACT_SYNC_STATUS, err, self.GetUserCred())
|
||||
logclient.AddActionLogWithStartable(self, net, logclient.ACT_SYNC_STATUS, err, self.UserCred, false)
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (self *NetworkSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
@@ -47,22 +47,19 @@ func (self *NetworkSyncstatusTask) OnInit(ctx context.Context, obj db.IStandalon
|
||||
|
||||
extNet, err := net.GetINetwork()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("fail to find ICloudNetwork for network %s", err)
|
||||
self.taskFail(ctx, net, jsonutils.NewString(msg))
|
||||
self.taskFail(ctx, net, errors.Wrapf(err, "net.GetINetwork"))
|
||||
return
|
||||
}
|
||||
|
||||
err = extNet.Refresh()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("fail to refresh ICloudNetwork status %s", err)
|
||||
self.taskFail(ctx, net, jsonutils.NewString(msg))
|
||||
self.taskFail(ctx, net, errors.Wrapf(err, "Refresh"))
|
||||
return
|
||||
}
|
||||
|
||||
err = net.SyncWithCloudNetwork(ctx, self.UserCred, extNet, nil, nil)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("fail to sync network status %s", err)
|
||||
self.taskFail(ctx, net, jsonutils.NewString(msg))
|
||||
self.taskFail(ctx, net, errors.Wrapf(err, "SyncWithCloudNetwork"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -972,10 +972,6 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return self.host.zone.region.updateInstance(self.InstanceId, "", "", "", "", userData)
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return self.host.zone.region.RenewInstance(self.InstanceId, bc)
|
||||
}
|
||||
|
||||
@@ -898,10 +898,6 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return self.host.zone.region.updateInstance(self.InstanceId, "", "", "", "", userData)
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return self.host.zone.region.RenewInstance(self.InstanceId, bc)
|
||||
}
|
||||
|
||||
@@ -1193,10 +1193,6 @@ func (self *SRegion) getPasswordData(instanceId string) (string, error) {
|
||||
return *ret.PasswordData, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -508,10 +508,6 @@ func (self *SClassicInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SClassicInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SClassicInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -1034,10 +1034,6 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -312,10 +312,6 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) MigrateVM(hostId string) error {
|
||||
input := api.GuestMigrateInput{}
|
||||
input.PreferHost = hostId
|
||||
|
||||
@@ -706,15 +706,6 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
_, err := self.host.zone.region.CreateDisk(self.host.zone.GetId(), uuid, driver, strconv.Itoa(sizeMb))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Instance.CreateDisk")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
_, err := self.host.zone.region.RenewVM(self.GetId(), &bc)
|
||||
if err != nil {
|
||||
|
||||
@@ -332,10 +332,6 @@ func (in *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -1283,7 +1283,8 @@ func (host *SHost) CloneVM(ctx context.Context, from *SVirtualMachine, snapshot
|
||||
}
|
||||
uuid := params.Disks[i].DiskId
|
||||
driver := params.Disks[i].Driver
|
||||
err := vm.CreateDisk(ctx, int(size), uuid, driver)
|
||||
opts := &cloudprovider.GuestDiskCreateOptions{SizeMb: int(size), UUID: uuid, Driver: driver}
|
||||
_, err := vm.CreateDisk(ctx, opts)
|
||||
if err != nil {
|
||||
log.Errorf("unable to add No.%d disk for vm %s", i, vm.GetId())
|
||||
return vm, nil
|
||||
|
||||
@@ -935,16 +935,16 @@ func (self *SVirtualMachine) getLayoutEx() *types.VirtualMachineFileLayoutEx {
|
||||
return vm.LayoutEx
|
||||
}
|
||||
|
||||
func (self *SVirtualMachine) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
if driver == "pvscsi" {
|
||||
driver = "scsi"
|
||||
func (self *SVirtualMachine) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
|
||||
if opts.Driver == "pvscsi" {
|
||||
opts.Driver = "scsi"
|
||||
}
|
||||
devs, err := self.FindController(ctx, driver)
|
||||
devs, err := self.FindController(ctx, opts.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if len(devs) == 0 {
|
||||
return self.createDriverAndDisk(ctx, sizeMb, uuid, driver)
|
||||
return "", self.createDriverAndDisk(ctx, opts.SizeMb, opts.UUID, opts.Driver)
|
||||
}
|
||||
numDevBelowCtrl := make([]int, len(devs))
|
||||
for i := range numDevBelowCtrl {
|
||||
@@ -965,13 +965,13 @@ func (self *SVirtualMachine) CreateDisk(ctx context.Context, sizeMb int, uuid st
|
||||
|
||||
// By default, the virtual SCSI controller is assigned to virtual device node (z:7),
|
||||
// so that device node is unavailable for hard disks or other devices.
|
||||
if unitNumber >= 7 && driver == "scsi" {
|
||||
if unitNumber >= 7 && opts.Driver == "scsi" {
|
||||
unitNumber++
|
||||
}
|
||||
|
||||
return self.createDiskInternal(ctx, SDiskConfig{
|
||||
SizeMb: int64(sizeMb),
|
||||
Uuid: uuid,
|
||||
return "", self.createDiskInternal(ctx, SDiskConfig{
|
||||
SizeMb: int64(opts.SizeMb),
|
||||
Uuid: opts.UUID,
|
||||
UnitNumber: int32(unitNumber),
|
||||
ControllerKey: ctrlKey,
|
||||
Key: diskKey,
|
||||
|
||||
@@ -527,10 +527,6 @@ func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -754,10 +754,6 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -763,10 +763,6 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return self.host.zone.region.RenewInstance(self.GetId(), bc)
|
||||
}
|
||||
|
||||
@@ -74,3 +74,7 @@ func (self *SInstanceBase) AllocatePublicIpAddress() (string, error) {
|
||||
func (self *SInstanceBase) GetOSArch() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceBase) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
|
||||
return "", errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateDisk")
|
||||
}
|
||||
|
||||
@@ -339,10 +339,6 @@ func (in *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -16,9 +16,13 @@ package nutanix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
@@ -204,11 +208,11 @@ func (self *SDisk) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) Delete(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) GetAccessPath() string {
|
||||
@@ -262,15 +266,38 @@ func (self *SDisk) GetStatus() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) Rebuild(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
ins, err := self.storage.zone.region.GetInstance(self.AttachedVMUUID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%s)", self.AttachedVMUUID)
|
||||
}
|
||||
for _, disk := range ins.VMDiskInfo {
|
||||
if disk.DiskAddress.VmdiskUUID == self.UUID {
|
||||
params := map[string]interface{}{
|
||||
"vm_disks": []map[string]interface{}{
|
||||
{
|
||||
"disk_address": disk.DiskAddress,
|
||||
"flash_mode_enabled": disk.FlashModeEnabled,
|
||||
"is_cdrom": disk.IsCdrom,
|
||||
"is_empty": disk.IsEmpty,
|
||||
"vm_disk_create": map[string]interface{}{
|
||||
"storage_container_uuid": disk.StorageContainerUUID,
|
||||
"size": sizeMb * 1024 * 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return self.storage.zone.region.update("vms", fmt.Sprintf("%s/disks/update", self.AttachedVMUUID), jsonutils.Marshal(params), nil)
|
||||
}
|
||||
}
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) GetTemplateId() string {
|
||||
|
||||
@@ -275,8 +275,94 @@ func (self *SHost) GetGlobalId() string {
|
||||
return self.UUID
|
||||
}
|
||||
|
||||
func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
image, err := self.zone.region.GetImage(opts.ExternalImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetImage")
|
||||
}
|
||||
disks := []map[string]interface{}{
|
||||
{
|
||||
"disk_address": map[string]interface{}{
|
||||
"device_bus": "ide",
|
||||
"device_index": 0,
|
||||
},
|
||||
"is_cdrom": true,
|
||||
"is_empty": true,
|
||||
},
|
||||
{
|
||||
"disk_address": map[string]interface{}{
|
||||
"device_bus": "scsi",
|
||||
"device_index": 0,
|
||||
},
|
||||
"is_cdrom": false,
|
||||
"vm_disk_clone": map[string]interface{}{
|
||||
"disk_address": map[string]string{
|
||||
"vmdisk_uuid": image.VMDiskID,
|
||||
},
|
||||
"minimum_size": image.VMDiskSize,
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, disk := range opts.DataDisks {
|
||||
disks = append(disks, map[string]interface{}{
|
||||
"disk_address": map[string]interface{}{
|
||||
"device_bus": "scsi",
|
||||
"device_index": i + 1,
|
||||
},
|
||||
"is_cdrom": false,
|
||||
"vm_disk_create": map[string]interface{}{
|
||||
"size": disk.SizeGB * 1024 * 1024 * 1024,
|
||||
"storage_container_uuid": disk.StorageExternalId,
|
||||
},
|
||||
})
|
||||
}
|
||||
nic := map[string]interface{}{
|
||||
"network_uuid": opts.ExternalVpcId,
|
||||
}
|
||||
if len(opts.IpAddr) > 0 {
|
||||
nic["requested_ip_address"] = opts.IpAddr
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"boot": map[string]interface{}{
|
||||
"boot_device_order": []string{"CDROM", "DISK", "NIC"},
|
||||
"uefi_boot": false,
|
||||
},
|
||||
"description": opts.Description,
|
||||
"hypervisor_type": "ACROPOLIS",
|
||||
"memory_mb": opts.MemoryMB,
|
||||
"name": opts.Name,
|
||||
"num_cores_per_vcpu": 1,
|
||||
"num_vcpus": opts.Cpu,
|
||||
"timezone": "UTC",
|
||||
"vm_customization_config": map[string]interface{}{
|
||||
"files_to_inject_list": []string{},
|
||||
"userdata": opts.UserData,
|
||||
},
|
||||
"vm_disks": disks,
|
||||
"vm_features": map[string]interface{}{
|
||||
"AGENT_VM": false,
|
||||
},
|
||||
"vm_nics": []map[string]interface{}{
|
||||
nic,
|
||||
},
|
||||
}
|
||||
ret := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
err = self.zone.region.post("vms", jsonutils.Marshal(params), &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resId, err := self.zone.region.cli.wait(ret.TaskUUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm, err := self.zone.region.GetInstance(resId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm.host = self
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessIp() string {
|
||||
@@ -410,5 +496,17 @@ func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
}
|
||||
|
||||
func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
vpcs, err := self.zone.region.GetIVpcs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudWire{}
|
||||
for i := range vpcs {
|
||||
wires, err := vpcs[i].GetIWires()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetIWires")
|
||||
}
|
||||
ret = append(ret, wires...)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@ package nutanix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -157,3 +162,70 @@ func (self *SRegion) GetImage(id string) (*SImage, error) {
|
||||
params.Set("include_vm_disk_paths", "true")
|
||||
return image, self.get("images", id, params, image)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateImage(storageId string, opts *cloudprovider.SImageCreateOption, sizeBytes int64, body io.Reader, callback func(float32)) (*SImage, error) {
|
||||
params := map[string]interface{}{
|
||||
"image_type": "DISK_IMAGE",
|
||||
"name": opts.ImageName,
|
||||
"annotation": opts.OsDistribution,
|
||||
}
|
||||
ret := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
err := self.post("images", jsonutils.Marshal(params), &ret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "create image")
|
||||
}
|
||||
imageId := ""
|
||||
err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) {
|
||||
task, err := self.GetTask(ret.TaskUUID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, entity := range task.EntityList {
|
||||
imageId = entity.EntityID
|
||||
}
|
||||
log.Debugf("task %s %s status: %s", task.OperationType, task.UUID, task.ProgressStatus)
|
||||
if task.ProgressStatus == "Succeeded" {
|
||||
for _, entity := range task.EntityList {
|
||||
imageId = entity.EntityID
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := http.Header{}
|
||||
header.Set("X-Nutanix-Destination-Container", storageId)
|
||||
header.Set("Content-Type", "application/octet-stream")
|
||||
header.Set("Content-Length", fmt.Sprintf("%d", sizeBytes))
|
||||
reader := multicloud.NewProgress(sizeBytes, 90, body, callback)
|
||||
resp, err := self.upload("images", fmt.Sprintf("%s/upload", imageId), header, reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "upload")
|
||||
}
|
||||
resp.Unmarshal(&ret)
|
||||
err = cloudprovider.Wait(time.Second*5, time.Minute*10, func() (bool, error) {
|
||||
task, err := self.GetTask(ret.TaskUUID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if callback != nil {
|
||||
callback(90 + float32(task.PercentageComplete)*0.1)
|
||||
}
|
||||
log.Debugf("task %s %s status: %s", task.OperationType, task.UUID, task.ProgressStatus)
|
||||
if task.ProgressStatus == "Succeeded" {
|
||||
for _, entity := range task.EntityList {
|
||||
imageId = entity.EntityID
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "wait image ready")
|
||||
}
|
||||
return self.GetImage(imageId)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -37,6 +39,43 @@ type VMFeatures struct {
|
||||
AGENTVM bool `json:"AGENT_VM"`
|
||||
}
|
||||
|
||||
type DiskAddress struct {
|
||||
DeviceBus string `json:"device_bus"`
|
||||
DeviceIndex int `json:"device_index"`
|
||||
DiskLabel string `json:"disk_label"`
|
||||
NdfsFilepath string `json:"ndfs_filepath"`
|
||||
VmdiskUUID string `json:"vmdisk_uuid"`
|
||||
DeviceUUID string `json:"device_uuid"`
|
||||
}
|
||||
|
||||
type VMDiskInfo struct {
|
||||
IsCdrom bool `json:"is_cdrom"`
|
||||
IsEmpty bool `json:"is_empty"`
|
||||
FlashModeEnabled bool `json:"flash_mode_enabled"`
|
||||
IsScsiPassthrough bool `json:"is_scsi_passthrough"`
|
||||
IsHotRemoveEnabled bool `json:"is_hot_remove_enabled"`
|
||||
IsThinProvisioned bool `json:"is_thin_provisioned"`
|
||||
Shared bool `json:"shared"`
|
||||
SourceDiskAddress SourceDiskAddress `json:"source_disk_address,omitempty"`
|
||||
StorageContainerUUID string `json:"storage_container_uuid,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
DataSourceURL string `json:"data_source_url"`
|
||||
DiskAddress DiskAddress `json:"disk_address,omitempty"`
|
||||
}
|
||||
|
||||
type SourceDiskAddress struct {
|
||||
VmdiskUUID string `json:"vmdisk_uuid"`
|
||||
}
|
||||
|
||||
type VMNics struct {
|
||||
MacAddress string `json:"mac_address"`
|
||||
NetworkUUID string `json:"network_uuid"`
|
||||
NicUUID string `json:"nic_uuid"`
|
||||
Model string `json:"model"`
|
||||
VlanMode string `json:"vlan_mode"`
|
||||
IsConnected bool `json:"is_connected"`
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
multicloud.STagBase
|
||||
multicloud.SInstanceBase
|
||||
@@ -58,6 +97,9 @@ type SInstance struct {
|
||||
VMFeatures VMFeatures `json:"vm_features"`
|
||||
VMLogicalTimestamp int `json:"vm_logical_timestamp"`
|
||||
MachineType string `json:"machine_type"`
|
||||
|
||||
VMDiskInfo []VMDiskInfo `json:"vm_disk_info"`
|
||||
VMNics []VMNics `json:"vm_nics"`
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstances() ([]SInstance, error) {
|
||||
@@ -71,6 +113,8 @@ func (self *SRegion) GetInstances() ([]SInstance, error) {
|
||||
func (self *SRegion) GetInstance(id string) (*SInstance, error) {
|
||||
vm := &SInstance{}
|
||||
params := url.Values{}
|
||||
params.Set("include_vm_disk_config", "true")
|
||||
params.Set("include_vm_nic_config", "true")
|
||||
return vm, self.get("vms", id, params, vm)
|
||||
}
|
||||
|
||||
@@ -86,28 +130,74 @@ func (self *SInstance) GetGlobalId() string {
|
||||
return self.UUID
|
||||
}
|
||||
|
||||
func (self *SInstance) Refresh() error {
|
||||
ins, err := self.host.zone.region.GetInstance(self.UUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ins)
|
||||
}
|
||||
|
||||
func (self *SInstance) AssignSecurityGroup(id string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
|
||||
driver := opts.Driver
|
||||
if !utils.IsInStringArray(driver, []string{"ide", "scsi", "pci", "sata"}) {
|
||||
driver = "scsi"
|
||||
}
|
||||
idx := -1
|
||||
for _, disk := range self.VMDiskInfo {
|
||||
if disk.DiskAddress.DeviceBus == driver && disk.DiskAddress.DeviceIndex > idx && disk.DiskAddress.DeviceIndex == idx+1 {
|
||||
idx = disk.DiskAddress.DeviceIndex
|
||||
}
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"vm_disks": []map[string]interface{}{
|
||||
{
|
||||
"is_cdrom": false,
|
||||
"disk_address": map[string]interface{}{
|
||||
"device_bus": driver,
|
||||
"device_index": idx + 1,
|
||||
},
|
||||
"vm_disk_create": map[string]interface{}{
|
||||
"storage_container_uuid": opts.StorageId,
|
||||
"size": opts.SizeMb * 1024 * 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ret := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
res := fmt.Sprintf("vms/%s/disks/attach", self.UUID)
|
||||
err := self.host.zone.region.post(res, jsonutils.Marshal(params), &ret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return self.host.zone.region.cli.wait(ret.TaskUUID)
|
||||
}
|
||||
|
||||
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error {
|
||||
params := map[string]interface{}{
|
||||
"memory_mb": opts.MemoryMB,
|
||||
"num_cores_per_vcpu": self.NumCoresPerVcpu,
|
||||
"num_vcpus": opts.Cpu / self.NumCoresPerVcpu,
|
||||
}
|
||||
return self.host.zone.region.update("vms", self.UUID, jsonutils.Marshal(params), nil)
|
||||
}
|
||||
|
||||
func (self *SInstance) DeleteVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return self.host.zone.region.DeleteVM(self.UUID)
|
||||
}
|
||||
|
||||
func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
@@ -142,8 +232,17 @@ func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetInstanceDisks")
|
||||
}
|
||||
cdroms := []string{}
|
||||
for _, disk := range self.VMDiskInfo {
|
||||
if disk.IsCdrom && len(disk.DiskAddress.VmdiskUUID) > 0 {
|
||||
cdroms = append(cdroms, disk.DiskAddress.VmdiskUUID)
|
||||
}
|
||||
}
|
||||
ret := []cloudprovider.ICloudDisk{}
|
||||
for i := range disks {
|
||||
if utils.IsInStringArray(disks[i].UUID, cdroms) { // skip cdrom disk
|
||||
continue
|
||||
}
|
||||
storage, err := self.host.zone.GetIStorageById(disks[i].StorageContainerUUID)
|
||||
if err != nil {
|
||||
log.Errorf("can not found disk %s storage %s", disks[i].DiskAddress, disks[i].StorageContainerUUID)
|
||||
@@ -251,7 +350,7 @@ func (self *SInstance) GetVdi() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
@@ -259,11 +358,11 @@ func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
}
|
||||
|
||||
func (self *SInstance) StartVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
return self.host.zone.region.SetInstancePowerState(self.UUID, "on")
|
||||
}
|
||||
|
||||
func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
return self.host.zone.region.SetInstancePowerState(self.UUID, "acpi_shutdown")
|
||||
}
|
||||
|
||||
func (self *SInstance) UpdateUserData(userData string) error {
|
||||
@@ -273,3 +372,20 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) SetInstancePowerState(id string, state string) error {
|
||||
res := fmt.Sprintf("vms/%s/set_power_state", id)
|
||||
ret := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
err := self.post(res, jsonutils.Marshal(map[string]string{"transition": state}), &ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = self.cli.wait(ret.TaskUUID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteVM(id string) error {
|
||||
return self.delete("vms", id)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
package nutanix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -58,11 +61,24 @@ func (self *SNetwork) IsEmulated() bool {
|
||||
return len(self.Range) == 0
|
||||
}
|
||||
|
||||
func (self *SNetwork) Refresh() error {
|
||||
vpc, err := self.wire.vpc.region.GetVpc(self.wire.vpc.GetGlobalId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pool := range vpc.IPConfig.Pool {
|
||||
if pool.Range == self.Range {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SNetwork) Delete() error {
|
||||
if len(self.Range) == 0 {
|
||||
return nil
|
||||
}
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return self.wire.vpc.region.DeleteNetwork(self.wire.vpc.UUID, self.Range)
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
@@ -81,14 +97,24 @@ func (self *SNetwork) GetIpStart() string {
|
||||
if info := strings.Split(self.Range, " "); len(info) == 2 {
|
||||
return info[0]
|
||||
}
|
||||
return "0.0.0.1"
|
||||
cidr := self.wire.vpc.GetCidrBlock()
|
||||
if len(cidr) == 0 {
|
||||
cidr = "0.0.0.0/0"
|
||||
}
|
||||
_range, _ := netutils.NewIPV4Prefix(cidr)
|
||||
return _range.ToIPRange().StartIp().StepUp().String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpEnd() string {
|
||||
if info := strings.Split(self.Range, " "); len(info) == 2 {
|
||||
return info[1]
|
||||
}
|
||||
return "255.255.255.254"
|
||||
cidr := self.wire.vpc.GetCidrBlock()
|
||||
if len(cidr) == 0 {
|
||||
cidr = "0.0.0.0/0"
|
||||
}
|
||||
_range, _ := netutils.NewIPV4Prefix(cidr)
|
||||
return _range.ToIPRange().EndIp().StepDown().String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) Contains(_ip string) bool {
|
||||
@@ -118,3 +144,40 @@ func (self *SNetwork) GetServerType() string {
|
||||
func (self *SNetwork) GetStatus() string {
|
||||
return api.NETWORK_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNetwork(vpcId string, opts *cloudprovider.SNetworkCreateOptions) (*SNetwork, error) {
|
||||
vpc, err := self.GetVpc(vpcId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetVpc")
|
||||
}
|
||||
cidr, _ := netutils.NewIPV4Prefix(opts.Cidr)
|
||||
_range := fmt.Sprintf("%s %s", cidr.ToIPRange().StartIp().StepUp(), cidr.ToIPRange().EndIp().StepDown())
|
||||
pool := SPool{Range: _range}
|
||||
vpc.IPConfig.Pool = append(vpc.IPConfig.Pool, pool)
|
||||
err = self.update("networks", vpcId, jsonutils.Marshal(vpc), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wire := vpc.getWire()
|
||||
return &SNetwork{wire: wire, Range: _range}, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNetwork(vpcId string, _range string) error {
|
||||
vpc, err := self.GetVpc(vpcId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pools, find := []SPool{}, false
|
||||
for i := range vpc.IPConfig.Pool {
|
||||
if vpc.IPConfig.Pool[i].Range == _range {
|
||||
find = true
|
||||
continue
|
||||
}
|
||||
pools = append(pools, vpc.IPConfig.Pool[i])
|
||||
}
|
||||
if !find {
|
||||
return nil
|
||||
}
|
||||
vpc.IPConfig.Pool = pools
|
||||
return self.update("networks", vpcId, jsonutils.Marshal(vpc), nil)
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@ package nutanix
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -29,8 +32,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
NUTANIX_VERSION_V2 = "PrismGateway/services/rest/v2.0"
|
||||
NUTANIX_VERSION_V3 = "api/nutanix/v3"
|
||||
NUTANIX_VERSION_V2 = "v2.0"
|
||||
NUTANIX_VERSION_V0_8 = "v0.8"
|
||||
NUTANIX_VERSION_V3 = "v3"
|
||||
|
||||
CLOUD_PROVIDER_NUTANIX = api.CLOUD_PROVIDER_NUTANIX
|
||||
)
|
||||
@@ -89,8 +93,8 @@ func (self *SNutanixClient) GetAccountId() string {
|
||||
|
||||
func (self *SNutanixClient) GetCapabilities() []string {
|
||||
return []string{
|
||||
cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX,
|
||||
cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX,
|
||||
cloudprovider.CLOUD_CAPABILITY_COMPUTE,
|
||||
cloudprovider.CLOUD_CAPABILITY_NETWORK,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +103,19 @@ func (self *SNutanixClient) auth() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) _getBaseDomain(version string) string {
|
||||
if len(version) == 0 {
|
||||
version = NUTANIX_VERSION_V2
|
||||
}
|
||||
return fmt.Sprintf("https://%s:%d/api/nutanix/%s", self.host, self.port, version)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) getBaseDomain() string {
|
||||
return fmt.Sprintf("https://%s:%d/%s", self.host, self.port, NUTANIX_VERSION_V2)
|
||||
return self._getBaseDomain("")
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) getBaseDomainV0_8() string {
|
||||
return self._getBaseDomain(NUTANIX_VERSION_V0_8)
|
||||
}
|
||||
|
||||
func (cli *SNutanixClient) getDefaultClient() *http.Client {
|
||||
@@ -124,6 +139,36 @@ func (self *SNutanixClient) _list(res string, params url.Values) (jsonutils.JSON
|
||||
return self.jsonRequest(httputils.GET, url, nil)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) _post(res string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
url := fmt.Sprintf("%s/%s", self.getBaseDomain(), res)
|
||||
if body == nil {
|
||||
body = jsonutils.NewDict()
|
||||
}
|
||||
return self.jsonRequest(httputils.POST, url, body)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) _update(res, id string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
url := fmt.Sprintf("%s/%s/%s", self.getBaseDomain(), res, id)
|
||||
if body == nil {
|
||||
body = jsonutils.NewDict()
|
||||
}
|
||||
return self.jsonRequest(httputils.PUT, url, body)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) _upload(res, id string, header http.Header, body io.Reader) (jsonutils.JSONObject, error) {
|
||||
url := fmt.Sprintf("%s/%s/%s", self.getBaseDomainV0_8(), res, id)
|
||||
return self.rawRequest(httputils.PUT, url, header, body)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) upload(res, id string, header http.Header, body io.Reader) (jsonutils.JSONObject, error) {
|
||||
return self._upload(res, id, header, body)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) _delete(res, id string) (jsonutils.JSONObject, error) {
|
||||
url := fmt.Sprintf("%s/%s/%s", self.getBaseDomain(), res, id)
|
||||
return self.jsonRequest(httputils.DELETE, url, nil)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) list(res string, params url.Values, retVal interface{}) (int, error) {
|
||||
resp, err := self._list(res, params)
|
||||
if err != nil {
|
||||
@@ -142,6 +187,87 @@ func (self *SNutanixClient) list(res string, params url.Values, retVal interface
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) delete(res, id string) error {
|
||||
resp, err := self._delete(res, id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "delete %s", res)
|
||||
}
|
||||
if resp != nil && resp.Contains("task_uuid") {
|
||||
task := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
resp.Unmarshal(&task)
|
||||
if len(task.TaskUUID) > 0 {
|
||||
_, err = self.wait(task.TaskUUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) wait(taskId string) (string, error) {
|
||||
resId := ""
|
||||
err := cloudprovider.Wait(time.Second*5, time.Minute*10, func() (bool, error) {
|
||||
task, err := self.getTask(taskId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, entity := range task.EntityList {
|
||||
if len(entity.EntityID) > 0 {
|
||||
resId = entity.EntityID
|
||||
}
|
||||
}
|
||||
log.Debugf("task %s %s status: %s", task.OperationType, task.UUID, task.ProgressStatus)
|
||||
if task.ProgressStatus == "Succeeded" {
|
||||
return true, nil
|
||||
}
|
||||
if task.ProgressStatus == "Failed" {
|
||||
return false, errors.Errorf(jsonutils.Marshal(task.MetaResponse).String())
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
return resId, errors.Wrapf(err, "wait task %s", taskId)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) update(res, id string, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
resp, err := self._update(res, id, body)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "update %s/%s", res, id)
|
||||
}
|
||||
task := struct {
|
||||
TaskUUID string
|
||||
}{}
|
||||
resp.Unmarshal(&task)
|
||||
if len(task.TaskUUID) > 0 {
|
||||
_, err = self.wait(task.TaskUUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if retVal != nil {
|
||||
return resp.Unmarshal(retVal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) post(res string, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
resp, err := self._post(res, body)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "post %s", res)
|
||||
}
|
||||
if retVal != nil {
|
||||
if resp.Contains("entities") {
|
||||
err = resp.Unmarshal(retVal, "entities")
|
||||
} else {
|
||||
err = resp.Unmarshal(retVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) listAll(res string, params url.Values, retVal interface{}) error {
|
||||
if len(params) == 0 {
|
||||
params = url.Values{}
|
||||
@@ -206,7 +332,23 @@ func _jsonRequest(cli *http.Client, method httputils.THttpMethod, url string, he
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) rawRequest(method httputils.THttpMethod, url string, header http.Header, body io.Reader) (jsonutils.JSONObject, error) {
|
||||
client := self.getDefaultClient()
|
||||
_resp, err := _rawRequest(client, method, url, header, body, false)
|
||||
_, resp, err := httputils.ParseJSONResponse("", _resp, err, self.debug)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func _rawRequest(cli *http.Client, method httputils.THttpMethod, url string, header http.Header, body io.Reader, debug bool) (*http.Response, error) {
|
||||
return httputils.Request(cli, context.Background(), method, url, header, body, debug)
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
region := &SRegion{cli: self}
|
||||
return []cloudprovider.ICloudRegion{region}
|
||||
}
|
||||
|
||||
func (self *SNutanixClient) getTask(id string) (*STask, error) {
|
||||
task := &STask{}
|
||||
return task, self.get("tasks", id, nil, task)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@ package nutanix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -68,7 +71,7 @@ func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreat
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
return self.CreateVpc(opts)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCapabilities() []string {
|
||||
@@ -196,3 +199,24 @@ func (self *SRegion) get(res, id string, params url.Values, retVal interface{})
|
||||
func (self *SRegion) listAll(res string, params url.Values, retVal interface{}) error {
|
||||
return self.cli.listAll(res, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) post(res string, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
return self.cli.post(res, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) delete(res string, id string) error {
|
||||
return self.cli.delete(res, id)
|
||||
}
|
||||
|
||||
func (self *SRegion) update(res string, id string, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
return self.cli.update(res, id, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) upload(res string, id string, header http.Header, body io.Reader) (jsonutils.JSONObject, error) {
|
||||
return self.cli.upload(res, id, header, body)
|
||||
}
|
||||
|
||||
func (self *SRegion) getTask(id string) (*STask, error) {
|
||||
task := &STask{}
|
||||
return task, self.get("tasks", id, nil, task)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/nutanix"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
@@ -44,4 +47,34 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageUploadOptions struct {
|
||||
STOREG_ID string
|
||||
NAME string
|
||||
FILE string
|
||||
}
|
||||
|
||||
shellutils.R(&ImageUploadOptions{}, "image-upload", "upload host", func(cli *nutanix.SRegion, args *ImageUploadOptions) error {
|
||||
fi, err := os.Open(args.FILE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
stat, _ := fi.Stat()
|
||||
image, err := cli.CreateImage(
|
||||
args.STOREG_ID,
|
||||
&cloudprovider.SImageCreateOption{
|
||||
ImageName: args.NAME,
|
||||
},
|
||||
stat.Size(),
|
||||
fi,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/nutanix"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type TaskListOptions struct {
|
||||
}
|
||||
shellutils.R(&TaskListOptions{}, "task-list", "list task", func(cli *nutanix.SRegion, args *TaskListOptions) error {
|
||||
tasks, err := cli.GetTasks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(tasks, 0, 0, 0, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type TaskIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
shellutils.R(&TaskIdOptions{}, "task-show", "show task", func(cli *nutanix.SRegion, args *TaskIdOptions) error {
|
||||
task, err := cli.GetTask(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(task)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/nutanix"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
@@ -44,4 +45,28 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&VpcIdOptions{}, "vpc-delete", "delete vpc", func(cli *nutanix.SRegion, args *VpcIdOptions) error {
|
||||
return cli.DeleteVpc(args.ID)
|
||||
})
|
||||
|
||||
type VpcCreateOptions struct {
|
||||
Name string
|
||||
Desc string
|
||||
CIDR string
|
||||
}
|
||||
|
||||
shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *nutanix.SRegion, args *VpcCreateOptions) error {
|
||||
opts := cloudprovider.VpcCreateOptions{
|
||||
NAME: args.Name,
|
||||
CIDR: args.CIDR,
|
||||
Desc: args.Desc,
|
||||
}
|
||||
vpc, err := cli.CreateVpc(&opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(vpc)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -316,3 +316,17 @@ func (self *SStorage) GetStorageType() string {
|
||||
func (self *SStorage) IsSysDiskStore() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
zones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetIZones")
|
||||
}
|
||||
for i := range zones {
|
||||
storage, err := zones[i].GetIStorageById(id)
|
||||
if err == nil && storage != nil {
|
||||
return storage, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
|
||||
}
|
||||
|
||||
@@ -18,11 +18,16 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/esxi/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/image"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
type SStoragecache struct {
|
||||
@@ -74,9 +79,6 @@ func (self *SStoragecache) GetIImageById(id string) (cloudprovider.ICloudImage,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if image.StorageContainerUUID != self.storage.GetGlobalId() {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
image.cache = self
|
||||
return image, nil
|
||||
}
|
||||
@@ -93,8 +95,23 @@ func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imag
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, opts *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region, "")
|
||||
|
||||
meta, reader, size, err := modules.Images.Download(s, opts.ImageId, string(qemuimg.QCOW2), false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Infof("meta data %s", meta)
|
||||
image, err := self.region.CreateImage(self.storage.StorageContainerUUID, opts, size, reader, callback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if callback != nil {
|
||||
callback(100.0)
|
||||
}
|
||||
image.cache = self
|
||||
return image.GetGlobalId(), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 nutanix
|
||||
|
||||
type MetaRequest struct {
|
||||
MethodName string `json:"method_name"`
|
||||
}
|
||||
|
||||
type EntityList struct {
|
||||
EntityID string `json:"entity_id"`
|
||||
EntityType string `json:"entity_type"`
|
||||
EntityName interface{} `json:"entity_name"`
|
||||
}
|
||||
|
||||
type MetaResponse struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
ErrorDetail string `json:"error_detail"`
|
||||
}
|
||||
|
||||
type STask struct {
|
||||
UUID string `json:"uuid"`
|
||||
MetaRequest MetaRequest `json:"meta_request"`
|
||||
MetaResponse MetaResponse `json:"meta_response,omitempty"`
|
||||
CreateTimeUsecs int64 `json:"create_time_usecs"`
|
||||
StartTimeUsecs int64 `json:"start_time_usecs"`
|
||||
CompleteTimeUsecs int64 `json:"complete_time_usecs"`
|
||||
LastUpdatedTimeUsecs int64 `json:"last_updated_time_usecs"`
|
||||
EntityList []EntityList `json:"entity_list,omitempty"`
|
||||
OperationType string `json:"operation_type"`
|
||||
Message string `json:"message"`
|
||||
PercentageComplete int `json:"percentage_complete"`
|
||||
ProgressStatus string `json:"progress_status"`
|
||||
ClusterUUID string `json:"cluster_uuid"`
|
||||
SubtaskUUIDList []string `json:"subtask_uuid_list,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SRegion) GetTasks() ([]STask, error) {
|
||||
tasks := []STask{}
|
||||
return tasks, self.post("tasks/list", nil, &tasks)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetTask(id string) (*STask, error) {
|
||||
return self.cli.getTask(id)
|
||||
}
|
||||
@@ -17,6 +17,10 @@ package nutanix
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
@@ -26,15 +30,17 @@ import (
|
||||
type DhcpOptions struct {
|
||||
}
|
||||
|
||||
type SPool struct {
|
||||
Range string `json:"range"`
|
||||
}
|
||||
|
||||
type IPConfig struct {
|
||||
NetworkAddress string `json:"network_address"`
|
||||
PrefixLength int `json:"prefix_length"`
|
||||
DefaultGateway string `json:"default_gateway"`
|
||||
DhcpOptions DhcpOptions `json:"dhcp_options"`
|
||||
Pool []struct {
|
||||
Range string `json:"range"`
|
||||
} `json:"pool"`
|
||||
DhcpServerAddress string `json:"dhcp_server_address"`
|
||||
NetworkAddress string `json:"network_address"`
|
||||
PrefixLength int `json:"prefix_length"`
|
||||
DefaultGateway string `json:"default_gateway"`
|
||||
DhcpOptions DhcpOptions `json:"dhcp_options"`
|
||||
Pool []SPool `json:"pool"`
|
||||
DhcpServerAddress string `json:"dhcp_server_address"`
|
||||
}
|
||||
|
||||
type SVpc struct {
|
||||
@@ -63,7 +69,7 @@ func (self *SVpc) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (self *SVpc) Delete() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return self.region.DeleteVpc(self.UUID)
|
||||
}
|
||||
|
||||
func (self *SVpc) GetCidrBlock() string {
|
||||
@@ -91,8 +97,12 @@ func (self *SRegion) GetVpcs() ([]SVpc, error) {
|
||||
return vpcs, err
|
||||
}
|
||||
|
||||
func (self *SVpc) getWire() *SWire {
|
||||
return &SWire{vpc: self}
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
wire := &SWire{vpc: self}
|
||||
wire := self.getWire()
|
||||
return []cloudprovider.ICloudWire{wire}, nil
|
||||
}
|
||||
|
||||
@@ -125,3 +135,32 @@ func (self *SRegion) GetVpc(id string) (*SVpc, error) {
|
||||
vpc := &SVpc{region: self}
|
||||
return vpc, self.get("networks", id, url.Values{}, vpc)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateVpc(opts *cloudprovider.VpcCreateOptions) (*SVpc, error) {
|
||||
ipConfig := map[string]interface{}{}
|
||||
if len(opts.CIDR) > 0 {
|
||||
if addrs := strings.Split(opts.CIDR, "/"); len(addrs) == 2 {
|
||||
ipConfig["network_address"] = addrs[0]
|
||||
ipConfig["prefix_length"], _ = strconv.Atoi(addrs[1])
|
||||
}
|
||||
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"name": opts.NAME,
|
||||
"annotation": opts.Desc,
|
||||
"ip_config": ipConfig,
|
||||
"vlan_id": 0,
|
||||
}
|
||||
ret := struct {
|
||||
NetworkUUID string
|
||||
}{}
|
||||
err := self.post("networks", jsonutils.Marshal(params), &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return self.GetVpc(ret.NetworkUUID)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteVpc(id string) error {
|
||||
return self.delete("networks", id)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package nutanix
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
@@ -39,8 +41,16 @@ func (self *SWire) GetGlobalId() string {
|
||||
return self.vpc.GetGlobalId()
|
||||
}
|
||||
|
||||
func (self *SWire) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SWire) CreateINetwork(opts *cloudprovider.SNetworkCreateOptions) (cloudprovider.ICloudNetwork, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
network, err := self.vpc.region.CreateNetwork(self.vpc.UUID, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CreateNetwork")
|
||||
}
|
||||
return network, nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetBandwidth() int {
|
||||
|
||||
@@ -820,10 +820,6 @@ func (instance *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -945,10 +945,6 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return self.host.zone.region.RenewInstances([]string{self.InstanceId}, bc)
|
||||
}
|
||||
|
||||
@@ -555,10 +555,6 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
// return self.host.zone.region
|
||||
return self.host.zone.region.RenewInstance(self.GetId(), bc)
|
||||
|
||||
@@ -614,10 +614,6 @@ func (instance *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user