yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/nutanix/instance.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nutanix 16 17 import ( 18 "context" 19 "fmt" 20 "net/url" 21 "sort" 22 "strings" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 "yunion.io/x/pkg/errors" 27 "yunion.io/x/pkg/utils" 28 29 "yunion.io/x/cloudmux/pkg/apis" 30 api "yunion.io/x/cloudmux/pkg/apis/compute" 31 "yunion.io/x/cloudmux/pkg/cloudprovider" 32 "yunion.io/x/cloudmux/pkg/multicloud" 33 ) 34 35 type Boot struct { 36 UefiBoot bool `json:"uefi_boot"` 37 } 38 39 type VMFeatures struct { 40 VGACONSOLE bool `json:"VGA_CONSOLE"` 41 AGENTVM bool `json:"AGENT_VM"` 42 } 43 44 type DiskAddress struct { 45 DeviceBus string `json:"device_bus"` 46 DeviceIndex int `json:"device_index"` 47 DiskLabel string `json:"disk_label"` 48 NdfsFilepath string `json:"ndfs_filepath"` 49 VmdiskUUID string `json:"vmdisk_uuid"` 50 DeviceUUID string `json:"device_uuid"` 51 } 52 53 type VMDiskInfo struct { 54 IsCdrom bool `json:"is_cdrom"` 55 IsEmpty bool `json:"is_empty"` 56 FlashModeEnabled bool `json:"flash_mode_enabled"` 57 IsScsiPassthrough bool `json:"is_scsi_passthrough"` 58 IsHotRemoveEnabled bool `json:"is_hot_remove_enabled"` 59 IsThinProvisioned bool `json:"is_thin_provisioned"` 60 Shared bool `json:"shared"` 61 SourceDiskAddress SourceDiskAddress `json:"source_disk_address,omitempty"` 62 StorageContainerUUID string `json:"storage_container_uuid,omitempty"` 63 Size int64 `json:"size,omitempty"` 64 DataSourceURL string `json:"data_source_url"` 65 DiskAddress DiskAddress `json:"disk_address,omitempty"` 66 } 67 68 type SourceDiskAddress struct { 69 VmdiskUUID string `json:"vmdisk_uuid"` 70 } 71 72 type VMNics struct { 73 MacAddress string `json:"mac_address"` 74 NetworkUUID string `json:"network_uuid"` 75 NicUUID string `json:"nic_uuid"` 76 Model string `json:"model"` 77 VlanMode string `json:"vlan_mode"` 78 IsConnected bool `json:"is_connected"` 79 } 80 81 type SInstance struct { 82 multicloud.STagBase 83 multicloud.SInstanceBase 84 85 host *SHost 86 87 AllowLiveMigrate bool `json:"allow_live_migrate"` 88 GpusAssigned bool `json:"gpus_assigned"` 89 Boot Boot `json:"boot"` 90 HaPriority int `json:"ha_priority"` 91 HostUUID string `json:"host_uuid"` 92 MemoryMb int `json:"memory_mb"` 93 Name string `json:"name"` 94 NumCoresPerVcpu int `json:"num_cores_per_vcpu"` 95 NumVcpus int `json:"num_vcpus"` 96 PowerState string `json:"power_state"` 97 Timezone string `json:"timezone"` 98 UUID string `json:"uuid"` 99 VMFeatures VMFeatures `json:"vm_features"` 100 VMLogicalTimestamp int `json:"vm_logical_timestamp"` 101 MachineType string `json:"machine_type"` 102 103 VMDiskInfo []VMDiskInfo `json:"vm_disk_info"` 104 VMNics []VMNics `json:"vm_nics"` 105 } 106 107 func (self *SRegion) GetInstances() ([]SInstance, error) { 108 vms := []SInstance{} 109 params := url.Values{} 110 params.Set("include_vm_disk_config", "true") 111 params.Set("include_vm_nic_config", "true") 112 return vms, self.listAll("vms", params, &vms) 113 } 114 115 func (self *SRegion) GetInstance(id string) (*SInstance, error) { 116 vm := &SInstance{} 117 params := url.Values{} 118 params.Set("include_vm_disk_config", "true") 119 params.Set("include_vm_nic_config", "true") 120 return vm, self.get("vms", id, params, vm) 121 } 122 123 func (self *SInstance) GetName() string { 124 return self.Name 125 } 126 127 func (self *SInstance) GetId() string { 128 return self.UUID 129 } 130 131 func (self *SInstance) GetGlobalId() string { 132 return self.UUID 133 } 134 135 func (self *SInstance) Refresh() error { 136 ins, err := self.host.zone.region.GetInstance(self.UUID) 137 if err != nil { 138 return err 139 } 140 return jsonutils.Update(self, ins) 141 } 142 143 func (self *SInstance) AssignSecurityGroup(id string) error { 144 return cloudprovider.ErrNotSupported 145 } 146 147 func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) { 148 driver := opts.Driver 149 if !utils.IsInStringArray(driver, []string{"ide", "scsi", "pci", "sata"}) { 150 driver = "scsi" 151 } 152 idx, ids := -1, []int{} 153 for _, disk := range self.VMDiskInfo { 154 if disk.DiskAddress.DeviceBus == driver { 155 ids = append(ids, disk.DiskAddress.DeviceIndex) 156 } 157 } 158 sort.Ints(ids) 159 for _, id := range ids { 160 if id == idx+1 { 161 idx = id 162 } 163 } 164 params := map[string]interface{}{ 165 "vm_disks": []map[string]interface{}{ 166 { 167 "is_cdrom": false, 168 "disk_address": map[string]interface{}{ 169 "device_bus": driver, 170 "device_index": idx + 1, 171 }, 172 "vm_disk_create": map[string]interface{}{ 173 "storage_container_uuid": opts.StorageId, 174 "size": opts.SizeMb * 1024 * 1024, 175 }, 176 }, 177 }, 178 } 179 ret := struct { 180 TaskUUID string 181 }{} 182 res := fmt.Sprintf("vms/%s/disks/attach", self.UUID) 183 err := self.host.zone.region.post(res, jsonutils.Marshal(params), &ret) 184 if err != nil { 185 return "", err 186 } 187 return self.host.zone.region.cli.wait(ret.TaskUUID) 188 } 189 190 func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { 191 return cloudprovider.ErrNotSupported 192 } 193 194 func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error { 195 params := map[string]interface{}{ 196 "memory_mb": opts.MemoryMB, 197 "num_cores_per_vcpu": self.NumCoresPerVcpu, 198 "num_vcpus": opts.Cpu / self.NumCoresPerVcpu, 199 } 200 return self.host.zone.region.update("vms", self.UUID, jsonutils.Marshal(params), nil) 201 } 202 203 func (self *SInstance) DeleteVM(ctx context.Context) error { 204 return self.host.zone.region.DeleteVM(self.UUID) 205 } 206 207 func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error { 208 return cloudprovider.ErrNotSupported 209 } 210 211 func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { 212 return cloudprovider.ErrNotImplemented 213 } 214 215 func (self *SInstance) GetBios() cloudprovider.TBiosType { 216 if self.Boot.UefiBoot { 217 return cloudprovider.UEFI 218 } 219 return cloudprovider.BIOS 220 } 221 222 func (self *SInstance) GetBootOrder() string { 223 return "dcn" 224 } 225 226 func (self *SInstance) GetError() error { 227 return nil 228 } 229 230 func (self *SInstance) GetHostname() string { 231 return self.Name 232 } 233 234 func (self *SInstance) GetHypervisor() string { 235 return api.HYPERVISOR_NUTANIX 236 } 237 238 func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { 239 disks, err := self.host.zone.region.GetDisks("", self.GetGlobalId()) 240 if err != nil { 241 return nil, errors.Wrapf(err, "GetInstanceDisks") 242 } 243 cdroms := []string{} 244 for _, disk := range self.VMDiskInfo { 245 if disk.IsCdrom && len(disk.DiskAddress.VmdiskUUID) > 0 { 246 cdroms = append(cdroms, disk.DiskAddress.VmdiskUUID) 247 } 248 } 249 isSys := true 250 ret := []cloudprovider.ICloudDisk{} 251 for i := range disks { 252 if utils.IsInStringArray(disks[i].UUID, cdroms) { // skip cdrom disk 253 continue 254 } 255 storage, err := self.host.zone.GetIStorageById(disks[i].StorageContainerUUID) 256 if err != nil { 257 log.Errorf("can not found disk %s storage %s", disks[i].DiskAddress, disks[i].StorageContainerUUID) 258 continue 259 } 260 disks[i].isSys = isSys 261 disks[i].storage = storage.(*SStorage) 262 isSys = false 263 ret = append(ret, &disks[i]) 264 } 265 return ret, nil 266 } 267 268 func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { 269 return nil, cloudprovider.ErrNotSupported 270 } 271 272 func (self *SInstance) GetIHost() cloudprovider.ICloudHost { 273 return self.host 274 } 275 276 func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { 277 nics, err := self.host.zone.region.GetInstanceNics(self.GetGlobalId()) 278 if err != nil { 279 return nil, errors.Wrapf(err, "GetInstanceNics") 280 } 281 ret := []cloudprovider.ICloudNic{} 282 for i := range nics { 283 nics[i].ins = self 284 ret = append(ret, &nics[i]) 285 } 286 return ret, nil 287 } 288 289 func (self *SInstance) GetInstanceType() string { 290 return fmt.Sprintf("ecs.g1.c%dm%d", self.GetVcpuCount(), self.GetVmemSizeMB()/1024) 291 } 292 293 func (self *SInstance) GetMachine() string { 294 return self.MachineType 295 } 296 297 // "UNKNOWN", "OFF", "POWERING_ON", "ON", "SHUTTING_DOWN", "POWERING_OFF", "PAUSING", "PAUSED", "SUSPENDING", "SUSPENDED", "RESUMING", "RESETTING", "MIGRATING" 298 func (self *SInstance) GetStatus() string { 299 switch strings.ToUpper(self.PowerState) { 300 case "OFF": 301 return api.VM_READY 302 case "POWERING_ON": 303 return api.VM_START_START 304 case "ON": 305 return api.VM_RUNNING 306 case "SHUTTING_DOWN": 307 return api.VM_START_STOP 308 case "POWERING_OFF": 309 return api.VM_START_STOP 310 case "PAUSING", "PAUSED": 311 return api.VM_READY 312 case "SUSPENDING", "SUSPENDED": 313 return api.VM_SUSPEND 314 case "RESUMING": 315 return api.VM_RESUMING 316 case "RESETTING": 317 return api.VM_RUNNING 318 case "MIGRATING": 319 return api.VM_MIGRATING 320 } 321 return api.VM_UNKNOWN 322 } 323 324 func (self *SInstance) GetFullOsName() string { 325 return "" 326 } 327 328 func (self *SInstance) GetOsType() cloudprovider.TOsType { 329 if strings.Contains(strings.ToLower(self.Name), "win") { 330 return cloudprovider.OsTypeWindows 331 } 332 return cloudprovider.OsTypeLinux 333 } 334 335 func (ins *SInstance) GetOsDist() string { 336 return "" 337 } 338 339 func (ins *SInstance) GetOsVersion() string { 340 return "" 341 } 342 343 func (ins *SInstance) GetOsLang() string { 344 return "" 345 } 346 347 func (ins *SInstance) GetOsArch() string { 348 return apis.OS_ARCH_X86_64 349 } 350 351 func (self *SInstance) GetProjectId() string { 352 return "" 353 } 354 355 func (self *SInstance) GetSecurityGroupIds() ([]string, error) { 356 return []string{}, nil 357 } 358 359 func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { 360 return nil, cloudprovider.ErrNotImplemented 361 } 362 363 func (self *SInstance) GetVcpuCount() int { 364 return self.NumCoresPerVcpu * self.NumVcpus 365 } 366 367 func (self *SInstance) GetVmemSizeMB() int { 368 return self.MemoryMb 369 } 370 371 func (self *SInstance) GetVga() string { 372 return "std" 373 } 374 375 func (self *SInstance) GetVdi() string { 376 return "vnc" 377 } 378 379 func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { 380 return "", cloudprovider.ErrNotSupported 381 } 382 383 func (self *SInstance) SetSecurityGroups(secgroupIds []string) error { 384 return cloudprovider.ErrNotSupported 385 } 386 387 func (self *SInstance) StartVM(ctx context.Context) error { 388 return self.host.zone.region.SetInstancePowerState(self.UUID, "on") 389 } 390 391 func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { 392 act := "acpi_shutdown" 393 if opts.IsForce { 394 act = "off" 395 } 396 return self.host.zone.region.SetInstancePowerState(self.UUID, act) 397 } 398 399 func (self *SInstance) UpdateUserData(userData string) error { 400 return cloudprovider.ErrNotImplemented 401 } 402 403 func (self *SInstance) UpdateVM(ctx context.Context, name string) error { 404 return cloudprovider.ErrNotSupported 405 } 406 407 func (self *SRegion) SetInstancePowerState(id string, state string) error { 408 res := fmt.Sprintf("vms/%s/set_power_state", id) 409 ret := struct { 410 TaskUUID string 411 }{} 412 err := self.post(res, jsonutils.Marshal(map[string]string{"transition": state}), &ret) 413 if err != nil { 414 return err 415 } 416 _, err = self.cli.wait(ret.TaskUUID) 417 return err 418 } 419 420 func (self *SRegion) DeleteVM(id string) error { 421 return self.delete("vms", id) 422 }