kvm.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  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. "net/http"
  19. "net/url"
  20. "strings"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/util/httputils"
  27. "yunion.io/x/pkg/utils"
  28. api "yunion.io/x/onecloud/pkg/apis/compute"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  32. "yunion.io/x/onecloud/pkg/compute/baremetal"
  33. "yunion.io/x/onecloud/pkg/compute/models"
  34. "yunion.io/x/onecloud/pkg/compute/options"
  35. "yunion.io/x/onecloud/pkg/httperrors"
  36. "yunion.io/x/onecloud/pkg/mcclient"
  37. "yunion.io/x/onecloud/pkg/util/k8s/tokens"
  38. )
  39. type SKVMHostDriver struct {
  40. SVirtualizationHostDriver
  41. }
  42. func init() {
  43. driver := SKVMHostDriver{}
  44. models.RegisterHostDriver(&driver)
  45. }
  46. func (self *SKVMHostDriver) GetHostType() string {
  47. return api.HOST_TYPE_HYPERVISOR
  48. }
  49. func (self *SKVMHostDriver) GetHypervisor() string {
  50. return api.HYPERVISOR_KVM
  51. }
  52. func (self *SKVMHostDriver) GetProvider() string {
  53. return api.CLOUD_PROVIDER_ONECLOUD
  54. }
  55. func (self *SKVMHostDriver) validateGPFS(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, input api.HostStorageCreateInput) (api.HostStorageCreateInput, error) {
  56. header := http.Header{}
  57. header.Set(mcclient.AUTH_TOKEN, userCred.GetTokenString())
  58. header.Set(mcclient.REGION_VERSION, "v2")
  59. params := jsonutils.NewDict()
  60. params.Set("mount_point", jsonutils.NewString(input.MountPoint))
  61. urlStr := fmt.Sprintf("%s/storages/is-mount-point?%s", host.ManagerUri, params.QueryString())
  62. _, res, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "GET", urlStr, header, nil, false)
  63. if err != nil {
  64. return input, err
  65. }
  66. if !jsonutils.QueryBoolean(res, "is_mount_point", false) {
  67. return input, httperrors.NewBadRequestError("%s is not mount point %s", input.MountPoint, res)
  68. }
  69. urlStr = fmt.Sprintf("%s/storages/is-local-mount-point?%s", host.ManagerUri, params.QueryString())
  70. _, res, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "GET", urlStr, header, nil, false)
  71. if err != nil {
  72. return input, err
  73. }
  74. if jsonutils.QueryBoolean(res, "is_local_mount_point", false) {
  75. return input, httperrors.NewBadRequestError("%s is local storage mount point", input.MountPoint)
  76. }
  77. return input, nil
  78. }
  79. func (self *SKVMHostDriver) validateSharedLVM(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storage *models.SStorage, input api.HostStorageCreateInput) (api.HostStorageCreateInput, error) {
  80. header := http.Header{}
  81. header.Set(mcclient.AUTH_TOKEN, userCred.GetTokenString())
  82. header.Set(mcclient.REGION_VERSION, "v2")
  83. params := jsonutils.NewDict()
  84. params.Set("vg_name", jsonutils.NewString(input.MountPoint))
  85. urlStr := fmt.Sprintf("%s/storages/is-vg-exist?%s", host.ManagerUri, params.QueryString())
  86. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "GET", urlStr, header, nil, false)
  87. if err != nil {
  88. return input, err
  89. }
  90. return input, nil
  91. }
  92. func (self *SKVMHostDriver) ValidateAttachStorage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storage *models.SStorage, input api.HostStorageCreateInput) (api.HostStorageCreateInput, error) {
  93. if !utils.IsInStringArray(storage.StorageType, append([]string{api.STORAGE_LOCAL, api.STORAGE_NVME_PT, api.STORAGE_NVME, api.STORAGE_LVM}, api.SHARED_STORAGE...)) {
  94. return input, httperrors.NewUnsupportOperationError("Unsupport attach %s storage for %s host", storage.StorageType, host.HostType)
  95. }
  96. if storage.StorageType == api.STORAGE_RBD {
  97. if host.HostStatus != api.HOST_ONLINE {
  98. return input, httperrors.NewInvalidStatusError("Attach rbd storage require host status is online")
  99. }
  100. pool, _ := storage.StorageConf.GetString("pool")
  101. input.MountPoint = fmt.Sprintf("rbd:%s", pool)
  102. } else if utils.IsInStringArray(storage.StorageType, api.SHARED_FILE_STORAGE) {
  103. if len(input.MountPoint) == 0 {
  104. return input, httperrors.NewMissingParameterError("mount_point")
  105. }
  106. count, err := models.HoststorageManager.Query().Equals("host_id", host.Id).Equals("mount_point", input.MountPoint).CountWithError()
  107. if err != nil {
  108. return input, httperrors.NewInternalServerError("Query host storage error %s", err)
  109. }
  110. if count > 0 {
  111. return input, httperrors.NewBadRequestError("Host %s already have mount point %s with other storage", host.Name, input.MountPoint)
  112. }
  113. if host.HostStatus != api.HOST_ONLINE {
  114. return input, httperrors.NewInvalidStatusError("Attach nfs storage require host status is online")
  115. }
  116. if storage.StorageType == api.STORAGE_GPFS {
  117. return self.validateGPFS(ctx, userCred, host, input)
  118. }
  119. } else if storage.StorageType == api.STORAGE_CLVM {
  120. vgName, _ := storage.StorageConf.GetString("clvm_vg_name")
  121. if vgName == "" {
  122. return input, httperrors.NewInternalServerError("storage has no clvm_vg_name")
  123. }
  124. input.MountPoint = vgName
  125. return self.validateSharedLVM(ctx, userCred, host, storage, input)
  126. } else if storage.StorageType == api.STORAGE_SLVM {
  127. vgName, _ := storage.StorageConf.GetString("slvm_vg_name")
  128. if vgName == "" {
  129. return input, httperrors.NewInternalServerError("storage has no slvm_vg_name")
  130. }
  131. input.MountPoint = vgName
  132. return self.validateSharedLVM(ctx, userCred, host, storage, input)
  133. }
  134. return input, nil
  135. }
  136. func (self *SKVMHostDriver) RequestAttachStorage(ctx context.Context, hoststorage *models.SHoststorage, host *models.SHost, storage *models.SStorage, task taskman.ITask) error {
  137. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  138. if utils.IsInStringArray(storage.StorageType, api.SHARED_STORAGE) {
  139. log.Infof("Attach SharedStorage[%s] on host %s ...", storage.Name, host.Name)
  140. url := fmt.Sprintf("%s/storages/attach", host.ManagerUri)
  141. headers := mcclient.GetTokenHeaders(task.GetUserCred())
  142. data := map[string]interface{}{
  143. "mount_point": hoststorage.MountPoint,
  144. "name": storage.Name,
  145. "storage_id": storage.Id,
  146. "storage_conf": storage.StorageConf,
  147. "storage_type": storage.StorageType,
  148. }
  149. if len(storage.StoragecacheId) > 0 {
  150. storagecache := models.StoragecacheManager.FetchStoragecacheById(storage.StoragecacheId)
  151. if storagecache != nil {
  152. data["imagecache_path"] = storage.GetStorageCachePath(hoststorage.MountPoint, storagecache.Path)
  153. data["storagecache_id"] = storagecache.Id
  154. }
  155. }
  156. _, resp, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, headers, jsonutils.Marshal(data), false)
  157. return resp, err
  158. }
  159. return nil, nil
  160. })
  161. return nil
  162. }
  163. func (self *SKVMHostDriver) RequestDetachStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, task taskman.ITask) error {
  164. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  165. if utils.IsInStringArray(storage.StorageType, api.SHARED_STORAGE) && host.HostStatus == api.HOST_ONLINE {
  166. log.Infof("Detach SharedStorage[%s] on host %s ...", storage.Name, host.Name)
  167. url := fmt.Sprintf("%s/storages/detach", host.ManagerUri)
  168. headers := mcclient.GetTokenHeaders(task.GetUserCred())
  169. body := jsonutils.NewDict()
  170. mountPoint, _ := task.GetParams().GetString("mount_point")
  171. body.Set("mount_point", jsonutils.NewString(mountPoint))
  172. body.Set("name", jsonutils.NewString(storage.Name))
  173. body.Set("storage_id", jsonutils.NewString(storage.Id))
  174. _, resp, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, headers, body, false)
  175. return resp, err
  176. }
  177. return nil, nil
  178. })
  179. return nil
  180. }
  181. func (self *SKVMHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
  182. return nil
  183. }
  184. func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
  185. input := api.CacheImageInput{}
  186. task.GetParams().Unmarshal(&input)
  187. var srcHost *models.SHost
  188. if len(input.SourceHostId) > 0 {
  189. srcHost = models.HostManager.FetchHostById(input.SourceHostId)
  190. if srcHost == nil {
  191. return errors.Errorf("Source host %s not found", input.SourceHostId)
  192. }
  193. }
  194. obj, err := models.CachedimageManager.FetchById(input.ImageId)
  195. if err != nil {
  196. return errors.Wrapf(err, "Fetch cached image by image_id %s", input.ImageId)
  197. }
  198. cacheImage := obj.(*models.SCachedimage)
  199. zone, _ := host.GetZone()
  200. rangeObjs := []interface{}{zone}
  201. if srcHost != nil {
  202. rangeObjs = append(rangeObjs, srcHost)
  203. }
  204. srcHostCacheImage, err := cacheImage.ChooseSourceStoragecacheInRange(api.HOST_TYPE_HYPERVISOR, []string{host.Id}, rangeObjs)
  205. if err != nil {
  206. return errors.Wrapf(err, "Choose source storagecache")
  207. }
  208. if srcHostCacheImage != nil {
  209. err = srcHostCacheImage.AddDownloadRefcount()
  210. if err != nil {
  211. return err
  212. }
  213. /*srcHost, err := srcHostCacheImage.GetHost()
  214. if err != nil {
  215. return errors.Wrapf(err, "Get storage cached image %s host", srcHostCacheImage.GetId())
  216. }
  217. input.SrcUrl = fmt.Sprintf("%s/download/images/%s", srcHost.ManagerUri, input.ImageId)*/
  218. }
  219. url := fmt.Sprintf("%s/disks/image_cache", host.ManagerUri)
  220. input.StoragecacheId = storageCache.Id
  221. body := jsonutils.NewDict()
  222. body.Add(jsonutils.Marshal(&input), "disk")
  223. log.Infof("cache image %s(%s) on host %s(%s)", input.ImageName, input.ImageId, host.Name, host.AccessIp)
  224. header := task.GetTaskRequestHeader()
  225. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  226. if err != nil {
  227. return errors.Wrapf(err, "POST %s", url)
  228. }
  229. return nil
  230. }
  231. func (self *SKVMHostDriver) RequestUncacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask, deactivateImage bool) error {
  232. input := api.UncacheImageInput{}
  233. task.GetParams().Unmarshal(&input)
  234. input.StoragecacheId = storageCache.Id
  235. input.DeactivateImage = &deactivateImage
  236. url := fmt.Sprintf("%s/disks/image_cache", host.ManagerUri)
  237. body := jsonutils.NewDict()
  238. body.Add(jsonutils.Marshal(&input), "disk")
  239. if deactivateImage {
  240. body.Add(jsonutils.JSONTrue, "deactivate_image")
  241. }
  242. header := task.GetTaskRequestHeader()
  243. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "DELETE", url, header, body, false)
  244. if err != nil {
  245. return errors.Wrap(err, "JSONRequest")
  246. }
  247. return nil
  248. }
  249. func (self *SKVMHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, input api.DiskAllocateInput) error {
  250. header := task.GetTaskRequestHeader()
  251. if len(input.SnapshotId) > 0 {
  252. snapObj, err := models.SnapshotManager.FetchById(input.SnapshotId)
  253. if err != nil {
  254. return errors.Wrapf(err, "SnapshotManager.FetchById(%s)", input.SnapshotId)
  255. }
  256. snapshot := snapObj.(*models.SSnapshot)
  257. snapshotStorage := models.StorageManager.FetchStorageById(snapshot.StorageId)
  258. if snapshotStorage.StorageType == api.STORAGE_LOCAL || snapshotStorage.StorageType == api.STORAGE_LVM {
  259. snapshotHost, err := snapshotStorage.GetMasterHost()
  260. if err != nil {
  261. return errors.Wrapf(err, "GetMasterHost")
  262. }
  263. if options.Options.SnapshotCreateDiskProtocol == "url" {
  264. input.SnapshotUrl = fmt.Sprintf("%s/download/snapshots/%s/%s/%s", snapshotHost.ManagerUri, snapshotStorage.Id, snapshot.DiskId, snapshot.Id)
  265. input.SnapshotOutOfChain = snapshot.OutOfChain
  266. } else if options.Options.SnapshotCreateDiskProtocol == "fuse" {
  267. input.SnapshotUrl = fmt.Sprintf("%s/snapshots/%s/%s", snapshotHost.GetFetchUrl(true), snapshot.DiskId, snapshot.Id)
  268. }
  269. input.Protocol = options.Options.SnapshotCreateDiskProtocol
  270. } else if snapshotStorage.StorageType == api.STORAGE_RBD {
  271. input.SnapshotUrl = snapshot.Id
  272. input.SrcDiskId = snapshot.DiskId
  273. input.SrcPool, _ = snapshotStorage.StorageConf.GetString("pool")
  274. } else {
  275. input.SnapshotUrl = snapshot.Location
  276. }
  277. }
  278. url := fmt.Sprintf("/disks/%s/create/%s", storage.Id, disk.Id)
  279. body := jsonutils.NewDict()
  280. body.Add(jsonutils.Marshal(input), "disk")
  281. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  282. return err
  283. }
  284. func (self *SKVMHostDriver) RequestRebuildDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, input api.DiskAllocateInput) error {
  285. input.Rebuild = true
  286. input.BackingDiskId, _ = task.GetParams().GetString("backing_disk_id")
  287. return self.RequestAllocateDiskOnStorage(ctx, task.GetUserCred(), host, storage, disk, task, input)
  288. }
  289. func (self *SKVMHostDriver) RequestDeallocateDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, cleanSnapshots bool, task taskman.ITask) error {
  290. log.Infof("Deallocating disk on host %s", host.GetName())
  291. header := task.GetTaskRequestHeader()
  292. url := fmt.Sprintf("/disks/%s/delete/%s", storage.Id, disk.Id)
  293. body := jsonutils.NewDict()
  294. if flatPath := disk.GetMetadata(ctx, api.DISK_META_REMOTE_ACCESS_PATH, nil); flatPath != "" {
  295. body.Set("esxi_flat_file_path", jsonutils.NewString(flatPath))
  296. }
  297. body.Set("clean_snapshots", jsonutils.NewBool(cleanSnapshots))
  298. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  299. if err != nil {
  300. if errors.Cause(err) == cloudprovider.ErrNotFound {
  301. return task.ScheduleRun(nil)
  302. }
  303. return err
  304. }
  305. return nil
  306. }
  307. func (driver *SKVMHostDriver) RequestDeallocateBackupDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error {
  308. log.Infof("Deallocating disk on host %s", host.GetName())
  309. header := mcclient.GetTokenHeaders(task.GetUserCred())
  310. url := fmt.Sprintf("/disks/%s/delete/%s", storage.Id, disk.Id)
  311. body := jsonutils.NewDict()
  312. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  313. return err
  314. }
  315. func (self *SKVMHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
  316. header := task.GetTaskRequestHeader()
  317. url := fmt.Sprintf("/disks/%s/resize/%s", storage.Id, disk.Id)
  318. body := jsonutils.NewDict()
  319. content := jsonutils.NewDict()
  320. content.Add(jsonutils.NewInt(sizeMb), "size")
  321. if disk.IsEncrypted() {
  322. info, err := disk.GetEncryptInfo(ctx, task.GetUserCred())
  323. if err != nil {
  324. return errors.Wrap(err, "disk.GetEncryptInfo")
  325. }
  326. content.Add(jsonutils.Marshal(info), "encrypt_info")
  327. }
  328. guest := disk.GetGuest()
  329. if guest != nil {
  330. content.Add(jsonutils.NewString(guest.Id), "server_id")
  331. }
  332. body.Add(content, "disk")
  333. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  334. return err
  335. }
  336. func (self *SKVMHostDriver) RequestDiskSrcMigratePrepare(ctx context.Context, host *models.SHost, disk *models.SDisk, task taskman.ITask) (jsonutils.JSONObject, error) {
  337. body := jsonutils.NewDict()
  338. destUrl := fmt.Sprintf("/disks/%s/src-migrate-prepare/%s", disk.StorageId, disk.Id)
  339. header := task.GetTaskRequestHeader()
  340. return host.Request(ctx, task.GetUserCred(), "POST", destUrl, header, body)
  341. }
  342. func (self *SKVMHostDriver) RequestDiskMigrate(ctx context.Context, targetHost *models.SHost, targetStorage *models.SStorage, disk *models.SDisk, task taskman.ITask, body *jsonutils.JSONDict) error {
  343. destUrl := fmt.Sprintf("/disks/%s/migrate/%s", targetStorage.Id, disk.Id)
  344. header := task.GetTaskRequestHeader()
  345. _, err := targetHost.Request(ctx, task.GetUserCred(), "POST", destUrl, header, body)
  346. return err
  347. }
  348. func (self *SKVMHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error {
  349. body := jsonutils.NewDict()
  350. body.Add(jsonutils.Marshal(map[string]string{"image_id": imageId}), "disk")
  351. url := fmt.Sprintf("/disks/%s/save-prepare/%s", disk.StorageId, disk.Id)
  352. header := task.GetTaskRequestHeader()
  353. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  354. return err
  355. }
  356. func (self *SKVMHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error {
  357. body := jsonutils.NewDict()
  358. backup, _ := data.GetString("backup")
  359. storage, _ := disk.GetStorage()
  360. content := map[string]string{
  361. "image_path": backup,
  362. "image_id": imageId,
  363. "storagecached_id": storage.StoragecacheId,
  364. }
  365. if disk.IsEncrypted() {
  366. content["encrypt_key_id"] = disk.EncryptKeyId
  367. }
  368. if data.Contains("format") {
  369. content["format"], _ = data.GetString("format")
  370. }
  371. body.Add(jsonutils.Marshal(content), "disk")
  372. url := fmt.Sprintf("/disks/%s/upload", disk.StorageId)
  373. header := task.GetTaskRequestHeader()
  374. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  375. return err
  376. }
  377. func (self *SKVMHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask, snapshotIds []string) error {
  378. url := fmt.Sprintf("/storages/%s/delete-snapshots", snapshot.StorageId)
  379. body := jsonutils.NewDict()
  380. body.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
  381. body.Set("snapshot_ids", jsonutils.NewStringArray(snapshotIds))
  382. header := task.GetTaskRequestHeader()
  383. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  384. return err
  385. }
  386. func (self *SKVMHostDriver) RequestDeleteSnapshotWithoutGuest(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, params *jsonutils.JSONDict, task taskman.ITask) error {
  387. url := fmt.Sprintf("/storages/%s/delete-snapshot", snapshot.StorageId)
  388. header := task.GetTaskRequestHeader()
  389. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, params)
  390. return err
  391. }
  392. func (self *SKVMHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, input *api.DiskResetInput) (*api.DiskResetInput, error) {
  393. if len(guests) > 1 {
  394. return nil, httperrors.NewBadRequestError("Disk attach muti guests")
  395. } else if len(guests) == 1 {
  396. if guests[0].Status != api.VM_READY {
  397. return nil, httperrors.NewServerStatusError("Disk attached guest status must be ready")
  398. }
  399. } else {
  400. return nil, httperrors.NewBadRequestError("Disk dosen't attach guest")
  401. }
  402. return input, nil
  403. }
  404. func (self *SKVMHostDriver) RequestResetDisk(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error {
  405. url := fmt.Sprintf("/disks/%s/reset/%s", disk.StorageId, disk.Id)
  406. header := task.GetTaskRequestHeader()
  407. if disk.IsEncrypted() {
  408. info, err := disk.GetEncryptInfo(ctx, task.GetUserCred())
  409. if err != nil {
  410. return errors.Wrap(err, "disk.GetEncryptInfo")
  411. }
  412. params.Add(jsonutils.Marshal(info), "encrypt_info")
  413. }
  414. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, params)
  415. return err
  416. }
  417. func (self *SKVMHostDriver) RequestCleanUpDiskSnapshots(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error {
  418. url := fmt.Sprintf("/disks/%s/cleanup-snapshots/%s", disk.StorageId, disk.Id)
  419. header := task.GetTaskRequestHeader()
  420. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, params)
  421. return err
  422. }
  423. func (self *SKVMHostDriver) PrepareConvert(host *models.SHost, image, raid string, data jsonutils.JSONObject) (*api.ServerCreateInput, error) {
  424. params, err := self.SBaseHostDriver.PrepareConvert(host, image, raid, data)
  425. if err != nil {
  426. return nil, err
  427. }
  428. var sysSize = "60g"
  429. raidConfs, _ := cmdline.FetchBaremetalDiskConfigsByJSON(data)
  430. if len(raidConfs) == 0 {
  431. raid, err = self.GetRaidScheme(host, raid)
  432. if err != nil {
  433. return nil, err
  434. }
  435. if raid != baremetal.DISK_CONF_NONE {
  436. raidConfs = []*api.BaremetalDiskConfig{
  437. {
  438. Conf: raid,
  439. Splits: fmt.Sprintf("%s,", sysSize),
  440. Type: api.DISK_TYPE_HYBRID,
  441. },
  442. }
  443. }
  444. }
  445. params.BaremetalDiskConfigs = raidConfs
  446. disks, _ := cmdline.FetchDiskConfigsByJSON(data)
  447. if len(disks) == 0 {
  448. if len(image) == 0 {
  449. image = options.Options.ConvertHypervisorDefaultTemplate
  450. }
  451. if len(image) == 0 {
  452. return nil, fmt.Errorf("Not default image specified for converting %s", self.GetHostType())
  453. }
  454. rootDisk := &api.DiskConfig{}
  455. if raid != baremetal.DISK_CONF_NONE {
  456. rootDisk = &api.DiskConfig{
  457. ImageId: image,
  458. SizeMb: -1,
  459. }
  460. } else if host.StorageInfo.(*jsonutils.JSONArray).Length() > 1 {
  461. rootDisk = &api.DiskConfig{
  462. ImageId: image,
  463. SizeMb: -1,
  464. }
  465. } else {
  466. rootDisk = &api.DiskConfig{
  467. ImageId: image,
  468. SizeMb: 60 * 1024, // 60g
  469. }
  470. }
  471. optDisk := &api.DiskConfig{
  472. Fs: "ext4",
  473. SizeMb: -1,
  474. Mountpoint: "/opt/cloud/workspace",
  475. }
  476. disks = append(disks, rootDisk, optDisk)
  477. }
  478. params.Disks = disks
  479. nets, _ := cmdline.FetchNetworkConfigsByJSON(data)
  480. if len(nets) == 0 {
  481. wire := host.GetMasterWire()
  482. if wire == nil {
  483. return nil, fmt.Errorf("No master wire?")
  484. }
  485. net := &api.NetworkConfig{
  486. Wire: wire.GetId(),
  487. Private: true,
  488. TryTeaming: true,
  489. }
  490. nets = append(nets, net)
  491. }
  492. params.Networks = nets
  493. deployConfigs, err := self.getDeployConfig(host)
  494. if err != nil {
  495. return nil, err
  496. }
  497. params.DeployConfigs = deployConfigs
  498. return params, nil
  499. }
  500. func (self *SKVMHostDriver) getDeployConfig(host *models.SHost) ([]*api.DeployConfig, error) {
  501. deployConf := &api.DeployConfig{
  502. Action: "create",
  503. Path: "/etc/sysconfig/yunionauth",
  504. }
  505. authLoc, err := url.Parse(options.Options.AuthURL)
  506. if err != nil {
  507. return nil, err
  508. }
  509. portStr := authLoc.Port()
  510. useSsl := ""
  511. if authLoc.Scheme == "https" {
  512. useSsl = "yes"
  513. if len(portStr) == 0 {
  514. portStr = "443"
  515. }
  516. } else {
  517. if len(portStr) == 0 {
  518. portStr = "80"
  519. }
  520. }
  521. authInfo := fmt.Sprintf("YUNION_REGION=%s\n", options.Options.Region)
  522. authInfo += fmt.Sprintf("YUNION_KEYSTONE=%s\n", options.Options.AuthURL)
  523. authInfo += fmt.Sprintf("YUNION_KEYSTONE_HOST=%s\n", authLoc.Hostname())
  524. authInfo += fmt.Sprintf("YUNION_KEYSTONE_PORT=%s\n", portStr)
  525. authInfo += fmt.Sprintf("YUNION_KEYSTONE_USE_SSL=%s\n", useSsl)
  526. authInfo += fmt.Sprintf("YUNION_HOST_NAME=%s\n", host.GetName())
  527. authInfo += fmt.Sprintf("YUNION_HOST_ADMIN=%s\n", options.Options.AdminUser)
  528. authInfo += fmt.Sprintf("YUNION_HOST_PASSWORD=%s\n", options.Options.AdminPassword)
  529. authInfo += fmt.Sprintf("YUNION_HOST_PROJECT=%s\n", options.Options.AdminProject)
  530. authInfo += "YUNION_START=yes\n"
  531. apiServer, err := tokens.GetControlPlaneEndpoint()
  532. if err != nil {
  533. log.Errorf("Failed to get kubernetes controlplane endpoint: %v", err)
  534. }
  535. joinToken, err := tokens.GetNodeJoinToken()
  536. if err != nil {
  537. log.Errorf("Failed to get kubernetes node join token: %v", err)
  538. }
  539. authInfo += fmt.Sprintf("API_SERVER=%s\n", apiServer)
  540. authInfo += fmt.Sprintf("JOIN_TOKEN=%s\n", joinToken)
  541. if apiServer != "" {
  542. dockerCfg, err := tokens.GetDockerDaemonContent()
  543. if err != nil {
  544. return nil, errors.Wrap(err, "Failed to get docker daemon config")
  545. }
  546. authInfo += fmt.Sprintf("DOCKER_DAEMON_JSON=%s\n", dockerCfg)
  547. }
  548. deployConf.Content = authInfo
  549. return []*api.DeployConfig{deployConf}, nil
  550. }
  551. func (self *SKVMHostDriver) PrepareUnconvert(host *models.SHost) error {
  552. hoststorages := host.GetHoststorages()
  553. if hoststorages == nil {
  554. return self.SBaseHostDriver.PrepareUnconvert(host)
  555. }
  556. for i := 0; i < len(hoststorages); i++ {
  557. storage := hoststorages[i].GetStorage()
  558. if storage.IsLocal() && storage.StorageType != api.STORAGE_BAREMETAL {
  559. cnt, err := storage.GetDiskCount()
  560. if err != nil {
  561. return err
  562. }
  563. if cnt > 0 {
  564. return fmt.Errorf("Local host storage is not empty??? %s", storage.GetName())
  565. }
  566. }
  567. }
  568. return self.SBaseHostDriver.PrepareUnconvert(host)
  569. }
  570. func (self *SKVMHostDriver) FinishUnconvert(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost) error {
  571. for _, hs := range host.GetHoststorages() {
  572. storage := hs.GetStorage()
  573. if storage == nil {
  574. continue
  575. }
  576. if storage.StorageType != api.STORAGE_BAREMETAL {
  577. hs.Delete(ctx, userCred)
  578. if storage.IsLocal() {
  579. storage.Delete(ctx, userCred)
  580. }
  581. }
  582. }
  583. onK8s := host.GetMetadata(ctx, "on_kubernetes", userCred)
  584. hostname := host.GetMetadata(ctx, "hostname", userCred)
  585. if strings.ToLower(onK8s) == "true" {
  586. if err := self.tryCleanKubernetesData(host, hostname); err != nil {
  587. log.Errorf("try clean kubernetes data: %v", err)
  588. }
  589. }
  590. kwargs := make(map[string]interface{}, 0)
  591. for _, k := range []string{
  592. "kernel_version", "nest", "os_distribution", "os_version",
  593. "ovs_version", "qemu_version", "storage_type", "on_kubernetes",
  594. } {
  595. kwargs[k] = "None"
  596. }
  597. host.SetAllMetadata(ctx, kwargs, userCred)
  598. return self.SBaseHostDriver.FinishUnconvert(ctx, userCred, host)
  599. }
  600. func (self *SKVMHostDriver) tryCleanKubernetesData(host *models.SHost, hostname string) error {
  601. cli, err := tokens.GetCoreClient()
  602. if err != nil {
  603. return errors.Wrap(err, "get k8s client")
  604. }
  605. if hostname == "" {
  606. hostname = host.GetName()
  607. }
  608. return cli.Nodes().Delete(context.Background(), hostname, metav1.DeleteOptions{})
  609. }
  610. func (self *SKVMHostDriver) RequestSyncOnHost(ctx context.Context, host *models.SHost, task taskman.ITask) error {
  611. log.Infof("Deallocating disk on host %s", host.GetName())
  612. header := mcclient.GetTokenHeaders(task.GetUserCred())
  613. url := fmt.Sprintf("/hosts/%s/sync", host.Id)
  614. body := jsonutils.NewDict()
  615. desc := self.GetJsonFromHost(ctx, host)
  616. body.Add(desc, "desc")
  617. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
  618. return err
  619. }
  620. func (self *SKVMHostDriver) GetJsonFromHost(ctx context.Context, host *models.SHost) *jsonutils.JSONDict {
  621. desc := jsonutils.NewDict()
  622. desc.Add(jsonutils.NewString(host.Name), "name")
  623. // tenant
  624. domainFetcher, _ := db.DefaultDomainFetcher(ctx, host.DomainId)
  625. if domainFetcher != nil {
  626. desc.Add(jsonutils.NewString(domainFetcher.GetProjectDomainId()), "domain_id")
  627. desc.Add(jsonutils.NewString(domainFetcher.GetProjectDomain()), "project_domain")
  628. }
  629. return desc
  630. }
  631. func (driver *SKVMHostDriver) RequestProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, input jsonutils.JSONObject) (*jsonutils.JSONArray, error) {
  632. url := fmt.Sprintf("%s/hosts/%s/probe-isolated-devices", host.ManagerUri, host.GetId())
  633. httpClient := httputils.GetDefaultClient()
  634. header := mcclient.GetTokenHeaders(userCred)
  635. _, respBody, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, input, false)
  636. if err != nil {
  637. return nil, errors.Wrapf(err, "send to host %s", url)
  638. }
  639. return respBody.(*jsonutils.JSONArray), err
  640. }
  641. func (driver *SKVMHostDriver) RequestUploadGuestsStatus(ctx context.Context, host *models.SHost, guests []models.SGuest, task taskman.ITask) error {
  642. input := &api.HostUploadGuestsStatusRequest{GuestIds: make([]string, len(guests))}
  643. for i := range guests {
  644. input.GuestIds[i] = guests[i].Id
  645. }
  646. header := task.GetTaskRequestHeader()
  647. url := fmt.Sprintf("/servers/upload-status")
  648. _, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, jsonutils.Marshal(input))
  649. return err
  650. }