Merge pull request #13216 from ioito/feat/qx-nutanix-op

feat(region): support nutanix
This commit is contained in:
Zexi Li
2022-01-22 18:23:42 +08:00
committed by GitHub
42 changed files with 984 additions and 154 deletions
+1
View File
@@ -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}
+8
View File
@@ -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
+1 -1
View File
@@ -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
+20 -12
View File
@@ -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)
+21 -4
View File
@@ -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) {
+1 -1
View File
@@ -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),
)
+81 -1
View File
@@ -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{})
}
+9 -12
View File
@@ -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
}
-4
View File
@@ -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)
}
-4
View File
@@ -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)
}
-4
View File
@@ -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
}
-4
View File
@@ -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
}
-4
View File
@@ -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
}
-4
View File
@@ -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
-9
View File
@@ -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 {
-4
View File
@@ -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
}
+2 -1
View File
@@ -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
+10 -10
View File
@@ -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,
-4
View File
@@ -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
}
-4
View File
@@ -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
}
-4
View File
@@ -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)
}
+4
View File
@@ -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")
}
-4
View File
@@ -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
}
+32 -5
View File
@@ -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 {
+101 -3
View File
@@ -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
}
+72
View File
@@ -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)
}
+126 -10
View File
@@ -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)
}
+66 -3
View File
@@ -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)
}
+147 -5
View File
@@ -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)
}
+25 -1
View File
@@ -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)
}
+33
View File
@@ -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
})
}
+47
View File
@@ -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
})
}
+25
View File
@@ -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
})
}
+14
View File
@@ -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)
}
+22 -5
View File
@@ -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) {
+56
View File
@@ -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)
}
+49 -10
View File
@@ -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)
}
+11 -1
View File
@@ -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 {
-4
View File
@@ -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
}
-4
View File
@@ -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)
}
-4
View File
@@ -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)
-4
View File
@@ -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
}