instance.go 11 KB

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