esxi.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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 hostdrivers
  15. import (
  16. "context"
  17. "fmt"
  18. "yunion.io/x/cloudmux/pkg/cloudprovider"
  19. "yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/httputils"
  24. api "yunion.io/x/onecloud/pkg/apis/compute"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  26. "yunion.io/x/onecloud/pkg/compute/models"
  27. "yunion.io/x/onecloud/pkg/mcclient"
  28. )
  29. type SESXiHostDriver struct {
  30. SManagedVirtualizationHostDriver
  31. }
  32. func init() {
  33. driver := SESXiHostDriver{}
  34. models.RegisterHostDriver(&driver)
  35. }
  36. func (self *SESXiHostDriver) GetHostType() string {
  37. return api.HOST_TYPE_ESXI
  38. }
  39. func (self *SESXiHostDriver) GetHypervisor() string {
  40. return api.HYPERVISOR_ESXI
  41. }
  42. func (self *SESXiHostDriver) GetProvider() string {
  43. return api.CLOUD_PROVIDER_ONECLOUD
  44. }
  45. func (self *SESXiHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
  46. return nil
  47. }
  48. func (self *SESXiHostDriver) RequestRemoteUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, storage *models.SStorage, disk *models.SDisk, replaceTags bool) error {
  49. return nil
  50. }
  51. func (self *SESXiHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
  52. params := task.GetParams()
  53. imageId, err := params.GetString("image_id")
  54. if err != nil {
  55. return err
  56. }
  57. isForce := jsonutils.QueryBoolean(params, "is_force", false)
  58. obj, err := models.CachedimageManager.FetchById(imageId)
  59. if err != nil {
  60. return err
  61. }
  62. cacheImage := obj.(*models.SCachedimage)
  63. var srcHostCacheImage *models.SStoragecachedimage
  64. // Check if storageCache has this cacheImage.
  65. // If no, choose source storage cache
  66. // else, use it
  67. hostCacheImage := models.StoragecachedimageManager.GetStoragecachedimage(storageCache.GetId(), cacheImage.GetId())
  68. if hostCacheImage == nil {
  69. zone, _ := host.GetZone()
  70. srcHostCacheImage, err = cacheImage.ChooseSourceStoragecacheInRange(api.HOST_TYPE_ESXI, []string{host.Id},
  71. []interface{}{zone, host.GetCloudprovider()})
  72. if err != nil {
  73. return err
  74. }
  75. }
  76. type contentStruct struct {
  77. ImageId string
  78. HostId string
  79. HostIp string
  80. SrcHostIp string
  81. SrcPath string
  82. SrcDatastore vcenter.SVCenterAccessInfo
  83. Datastore vcenter.SVCenterAccessInfo
  84. Format string
  85. IsForce bool
  86. StoragecacheId string
  87. ImageType string
  88. ImageExternalId string
  89. StorageCacheHostIp string
  90. }
  91. content := contentStruct{}
  92. content.ImageId = imageId
  93. content.HostId = host.Id
  94. content.HostIp = host.AccessIp
  95. // format force VMDK
  96. format := cacheImage.GetFormat()
  97. if format == "qcow2" {
  98. format = "vmdk"
  99. }
  100. content.Format = format
  101. content.ImageType = cacheImage.ImageType
  102. content.ImageExternalId = cacheImage.ExternalId
  103. storage := host.GetStorageByFilePath(storageCache.Path)
  104. if storage == nil {
  105. msg := fmt.Sprintf("fail to find storage for storageCache %s", storageCache.Path)
  106. log.Errorf("%s", msg)
  107. return errors.Error(msg)
  108. }
  109. accessInfo, err := host.GetCloudaccount().GetVCenterAccessInfo(storage.ExternalId)
  110. if err != nil {
  111. return err
  112. }
  113. content.Datastore = accessInfo
  114. if cloudprovider.TImageType(cacheImage.ImageType) == cloudprovider.ImageTypeSystem {
  115. var host *models.SHost
  116. if srcHostCacheImage != nil {
  117. host, err = srcHostCacheImage.GetHost()
  118. if err != nil {
  119. return errors.Wrap(err, "srcHostCacheImage.GetHost")
  120. }
  121. } else {
  122. host, err = storageCache.GetMasterHost()
  123. if err != nil {
  124. return errors.Wrap(err, "StorageCache.GetHost")
  125. }
  126. }
  127. content.StorageCacheHostIp = host.AccessIp
  128. } else if srcHostCacheImage != nil {
  129. err = srcHostCacheImage.AddDownloadRefcount()
  130. if err != nil {
  131. return err
  132. }
  133. srcHost, err := srcHostCacheImage.GetHost()
  134. if err != nil {
  135. return err
  136. }
  137. content.SrcHostIp = srcHost.AccessIp
  138. content.SrcPath = srcHostCacheImage.Path
  139. srcStorageCache := srcHostCacheImage.GetStoragecache()
  140. if srcStorageCache == nil {
  141. return errors.Wrap(errors.ErrNotFound, "StorageCacheImage.GetStoragecaceh")
  142. }
  143. srcStorage := srcHost.GetStorageByFilePath(srcStorageCache.Path)
  144. accessInfo, err := srcHost.GetCloudaccount().GetVCenterAccessInfo(srcStorage.ExternalId)
  145. if err != nil {
  146. return err
  147. }
  148. content.SrcDatastore = accessInfo
  149. }
  150. if !host.IsEsxiAgentReady() {
  151. return fmt.Errorf("fail to find valid ESXi agent")
  152. }
  153. url := "/disks/image_cache"
  154. if isForce {
  155. content.IsForce = true
  156. }
  157. content.StoragecacheId = storageCache.Id
  158. body := jsonutils.NewDict()
  159. body.Add(jsonutils.Marshal(&content), "disk")
  160. header := task.GetTaskRequestHeader()
  161. _, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
  162. if err != nil {
  163. return err
  164. }
  165. return nil
  166. }
  167. func (self *SESXiHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, input api.DiskAllocateInput) error {
  168. if !host.IsEsxiAgentReady() {
  169. return fmt.Errorf("fail to find valid ESXi agent")
  170. }
  171. type specStruct struct {
  172. Datastore vcenter.SVCenterAccessInfo
  173. }
  174. input.HostIp = host.AccessIp
  175. input.Format = "vmdk"
  176. var err error
  177. input.Datastore, err = host.GetCloudaccount().GetVCenterAccessInfo(storage.ExternalId)
  178. if err != nil {
  179. return err
  180. }
  181. body := jsonutils.NewDict()
  182. body.Add(jsonutils.Marshal(&input), "disk")
  183. url := fmt.Sprintf("/disks/agent/create/%s", disk.Id)
  184. header := task.GetTaskRequestHeader()
  185. _, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
  186. return err
  187. }
  188. func (self *SESXiHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error {
  189. if !host.IsEsxiAgentReady() {
  190. return fmt.Errorf("fail to find valid ESXi agent")
  191. }
  192. guests := disk.GetGuests()
  193. if len(guests) == 0 {
  194. return fmt.Errorf("No VM associate with this disk")
  195. }
  196. if len(guests) > 1 {
  197. return fmt.Errorf("The disk is attached to multiple guests")
  198. }
  199. guest := guests[0]
  200. if guest.HostId != host.Id {
  201. return fmt.Errorf("The only guest is not on the host????")
  202. }
  203. type specStruct struct {
  204. Vm vcenter.SVCenterAccessInfo
  205. Disk vcenter.SVCenterAccessInfo
  206. HostIp string
  207. ImageId string
  208. }
  209. spec := specStruct{}
  210. spec.HostIp = host.AccessIp
  211. spec.ImageId = imageId
  212. account := host.GetCloudaccount()
  213. accessInfo, err := account.GetVCenterAccessInfo(guest.ExternalId)
  214. if err != nil {
  215. return err
  216. }
  217. spec.Vm = accessInfo
  218. accessInfo, err = account.GetVCenterAccessInfo(disk.ExternalId)
  219. if err != nil {
  220. return err
  221. }
  222. spec.Disk = accessInfo
  223. body := jsonutils.NewDict()
  224. body.Add(jsonutils.Marshal(&spec), "disk")
  225. url := fmt.Sprintf("/disks/agent/save-prepare/%s", disk.Id)
  226. header := task.GetTaskRequestHeader()
  227. _, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
  228. return err
  229. }
  230. func (self *SESXiHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
  231. guest := disk.GetGuest()
  232. if guest == nil {
  233. return fmt.Errorf("unable to find guest has disk %s", disk.GetId())
  234. }
  235. iVm, err := guest.GetIVM(ctx)
  236. if err != nil {
  237. return errors.Wrapf(err, "GetIVM")
  238. }
  239. if iVm.GetStatus() == api.VM_RUNNING {
  240. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  241. disks, err := iVm.GetIDisks()
  242. if err != nil {
  243. return nil, errors.Wrapf(err, "GetIDisk")
  244. }
  245. for i := range disks {
  246. if disks[i].GetGlobalId() == disk.ExternalId {
  247. err = disks[i].Resize(ctx, sizeMb)
  248. if err != nil {
  249. return nil, errors.Wrapf(err, "Resize")
  250. }
  251. return jsonutils.Marshal(map[string]int64{"disk_size": sizeMb}), nil
  252. }
  253. }
  254. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "disk %s", disk.Name)
  255. })
  256. return nil
  257. }
  258. spec := struct {
  259. HostInfo vcenter.SVCenterAccessInfo
  260. VMId string
  261. DiskId string
  262. SizeMb int64
  263. }{}
  264. account := host.GetCloudaccount()
  265. accessInfo, err := account.GetVCenterAccessInfo(host.ExternalId)
  266. if err != nil {
  267. return err
  268. }
  269. spec.HostInfo = accessInfo
  270. spec.DiskId = disk.GetExternalId()
  271. spec.VMId = guest.GetExternalId()
  272. spec.SizeMb = sizeMb
  273. body := jsonutils.NewDict()
  274. body.Add(jsonutils.Marshal(spec), "disk")
  275. url := fmt.Sprintf("/disks/agent/resize/%s", disk.Id)
  276. header := task.GetTaskRequestHeader()
  277. _, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
  278. return err
  279. }
  280. func (self *SESXiHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error {
  281. imagePath, _ := data.GetString("backup")
  282. if len(imagePath) == 0 {
  283. return fmt.Errorf("missing parameter backup")
  284. }
  285. // agentId, _ := data.GetString("agent_id")
  286. // if len(agentId) == 0 {
  287. // return fmt.Errorf("missing parameter agent_id")
  288. // }
  289. // agent := models.HostManager.FetchHostById(agentId)
  290. // if agent == nil {
  291. // return fmt.Errorf("cannot find host with id %s", agentId)
  292. // }
  293. storage, _ := disk.GetStorage()
  294. type specStruct struct {
  295. ImagePath string
  296. ImageId string
  297. StorageId string
  298. StoragecacheId string
  299. Compress bool `json:",allowfalse"`
  300. }
  301. spec := specStruct{}
  302. spec.ImageId = imageId
  303. spec.ImagePath = imagePath
  304. spec.StorageId = storage.Id
  305. spec.StoragecacheId = storage.StoragecacheId
  306. spec.Compress = false
  307. body := jsonutils.NewDict()
  308. body.Add(jsonutils.Marshal(&spec), "disk")
  309. url := "/disks/agent/upload"
  310. header := task.GetTaskRequestHeader()
  311. _, err := host.EsxiRequest(ctx, httputils.POST, url, header, body)
  312. return err
  313. }