kvm.go 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  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 guestdrivers
  15. import (
  16. "context"
  17. "encoding/base64"
  18. "fmt"
  19. "net/http"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. "yunion.io/x/jsonutils"
  25. "yunion.io/x/log"
  26. "yunion.io/x/pkg/errors"
  27. "yunion.io/x/pkg/util/httputils"
  28. "yunion.io/x/pkg/util/rbacscope"
  29. "yunion.io/x/pkg/utils"
  30. "yunion.io/x/sqlchemy"
  31. "yunion.io/x/onecloud/pkg/apis"
  32. api "yunion.io/x/onecloud/pkg/apis/compute"
  33. host_api "yunion.io/x/onecloud/pkg/apis/host"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  36. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  37. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  38. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  39. guestdriver_types "yunion.io/x/onecloud/pkg/compute/guestdrivers/types"
  40. "yunion.io/x/onecloud/pkg/compute/models"
  41. "yunion.io/x/onecloud/pkg/compute/options"
  42. "yunion.io/x/onecloud/pkg/httperrors"
  43. "yunion.io/x/onecloud/pkg/mcclient"
  44. "yunion.io/x/onecloud/pkg/util/logclient"
  45. )
  46. type SKVMGuestDriver struct {
  47. SVirtualizedGuestDriver
  48. }
  49. func init() {
  50. driver := SKVMGuestDriver{}
  51. models.RegisterGuestDriver(&driver)
  52. }
  53. func (self *SKVMGuestDriver) GetHypervisor() string {
  54. return api.HYPERVISOR_KVM
  55. }
  56. func (self *SKVMGuestDriver) GetProvider() string {
  57. return api.CLOUD_PROVIDER_ONECLOUD
  58. }
  59. func (self *SKVMGuestDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
  60. keys := models.SComputeResourceKeys{}
  61. keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
  62. keys.CloudEnv = api.CLOUD_ENV_ON_PREMISE
  63. keys.Provider = api.CLOUD_PROVIDER_ONECLOUD
  64. keys.Brand = api.ONECLOUD_BRAND_ONECLOUD
  65. keys.Hypervisor = api.HYPERVISOR_KVM
  66. return keys
  67. }
  68. func (self *SKVMGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
  69. return cloudprovider.SInstanceCapability{
  70. Hypervisor: self.GetHypervisor(),
  71. Provider: self.GetProvider(),
  72. DefaultAccount: cloudprovider.SDefaultAccount{
  73. Linux: cloudprovider.SOsDefaultAccount{
  74. DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER,
  75. Changeable: true,
  76. },
  77. Windows: cloudprovider.SOsDefaultAccount{
  78. DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER,
  79. },
  80. },
  81. Storages: cloudprovider.Storage{
  82. SysDisk: []cloudprovider.StorageInfo{
  83. {StorageType: api.STORAGE_LOCAL, MinSizeGb: options.Options.LocalSysDiskMinSizeGB, MaxSizeGb: options.Options.LocalSysDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  84. {StorageType: api.STORAGE_RBD, MinSizeGb: options.Options.LocalSysDiskMinSizeGB, MaxSizeGb: options.Options.LocalSysDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  85. {StorageType: api.STORAGE_NFS, MinSizeGb: options.Options.LocalSysDiskMinSizeGB, MaxSizeGb: options.Options.LocalSysDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  86. {StorageType: api.STORAGE_GPFS, MinSizeGb: options.Options.LocalSysDiskMinSizeGB, MaxSizeGb: options.Options.LocalSysDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  87. },
  88. DataDisk: []cloudprovider.StorageInfo{
  89. {StorageType: api.STORAGE_LOCAL, MinSizeGb: options.Options.LocalDataDiskMinSizeGB, MaxSizeGb: options.Options.LocalDataDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  90. {StorageType: api.STORAGE_RBD, MinSizeGb: options.Options.LocalDataDiskMinSizeGB, MaxSizeGb: options.Options.LocalDataDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  91. {StorageType: api.STORAGE_NFS, MinSizeGb: options.Options.LocalDataDiskMinSizeGB, MaxSizeGb: options.Options.LocalDataDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  92. {StorageType: api.STORAGE_GPFS, MinSizeGb: options.Options.LocalDataDiskMinSizeGB, MaxSizeGb: options.Options.LocalDataDiskMaxSizeGB, StepSizeGb: 1, Resizable: true},
  93. },
  94. },
  95. }
  96. }
  97. func (self *SKVMGuestDriver) GetDefaultSysDiskBackend() string {
  98. return api.STORAGE_LOCAL
  99. }
  100. func (self *SKVMGuestDriver) GetMinimalSysDiskSizeGb() int {
  101. return options.Options.DefaultDiskSizeMB / 1024
  102. }
  103. func (self *SKVMGuestDriver) RequestDetachDisksFromGuestForDelete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  104. subtask, err := taskman.TaskManager.NewTask(ctx, "GuestDetachAllDisksTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
  105. if err != nil {
  106. return err
  107. }
  108. subtask.ScheduleRun(nil)
  109. return nil
  110. }
  111. func (self *SKVMGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  112. subtask, err := taskman.TaskManager.NewTask(ctx, "KVMGuestCreateDiskTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
  113. if err != nil {
  114. return err
  115. }
  116. subtask.ScheduleRun(nil)
  117. return nil
  118. }
  119. func (self *SKVMGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
  120. obj, err := models.SnapshotManager.FetchById(snapshotId)
  121. if err != nil {
  122. return errors.Wrapf(err, "failed to find snapshot %s", snapshotId)
  123. }
  124. snapshot := obj.(*models.SSnapshot)
  125. host, _ := guest.GetHost()
  126. url := fmt.Sprintf("%s/servers/%s/snapshot", host.ManagerUri, guest.Id)
  127. body := jsonutils.NewDict()
  128. body.Set("disk_id", jsonutils.NewString(diskId))
  129. body.Set("snapshot_id", jsonutils.NewString(snapshotId))
  130. if snapshot.DiskBackupId != "" {
  131. backupObj, err := models.DiskBackupManager.FetchById(snapshot.DiskBackupId)
  132. if err != nil {
  133. return errors.Wrapf(err, "failed to find backup %s", snapshot.DiskBackupId)
  134. }
  135. backup := backupObj.(*models.SDiskBackup)
  136. body.Set("backup_disk_config", jsonutils.Marshal(backup.DiskConfig))
  137. }
  138. header := self.getTaskRequestHeader(task)
  139. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  140. return err
  141. }
  142. func (self *SKVMGuestDriver) RequestDeleteSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
  143. host, _ := guest.GetHost()
  144. url := fmt.Sprintf("%s/servers/%s/delete-snapshot", host.ManagerUri, guest.Id)
  145. header := self.getTaskRequestHeader(task)
  146. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, params, false)
  147. return err
  148. }
  149. func (self *SKVMGuestDriver) RequestReloadDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
  150. host, _ := guest.GetHost()
  151. url := fmt.Sprintf("%s/servers/%s/reload-disk-snapshot", host.ManagerUri, guest.Id)
  152. header := self.getTaskRequestHeader(task)
  153. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, params, false)
  154. return err
  155. }
  156. func findVNCPort(results string) int {
  157. vncInfo := strings.Split(results, "\n")
  158. addrParts := strings.Split(vncInfo[1], ":")
  159. port, _ := strconv.Atoi(strings.TrimSpace(addrParts[len(addrParts)-1]))
  160. return port
  161. }
  162. func findVNCPort2(results string) int {
  163. vncInfo := strings.Split(results, "\n")
  164. for i := 0; i < len(vncInfo); i++ {
  165. lineStr := strings.TrimSpace(vncInfo[i])
  166. if strings.HasSuffix(lineStr, "(ipv4)") {
  167. addrParts := strings.Split(lineStr, ":")
  168. v := addrParts[len(addrParts)-1]
  169. port, _ := strconv.Atoi(v[0 : len(v)-7])
  170. return port
  171. }
  172. }
  173. return -1
  174. }
  175. func (self *SKVMGuestDriver) GetGuestVncInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  176. url := fmt.Sprintf("/servers/%s/monitor", guest.Id)
  177. body := jsonutils.NewDict()
  178. var cmd string
  179. if guest.GetVdi() == "spice" {
  180. cmd = "info spice"
  181. } else {
  182. cmd = "info vnc"
  183. }
  184. body.Add(jsonutils.NewString(cmd), "cmd")
  185. ret, err := host.Request(ctx, userCred, "POST", url, nil, body)
  186. if err != nil {
  187. return nil, errors.Wrapf(err, "Fail to request VNC info")
  188. }
  189. results, err := ret.GetString("results")
  190. if len(results) == 0 {
  191. return nil, errors.Wrapf(err, "Can't get vnc information from host.")
  192. }
  193. // info_vnc = result['results'].split('\n')
  194. // port = int(info_vnc[1].split(':')[-1].split()[0])
  195. /* $ QEMU 2.9.1
  196. info spice $ QEMU 2.12.1 monitor Server:
  197. Server: (qemu) info vnc address: 0.0.0.0:5901
  198. address: *:5921 info vnc auth: none
  199. migrated: false default: Client: none
  200. auth: spice Server: :::5902 (ipv6) $ QEMU 2.12.1 monitor without ipv6
  201. compiled: 0.13.3 Auth: none (Sub: none) (qemu) info vnc
  202. mouse-mode: server Server: 0.0.0.0:5902 (ipv4) info vnc
  203. Channels: none Auth: none (Sub: none) default:
  204. Server: 0.0.0.0:5902 (ipv4)
  205. Auth: none (Sub: none)
  206. */
  207. var port int
  208. if guest.CheckQemuVersion(guest.GetMetadata(ctx, "__qemu_version", userCred), "2.12.1") && strings.HasSuffix(cmd, "vnc") {
  209. port = findVNCPort2(results)
  210. } else {
  211. port = findVNCPort(results)
  212. }
  213. if port < 5900 {
  214. return nil, httperrors.NewResourceNotReadyError("invalid vnc port %d", port)
  215. }
  216. if len(host.AccessIp) == 0 {
  217. return nil, httperrors.NewResourceNotReadyError("the host %s loses its ip address", host.Name)
  218. }
  219. password := guest.GetMetadata(ctx, "__vnc_password", userCred)
  220. result := &cloudprovider.ServerVncOutput{
  221. Host: host.AccessIp,
  222. Protocol: guest.GetVdi(),
  223. Port: int64(port),
  224. Hypervisor: api.HYPERVISOR_KVM,
  225. Password: password,
  226. }
  227. return result, nil
  228. }
  229. func (self *SKVMGuestDriver) RequestStopOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask, syncStatus bool) error {
  230. body := jsonutils.NewDict()
  231. params := task.GetParams()
  232. timeout, err := params.Int("timeout")
  233. if err != nil {
  234. timeout = 30
  235. }
  236. isForce, err := params.Bool("is_force")
  237. if isForce {
  238. timeout = 0
  239. }
  240. body.Add(jsonutils.NewInt(timeout), "timeout")
  241. header := self.getTaskRequestHeader(task)
  242. url := fmt.Sprintf("%s/servers/%s/stop", host.ManagerUri, guest.Id)
  243. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  244. return err
  245. }
  246. func (self *SKVMGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
  247. url := fmt.Sprintf("%s/servers/%s", host.ManagerUri, guest.Id)
  248. header := self.getTaskRequestHeader(task)
  249. body := jsonutils.NewDict()
  250. if guest.HostId != host.Id {
  251. body.Set("migrated", jsonutils.JSONTrue)
  252. }
  253. _, res, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "DELETE", url, header, body, false)
  254. if err != nil {
  255. return err
  256. }
  257. delayClean := jsonutils.QueryBoolean(res, "delay_clean", false)
  258. if res != nil && delayClean {
  259. return nil
  260. }
  261. task.ScheduleRun(nil)
  262. return nil
  263. }
  264. func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
  265. desc := guest.GetJsonDescAtHypervisor(ctx, host)
  266. if len(desc.UserData) > 0 {
  267. // host 需要加密后的user-data以提供 http://169.254.169.254/latest/user-data 解密访问
  268. desc.UserData = base64.StdEncoding.EncodeToString([]byte(desc.UserData))
  269. }
  270. return jsonutils.Marshal(desc), nil
  271. }
  272. func (self *SKVMGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
  273. config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
  274. if err != nil {
  275. log.Errorf("GetDeployConfigOnHost error: %v", err)
  276. return err
  277. }
  278. log.Debugf("RequestDeployGuestOnHost: %s", config)
  279. action, err := config.GetString("action")
  280. if err != nil {
  281. return err
  282. }
  283. url := fmt.Sprintf("%s/servers/%s/%s", host.ManagerUri, guest.Id, action)
  284. header := self.getTaskRequestHeader(task)
  285. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, config, false)
  286. if err != nil {
  287. return err
  288. }
  289. return nil
  290. }
  291. func (self *SKVMGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error {
  292. guest.SaveDeployInfo(ctx, task.GetUserCred(), data)
  293. return nil
  294. }
  295. func (self *SKVMGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
  296. header := self.getTaskRequestHeader(task)
  297. config := jsonutils.NewDict()
  298. drv, err := guest.GetDriver()
  299. if err != nil {
  300. return err
  301. }
  302. desc, err := drv.GetJsonDescAtHost(ctx, userCred, guest, host, nil)
  303. if err != nil {
  304. return errors.Wrapf(err, "GetJsonDescAtHost")
  305. }
  306. config.Add(desc, "desc")
  307. params := task.GetParams()
  308. if params.Length() > 0 {
  309. config.Add(params, "params")
  310. }
  311. url := fmt.Sprintf("%s/servers/%s/start", host.ManagerUri, guest.Id)
  312. _, body, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, config, false)
  313. if err != nil {
  314. return err
  315. }
  316. if jsonutils.QueryBoolean(body, "is_running", false) {
  317. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  318. return body, nil
  319. })
  320. }
  321. return nil
  322. }
  323. func (self *SKVMGuestDriver) RequestSyncstatusOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
  324. header := self.getTaskRequestHeader(task)
  325. url := fmt.Sprintf("%s/servers/%s/status", host.ManagerUri, guest.Id)
  326. _, res, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "GET", url, header, nil, false)
  327. if err != nil {
  328. return err
  329. }
  330. statusStr, _ := res.GetString("status")
  331. if len(statusStr) > 0 {
  332. // may be an old version host, use sync request
  333. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  334. // delay response to ensure event order
  335. time.Sleep(time.Second)
  336. return res, nil
  337. })
  338. }
  339. return nil
  340. }
  341. func (self *SKVMGuestDriver) OnDeleteGuestFinalCleanup(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential) error {
  342. if ispId := guest.GetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, userCred); len(ispId) > 0 {
  343. ispM, err := models.InstanceSnapshotManager.FetchById(ispId)
  344. if err == nil {
  345. isp := ispM.(*models.SInstanceSnapshot)
  346. isp.DecRefCount(ctx, userCred)
  347. }
  348. guest.SetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, "", userCred)
  349. }
  350. return nil
  351. }
  352. func (self *SKVMGuestDriver) IsSupportEip() bool {
  353. return true
  354. }
  355. func (self *SKVMGuestDriver) IsSupportShutdownMode() bool {
  356. return true
  357. }
  358. func (self *SKVMGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerCreateEipInput) error {
  359. return nil
  360. }
  361. func (self *SKVMGuestDriver) RequestAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, eip *models.SElasticip, task taskman.ITask) error {
  362. defer task.ScheduleRun(nil)
  363. lockman.LockObject(ctx, guest)
  364. defer lockman.ReleaseObject(ctx, guest)
  365. var guestnics []models.SGuestnetwork
  366. {
  367. netq := models.NetworkManager.Query().SubQuery()
  368. wirq := models.WireManager.Query().SubQuery()
  369. vpcq := models.VpcManager.Query().SubQuery()
  370. gneq := models.GuestnetworkManager.Query()
  371. q := gneq.Equals("guest_id", guest.Id).
  372. IsNullOrEmpty("eip_id")
  373. q = q.Join(netq, sqlchemy.Equals(netq.Field("id"), gneq.Field("network_id")))
  374. q = q.Join(wirq, sqlchemy.Equals(wirq.Field("id"), netq.Field("wire_id")))
  375. q = q.Join(vpcq, sqlchemy.Equals(vpcq.Field("id"), wirq.Field("vpc_id")))
  376. q = q.Filter(sqlchemy.NotEquals(vpcq.Field("id"), api.DEFAULT_VPC_ID))
  377. if err := db.FetchModelObjects(models.GuestnetworkManager, q, &guestnics); err != nil {
  378. return err
  379. }
  380. if len(guestnics) == 0 {
  381. return errors.Errorf("guest has no nics to associate eip")
  382. }
  383. }
  384. guestnic := &guestnics[0]
  385. lockman.LockObject(ctx, guestnic)
  386. defer lockman.ReleaseObject(ctx, guestnic)
  387. if _, err := db.Update(guestnic, func() error {
  388. guestnic.EipId = eip.Id
  389. return nil
  390. }); err != nil {
  391. return errors.Wrapf(err, "set associated eip for guestnic %s (guest:%s, network:%s)",
  392. guestnic.Ifname, guestnic.GuestId, guestnic.NetworkId)
  393. }
  394. if err := eip.AssociateInstance(ctx, userCred, api.EIP_ASSOCIATE_TYPE_SERVER, guest); err != nil {
  395. return errors.Wrapf(err, "associate eip %s(%s) to vm %s(%s)", eip.Name, eip.Id, guest.Name, guest.Id)
  396. }
  397. if err := eip.SetStatus(ctx, userCred, api.EIP_STATUS_READY, api.EIP_STATUS_ASSOCIATE); err != nil {
  398. return errors.Wrapf(err, "set eip status to %s", api.EIP_STATUS_ALLOCATE)
  399. }
  400. return nil
  401. }
  402. func (self *SKVMGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, cpuSockets, vmemSize int64) error {
  403. taskParams := task.GetParams()
  404. if jsonutils.QueryBoolean(taskParams, "guest_online", false) {
  405. addCpu := vcpuCount - int64(guest.VcpuCount)
  406. addMem := vmemSize - int64(guest.VmemSize)
  407. if addCpu < 0 || addMem < 0 {
  408. return fmt.Errorf("KVM guest doesn't support online reduce cpu or mem")
  409. }
  410. header := task.GetTaskRequestHeader()
  411. body := jsonutils.NewDict()
  412. if vcpuCount > int64(guest.VcpuCount) {
  413. body.Set("add_cpu", jsonutils.NewInt(addCpu))
  414. body.Set("total_cpu", jsonutils.NewInt(int64(guest.VcpuCount)))
  415. }
  416. if vmemSize > int64(guest.VmemSize) {
  417. body.Set("add_mem", jsonutils.NewInt(addMem))
  418. body.Set("total_mem", jsonutils.NewInt(int64(guest.VmemSize)))
  419. }
  420. if taskParams.Contains("cpu_numa_pin") {
  421. cpuNumaPin, _ := taskParams.Get("cpu_numa_pin")
  422. body.Set("cpu_numa_pin", cpuNumaPin)
  423. }
  424. host, _ := guest.GetHost()
  425. url := fmt.Sprintf("%s/servers/%s/hotplug-cpu-mem", host.ManagerUri, guest.Id)
  426. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  427. return err
  428. } else {
  429. task.ScheduleRun(nil)
  430. return nil
  431. }
  432. }
  433. func (self *SKVMGuestDriver) RequestSoftReset(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  434. _, err := guest.SendMonitorCommand(
  435. ctx, task.GetUserCred(),
  436. &api.ServerMonitorInput{COMMAND: "system_reset"},
  437. )
  438. return err
  439. }
  440. func (self *SKVMGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error {
  441. host, _ := guest.GetHost()
  442. header := task.GetTaskRequestHeader()
  443. url := fmt.Sprintf("%s/servers/%s/status", host.ManagerUri, guest.Id)
  444. task.SetStage("OnGetGuestStatus", nil)
  445. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "GET", url, header, nil, false)
  446. return err
  447. }
  448. func (self *SKVMGuestDriver) RequestAttachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error {
  449. return guest.StartSyncTaskWithoutSyncstatus(
  450. ctx,
  451. task.GetUserCred(),
  452. jsonutils.QueryBoolean(task.GetParams(), "sync_desc_only", false),
  453. task.GetTaskId(),
  454. )
  455. }
  456. func (self *SKVMGuestDriver) RequestSaveImage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, task taskman.ITask) error {
  457. disks := guest.CategorizeDisks()
  458. opts := api.DiskSaveInput{}
  459. task.GetParams().Unmarshal(&opts)
  460. return disks.Root.StartDiskSaveTask(ctx, userCred, opts, task.GetTaskId())
  461. }
  462. func (self *SKVMGuestDriver) RequestOpenForward(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, req *guestdriver_types.OpenForwardRequest) (*guestdriver_types.OpenForwardResponse, error) {
  463. var (
  464. host, _ = guest.GetHost()
  465. url = fmt.Sprintf("%s/servers/%s/open-forward", host.ManagerUri, guest.Id)
  466. httpClient = httputils.GetDefaultClient()
  467. header = mcclient.GetTokenHeaders(userCred)
  468. hostreq = &host_api.GuestOpenForwardRequest{
  469. NetworkId: req.NetworkId,
  470. Proto: req.Proto,
  471. Addr: req.Addr,
  472. Port: req.Port,
  473. }
  474. body = jsonutils.Marshal(hostreq)
  475. )
  476. _, respBody, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  477. if err != nil {
  478. return nil, errors.Wrap(err, "host request")
  479. }
  480. hostresp := &host_api.GuestOpenForwardResponse{}
  481. if err := respBody.Unmarshal(hostresp); err != nil {
  482. return nil, errors.Wrap(err, "unmarshal host response")
  483. }
  484. resp := &guestdriver_types.OpenForwardResponse{
  485. Proto: hostresp.Proto,
  486. ProxyAddr: hostresp.ProxyAddr,
  487. ProxyPort: hostresp.ProxyPort,
  488. Addr: hostresp.Addr,
  489. Port: hostresp.Port,
  490. }
  491. return resp, nil
  492. }
  493. func (self *SKVMGuestDriver) RequestCloseForward(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, req *guestdriver_types.CloseForwardRequest) (*guestdriver_types.CloseForwardResponse, error) {
  494. var (
  495. host, _ = guest.GetHost()
  496. url = fmt.Sprintf("%s/servers/%s/close-forward", host.ManagerUri, guest.Id)
  497. httpClient = httputils.GetDefaultClient()
  498. header = mcclient.GetTokenHeaders(userCred)
  499. hostreq = &host_api.GuestCloseForwardRequest{
  500. NetworkId: req.NetworkId,
  501. Proto: req.Proto,
  502. ProxyAddr: req.ProxyAddr,
  503. ProxyPort: req.ProxyPort,
  504. }
  505. body = jsonutils.Marshal(hostreq)
  506. )
  507. _, respBody, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  508. if err != nil {
  509. return nil, errors.Wrap(err, "host request")
  510. }
  511. hostresp := &host_api.GuestCloseForwardResponse{}
  512. if err := respBody.Unmarshal(hostresp); err != nil {
  513. return nil, errors.Wrap(err, "unmarshal host response")
  514. }
  515. resp := &guestdriver_types.CloseForwardResponse{
  516. Proto: hostresp.Proto,
  517. ProxyAddr: hostresp.ProxyAddr,
  518. ProxyPort: hostresp.ProxyPort,
  519. }
  520. return resp, nil
  521. }
  522. func (self *SKVMGuestDriver) RequestListForward(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, req *guestdriver_types.ListForwardRequest) (*guestdriver_types.ListForwardResponse, error) {
  523. var (
  524. host, _ = guest.GetHost()
  525. url = fmt.Sprintf("%s/servers/%s/list-forward", host.ManagerUri, guest.Id)
  526. httpClient = httputils.GetDefaultClient()
  527. header = mcclient.GetTokenHeaders(userCred)
  528. hostreq = &host_api.GuestListForwardRequest{
  529. NetworkId: req.NetworkId,
  530. Proto: req.Proto,
  531. Addr: req.Addr,
  532. Port: req.Port,
  533. }
  534. body = jsonutils.Marshal(hostreq)
  535. )
  536. _, respBody, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  537. if err != nil {
  538. return nil, errors.Wrap(err, "host request")
  539. }
  540. hostresp := &host_api.GuestListForwardResponse{}
  541. if err := respBody.Unmarshal(hostresp); err != nil {
  542. return nil, errors.Wrap(err, "unmarshal host response")
  543. }
  544. var respForwards []guestdriver_types.OpenForwardResponse
  545. for i := range hostresp.Forwards {
  546. respForwards = append(respForwards, guestdriver_types.OpenForwardResponse{
  547. Proto: hostresp.Forwards[i].Proto,
  548. ProxyAddr: hostresp.Forwards[i].ProxyAddr,
  549. ProxyPort: hostresp.Forwards[i].ProxyPort,
  550. Addr: hostresp.Forwards[i].Addr,
  551. Port: hostresp.Forwards[i].Port,
  552. })
  553. }
  554. resp := &guestdriver_types.ListForwardResponse{
  555. Forwards: respForwards,
  556. }
  557. return resp, nil
  558. }
  559. func (self *SKVMGuestDriver) GetDetachDiskStatus() ([]string, error) {
  560. return []string{api.VM_READY, api.VM_RUNNING}, nil
  561. }
  562. func (self *SKVMGuestDriver) GetAttachDiskStatus() ([]string, error) {
  563. return []string{api.VM_READY, api.VM_RUNNING}, nil
  564. }
  565. func (self *SKVMGuestDriver) GetRebuildRootStatus() ([]string, error) {
  566. return []string{api.VM_READY}, nil
  567. }
  568. func (self *SKVMGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
  569. return []string{api.VM_READY, api.VM_RUNNING}, nil
  570. }
  571. func (self *SKVMGuestDriver) GetDeployStatus() ([]string, error) {
  572. return []string{api.VM_READY, api.VM_ADMIN}, nil
  573. }
  574. func (self *SKVMGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
  575. if guest.Hypervisor == api.HYPERVISOR_KVM {
  576. if guest.GetDiskIndex(disk.Id) <= 0 && guest.Status == api.VM_RUNNING {
  577. return fmt.Errorf("Cann't online resize root disk")
  578. }
  579. if guest.Status == api.VM_RUNNING && storage.StorageType == api.STORAGE_SLVM {
  580. return fmt.Errorf("shared lvm storage cann't online resize")
  581. }
  582. }
  583. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  584. return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
  585. }
  586. return nil
  587. }
  588. func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
  589. drv, err := guest.GetDriver()
  590. if err != nil {
  591. return err
  592. }
  593. desc, err := drv.GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil)
  594. if err != nil {
  595. return errors.Wrapf(err, "GetJsonDescAtHost")
  596. }
  597. body := jsonutils.NewDict()
  598. body.Add(desc, "desc")
  599. if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only {
  600. body.Add(jsonutils.JSONTrue, "fw_only")
  601. }
  602. if setUefiBootOrder, _ := task.GetParams().Bool("set_uefi_boot_order"); setUefiBootOrder {
  603. body.Add(jsonutils.JSONTrue, "set_uefi_boot_order")
  604. }
  605. url := fmt.Sprintf("%s/servers/%s/sync", host.ManagerUri, guest.Id)
  606. header := self.getTaskRequestHeader(task)
  607. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  608. return err
  609. }
  610. func (self *SKVMGuestDriver) RequestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  611. host, _ := guest.GetHost()
  612. url := fmt.Sprintf("%s/servers/%s/suspend", host.ManagerUri, guest.Id)
  613. header := self.getTaskRequestHeader(task)
  614. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, nil, false)
  615. return err
  616. }
  617. func (self *SKVMGuestDriver) RequestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  618. host, _ := guest.GetHost()
  619. url := fmt.Sprintf("%s/servers/%s/start", host.ManagerUri, guest.Id)
  620. header := self.getTaskRequestHeader(task)
  621. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, nil, false)
  622. return err
  623. }
  624. func (self *SKVMGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  625. input := new(api.ServerCreateInput)
  626. task.GetParams().Unmarshal(input)
  627. return guest.StartGuestCreateDiskTask(ctx, task.GetUserCred(), input.Disks, task.GetTaskId())
  628. }
  629. func (self *SKVMGuestDriver) NeedRequestGuestHotAddIso(ctx context.Context, guest *models.SGuest) bool {
  630. return guest.Status == api.VM_RUNNING
  631. }
  632. func (self *SKVMGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
  633. return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
  634. }
  635. func (self *SKVMGuestDriver) RequestGuestHotRemoveIso(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  636. return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
  637. }
  638. func (self *SKVMGuestDriver) NeedRequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest) bool {
  639. return guest.Status == api.VM_RUNNING
  640. }
  641. func (self *SKVMGuestDriver) RequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
  642. return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
  643. }
  644. func (self *SKVMGuestDriver) RequestGuestHotRemoveVfd(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  645. return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
  646. }
  647. func (self *SKVMGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  648. subtask, err := taskman.TaskManager.NewTask(ctx, "KVMGuestRebuildRootTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
  649. if err != nil {
  650. return err
  651. }
  652. subtask.ScheduleRun(nil)
  653. return nil
  654. }
  655. func (self *SKVMGuestDriver) RequestSyncToBackup(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  656. host, err := guest.GetHost()
  657. if err != nil {
  658. return err
  659. }
  660. drv, err := guest.GetDriver()
  661. if err != nil {
  662. return err
  663. }
  664. desc, err := drv.GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil)
  665. if err != nil {
  666. return errors.Wrapf(err, "GetJsonDescAtHost")
  667. }
  668. body := jsonutils.NewDict()
  669. body.Add(desc, "desc")
  670. body.Set("backup_nbd_server_uri", jsonutils.NewString(guest.GetMetadata(ctx, "backup_nbd_server_uri", task.GetUserCred())))
  671. url := fmt.Sprintf("%s/servers/%s/block-replication", host.ManagerUri, guest.Id)
  672. header := self.getTaskRequestHeader(task)
  673. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  674. if err != nil {
  675. return err
  676. }
  677. return nil
  678. }
  679. func (self *SKVMGuestDriver) RequestSlaveBlockStreamDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  680. host := models.HostManager.FetchHostById(guest.BackupHostId)
  681. body := jsonutils.NewDict()
  682. url := fmt.Sprintf("%s/servers/%s/slave-block-stream-disks", host.ManagerUri, guest.Id)
  683. header := self.getTaskRequestHeader(task)
  684. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  685. return err
  686. }
  687. // kvm guest must add cpu first
  688. // if body has add_cpu_failed indicate dosen't exec add mem
  689. // 1. cpu added part of request --> add_cpu_failed: true && added_cpu: count
  690. // 2. cpu added all of request add mem failed --> add_mem_failed: true
  691. func (self *SKVMGuestDriver) OnGuestChangeCpuMemFailed(ctx context.Context, guest *models.SGuest, data *jsonutils.JSONDict, task taskman.ITask) error {
  692. var cpuAdded int64
  693. if jsonutils.QueryBoolean(data, "add_cpu_failed", false) {
  694. cpuAdded, _ = data.Int("added_cpu")
  695. } else if jsonutils.QueryBoolean(data, "add_mem_failed", false) {
  696. vcpuCount, _ := task.GetParams().Int("vcpu_count")
  697. if vcpuCount-int64(guest.VcpuCount) > 0 {
  698. cpuAdded = vcpuCount - int64(guest.VcpuCount)
  699. }
  700. }
  701. if cpuAdded > 0 {
  702. _, err := db.Update(guest, func() error {
  703. guest.VcpuCount = guest.VcpuCount + int(cpuAdded)
  704. return nil
  705. })
  706. if err != nil {
  707. return err
  708. }
  709. db.OpsLog.LogEvent(guest, db.ACT_CHANGE_FLAVOR,
  710. fmt.Sprintf("Change config task failed but added cpu count %d", cpuAdded), task.GetUserCred())
  711. logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_CHANGE_FLAVOR,
  712. fmt.Sprintf("Change config task failed but added cpu count %d", cpuAdded), task.GetUserCred(), false)
  713. models.HostManager.ClearSchedDescCache(guest.HostId)
  714. }
  715. return nil
  716. }
  717. func (self *SKVMGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) {
  718. return true, nil
  719. }
  720. func (self *SKVMGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
  721. return true, nil
  722. }
  723. func (self *SKVMGuestDriver) IsSupportMigrate() bool {
  724. return true
  725. }
  726. func (self *SKVMGuestDriver) IsSupportLiveMigrate() bool {
  727. return true
  728. }
  729. func checkAssignHost(ctx context.Context, userCred mcclient.TokenCredential, preferHost string) error {
  730. iHost, _ := models.HostManager.FetchByIdOrName(ctx, userCred, preferHost)
  731. if iHost == nil {
  732. return httperrors.NewBadRequestError("Host %s not found", preferHost)
  733. }
  734. host := iHost.(*models.SHost)
  735. err := host.IsAssignable(ctx, userCred)
  736. if err != nil {
  737. return errors.Wrap(err, "IsAssignable")
  738. }
  739. return nil
  740. }
  741. func (self *SKVMGuestDriver) CheckMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.GuestMigrateInput) error {
  742. if len(guest.BackupHostId) > 0 {
  743. return httperrors.NewBadRequestError("Guest have backup, can't migrate")
  744. }
  745. if !input.IsRescueMode && guest.Status != api.VM_READY {
  746. return httperrors.NewServerStatusError("Cannot normal migrate guest in status %s, try rescue mode or server-live-migrate?", guest.Status)
  747. }
  748. if input.IsRescueMode {
  749. host, err := guest.GetHost()
  750. if err != nil {
  751. return err
  752. }
  753. if host.HostStatus != api.HOST_OFFLINE {
  754. return httperrors.NewBadRequestError("Host status %s, can't do rescue mode migration", host.HostStatus)
  755. }
  756. disks, err := guest.GetDisks()
  757. if err != nil {
  758. return errors.Wrapf(err, "GetDisks")
  759. }
  760. for _, disk := range disks {
  761. storage, _ := disk.GetStorage()
  762. if utils.IsInStringArray(
  763. storage.StorageType, api.STORAGE_LOCAL_TYPES) {
  764. return httperrors.NewBadRequestError("Rescue mode requires all disk store in shared storages")
  765. }
  766. }
  767. }
  768. devices, err := guest.GetIsolatedDevices()
  769. if err != nil {
  770. return errors.Wrapf(err, "GetIsolatedDevices")
  771. }
  772. if len(devices) > 0 {
  773. return httperrors.NewBadRequestError("Cannot migrate with isolated devices")
  774. }
  775. if len(input.PreferHostId) > 0 {
  776. err := checkAssignHost(ctx, userCred, input.PreferHostId)
  777. if err != nil {
  778. return errors.Wrap(err, "checkAssignHost")
  779. }
  780. }
  781. return nil
  782. }
  783. func (self *SKVMGuestDriver) CheckLiveMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.GuestLiveMigrateInput) error {
  784. if len(guest.BackupHostId) > 0 {
  785. return httperrors.NewBadRequestError("Guest have backup, can't migrate")
  786. }
  787. if utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_SUSPEND}) {
  788. if input.MaxBandwidthMb != nil && *input.MaxBandwidthMb < 50 {
  789. return httperrors.NewBadRequestError("max bandwidth must gratethan 100M")
  790. }
  791. cdrom := guest.GetCdrom()
  792. if cdrom != nil && len(cdrom.ImageId) > 0 {
  793. return httperrors.NewBadRequestError("Cannot live migrate with cdrom")
  794. }
  795. devices, err := guest.GetIsolatedDevices()
  796. if err != nil {
  797. return errors.Wrapf(err, "GetIsolatedDevices")
  798. }
  799. if len(devices) > 0 {
  800. return httperrors.NewBadRequestError("Cannot live migrate with isolated devices")
  801. }
  802. if !guest.CheckQemuVersion(guest.GetQemuVersion(userCred), "1.1.2") {
  803. return httperrors.NewBadRequestError("Cannot do live migrate, too low qemu version")
  804. }
  805. if len(input.PreferHost) > 0 {
  806. err := checkAssignHost(ctx, userCred, input.PreferHost)
  807. if err != nil {
  808. return errors.Wrap(err, "checkAssignHost")
  809. }
  810. }
  811. }
  812. return nil
  813. }
  814. func (self *SKVMGuestDriver) RequestCancelLiveMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential) error {
  815. host, _ := guest.GetHost()
  816. url := fmt.Sprintf("%s/servers/%s/cancel-live-migrate", host.ManagerUri, guest.Id)
  817. httpClient := httputils.GetDefaultClient()
  818. header := mcclient.GetTokenHeaders(userCred)
  819. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, jsonutils.NewDict(), false)
  820. if err != nil {
  821. return errors.Wrap(err, "host request")
  822. }
  823. return nil
  824. }
  825. func (self *SKVMGuestDriver) ValidateDetachNetwork(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
  826. if guest.Status == api.VM_RUNNING && guest.GetMetadata(ctx, api.VM_METADATA_HOT_REMOVE_NIC, nil) != "enable" {
  827. return httperrors.NewBadRequestError("Guest %s can't hot remove nic", guest.GetName())
  828. }
  829. return nil
  830. }
  831. func (self *SKVMGuestDriver) ValidateChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, targetStorageId string) error {
  832. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_DISK_CHANGE_STORAGE}) {
  833. return httperrors.NewBadRequestError("Cannot change disk storage in status %s", guest.Status)
  834. }
  835. // backup guest not supported
  836. if guest.BackupHostId != "" {
  837. return httperrors.NewBadRequestError("Cannot change disk storage in backup guest %s", guest.GetName())
  838. }
  839. // storage must attached on guest's host
  840. host, err := guest.GetHost()
  841. if err != nil {
  842. return errors.Wrapf(err, "Get guest %s host", guest.GetName())
  843. }
  844. attachedStorages := host.GetAttachedEnabledHostStorages(nil)
  845. foundStorage := false
  846. for _, storage := range attachedStorages {
  847. if storage.GetId() == targetStorageId {
  848. foundStorage = true
  849. }
  850. }
  851. if !foundStorage {
  852. return httperrors.NewBadRequestError("Storage %s not attached or enabled on host %s", targetStorageId, host.GetName())
  853. }
  854. return nil
  855. }
  856. func (self *SKVMGuestDriver) RequestChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input *api.ServerChangeDiskStorageInternalInput, task taskman.ITask) error {
  857. host, err := guest.GetHost()
  858. if err != nil {
  859. return err
  860. }
  861. body := jsonutils.Marshal(input)
  862. header := self.getTaskRequestHeader(task)
  863. url := fmt.Sprintf("%s/servers/%s/storage-clone-disk", host.ManagerUri, guest.GetId())
  864. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  865. return err
  866. }
  867. func (self *SKVMGuestDriver) RequestSwitchToTargetStorageDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input *api.ServerChangeDiskStorageInternalInput, task taskman.ITask) error {
  868. host, err := guest.GetHost()
  869. if err != nil {
  870. return err
  871. }
  872. body := jsonutils.Marshal(input)
  873. header := self.getTaskRequestHeader(task)
  874. url := fmt.Sprintf("%s/servers/%s/live-change-disk", host.ManagerUri, guest.GetId())
  875. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  876. return err
  877. }
  878. func (self *SKVMGuestDriver) validateVdiProtocol(vdi string) error {
  879. if !utils.IsInStringArray(vdi, []string{api.VM_VDI_PROTOCOL_VNC, api.VM_VDI_PROTOCOL_SPICE}) {
  880. return httperrors.NewInputParameterError("unsupported vdi protocol %s", vdi)
  881. }
  882. return nil
  883. }
  884. func (self *SKVMGuestDriver) validateVGA(ovdi, ovga string, nvdi, nvga *string) (vdi, vga string) {
  885. vdi = ovdi
  886. if nvdi != nil {
  887. vdi = *nvdi
  888. }
  889. if vdi != api.VM_VDI_PROTOCOL_VNC && vdi != api.VM_VDI_PROTOCOL_SPICE {
  890. vdi = api.VM_VDI_PROTOCOL_VNC
  891. }
  892. var candidateVga []string
  893. switch vdi {
  894. case api.VM_VDI_PROTOCOL_VNC:
  895. candidateVga = []string{api.VM_VIDEO_STANDARD, api.VM_VIDEO_QXL, api.VM_VIDEO_VIRTIO}
  896. case api.VM_VDI_PROTOCOL_SPICE:
  897. candidateVga = []string{api.VM_VIDEO_QXL, api.VM_VIDEO_VIRTIO}
  898. }
  899. vga = ovga
  900. if nvga != nil {
  901. vga = *nvga
  902. }
  903. if !utils.IsInStringArray(vga, candidateVga) {
  904. vga = candidateVga[0]
  905. }
  906. return
  907. }
  908. func (self *SKVMGuestDriver) validateMachineType(machine string, osArch string) error {
  909. var candidate []string
  910. if apis.IsARM(osArch) || apis.IsRISCV(osArch) {
  911. candidate = []string{api.VM_MACHINE_TYPE_VIRT}
  912. } else {
  913. candidate = []string{api.VM_MACHINE_TYPE_PC, api.VM_MACHINE_TYPE_Q35}
  914. }
  915. if !utils.IsInStringArray(machine, candidate) {
  916. return httperrors.NewInputParameterError("Invalid machine type %q for arch %q", machine, osArch)
  917. }
  918. return nil
  919. }
  920. func (self *SKVMGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  921. input, err := self.SVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input)
  922. if err != nil {
  923. return input, errors.Wrap(err, "SVirtualizedGuestDriver.ValidateCreateData")
  924. }
  925. if input.Vdi != "" {
  926. err = self.validateVdiProtocol(input.Vdi)
  927. if err != nil {
  928. return nil, errors.Wrap(err, "validateVdiProtocol")
  929. }
  930. }
  931. if input.Vdi != "" || input.Vga != "" {
  932. input.Vdi, input.Vga = self.validateVGA("", "", &input.Vdi, &input.Vga)
  933. }
  934. if input.Machine != "" {
  935. if err := self.validateMachineType(input.Machine, input.OsArch); err != nil {
  936. return nil, errors.Wrap(err, "validateMachineType")
  937. }
  938. }
  939. for i := range input.Secgroups {
  940. if input.Secgroups[i] == api.SECGROUP_DEFAULT_ID {
  941. continue
  942. }
  943. secObj, err := validators.ValidateModel(ctx, userCred, models.SecurityGroupManager, &input.Secgroups[i])
  944. if err != nil {
  945. return nil, err
  946. }
  947. secgroup := secObj.(*models.SSecurityGroup)
  948. if secgroup.CloudregionId != api.DEFAULT_REGION_ID {
  949. return nil, httperrors.NewInputParameterError("invalid secgroup %s", secgroup.Name)
  950. }
  951. }
  952. return input, nil
  953. }
  954. func (self *SKVMGuestDriver) ValidateUpdateData(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.ServerUpdateInput) (api.ServerUpdateInput, error) {
  955. input, err := self.SVirtualizedGuestDriver.ValidateUpdateData(ctx, guest, userCred, input)
  956. if err != nil {
  957. return input, errors.Wrap(err, "SVirtualizedGuestDriver.ValidateUpdateData")
  958. }
  959. if input.Vdi != nil {
  960. err = self.validateVdiProtocol(*input.Vdi)
  961. if err != nil {
  962. return input, errors.Wrap(err, "validateVdiProtocol")
  963. }
  964. }
  965. if input.Vga != nil || input.Vdi != nil {
  966. vdi, vga := self.validateVGA(guest.Vdi, guest.Vga, input.Vdi, input.Vga)
  967. input.Vdi = &vdi
  968. input.Vga = &vga
  969. }
  970. if input.Machine != nil {
  971. err := self.validateMachineType(*input.Machine, guest.OsArch)
  972. if err != nil {
  973. return input, errors.Wrap(err, "ValidateMachineType")
  974. }
  975. }
  976. return input, nil
  977. }
  978. func (self *SKVMGuestDriver) RequestSyncIsolatedDevice(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  979. return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
  980. }
  981. func (self *SKVMGuestDriver) RequestCPUSet(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, guest *models.SGuest, input *api.ServerCPUSetInput) (*api.ServerCPUSetResp, error) {
  982. url := fmt.Sprintf("%s/servers/%s/cpuset", host.ManagerUri, guest.Id)
  983. httpClient := httputils.GetDefaultClient()
  984. header := mcclient.GetTokenHeaders(userCred)
  985. body := jsonutils.Marshal(input)
  986. _, respBody, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  987. if err != nil {
  988. return nil, errors.Wrap(err, "host request")
  989. }
  990. resp := new(api.ServerCPUSetResp)
  991. if respBody == nil {
  992. return resp, nil
  993. }
  994. if err := respBody.Unmarshal(resp); err != nil {
  995. return nil, errors.Wrap(err, "unmarshal response")
  996. }
  997. return resp, nil
  998. }
  999. func (self *SKVMGuestDriver) RequestCPUSetRemove(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, guest *models.SGuest, input *api.ServerCPUSetRemoveInput) error {
  1000. url := fmt.Sprintf("%s/servers/%s/cpuset-remove", host.ManagerUri, guest.Id)
  1001. httpClient := httputils.GetDefaultClient()
  1002. header := mcclient.GetTokenHeaders(userCred)
  1003. body := jsonutils.Marshal(input)
  1004. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1005. if err != nil {
  1006. return errors.Wrap(err, "host request")
  1007. }
  1008. return nil
  1009. }
  1010. func (self *SKVMGuestDriver) QgaRequestGuestPing(ctx context.Context, header http.Header, host *models.SHost, guest *models.SGuest, async bool, input *api.ServerQgaTimeoutInput) error {
  1011. url := fmt.Sprintf("%s/servers/%s/qga-guest-ping", host.ManagerUri, guest.Id)
  1012. httpClient := httputils.GetDefaultClient()
  1013. body := jsonutils.NewDict()
  1014. if input != nil {
  1015. body.Set("timeout", jsonutils.NewInt(int64(input.Timeout)))
  1016. }
  1017. body.Set("async", jsonutils.NewBool(async))
  1018. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1019. if err != nil {
  1020. return errors.Wrap(err, "host request")
  1021. }
  1022. return nil
  1023. }
  1024. func (self *SKVMGuestDriver) QgaRequestGuestInfoTask(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1025. url := fmt.Sprintf("%s/servers/%s/qga-guest-info-task", host.ManagerUri, guest.Id)
  1026. httpClient := httputils.GetDefaultClient()
  1027. header := mcclient.GetTokenHeaders(userCred)
  1028. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, nil, false)
  1029. if err != nil {
  1030. return nil, errors.Wrap(err, "host request")
  1031. }
  1032. return res, nil
  1033. }
  1034. func (self *SKVMGuestDriver) QgaRequestSetNetwork(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1035. url := fmt.Sprintf("%s/servers/%s/qga-set-network", host.ManagerUri, guest.Id)
  1036. httpClient := httputils.GetDefaultClient()
  1037. header := task.GetTaskRequestHeader()
  1038. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1039. if err != nil {
  1040. return nil, errors.Wrap(err, "host request")
  1041. }
  1042. return res, nil
  1043. }
  1044. func (self *SKVMGuestDriver) QgaRequestGetNetwork(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1045. url := fmt.Sprintf("%s/servers/%s/qga-get-network", host.ManagerUri, guest.Id)
  1046. httpClient := httputils.GetDefaultClient()
  1047. header := mcclient.GetTokenHeaders(userCred)
  1048. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, nil, false)
  1049. if err != nil {
  1050. return nil, errors.Wrap(err, "host request")
  1051. }
  1052. return res, nil
  1053. }
  1054. func (self *SKVMGuestDriver) QgaRequestGetOsInfo(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1055. url := fmt.Sprintf("%s/servers/%s/qga-get-os-info", host.ManagerUri, guest.Id)
  1056. httpClient := httputils.GetDefaultClient()
  1057. header := mcclient.GetTokenHeaders(userCred)
  1058. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, nil, false)
  1059. if err != nil {
  1060. return nil, errors.Wrap(err, "host request")
  1061. }
  1062. return res, nil
  1063. }
  1064. func (self *SKVMGuestDriver) QgaRequestSetUserPassword(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input *api.ServerQgaSetPasswordInput) error {
  1065. url := fmt.Sprintf("%s/servers/%s/qga-set-password", host.ManagerUri, guest.Id)
  1066. httpClient := httputils.GetDefaultClient()
  1067. header := task.GetTaskRequestHeader()
  1068. body := jsonutils.Marshal(input)
  1069. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1070. if err != nil {
  1071. return errors.Wrap(err, "host request")
  1072. }
  1073. return nil
  1074. }
  1075. func (self *SKVMGuestDriver) RequestQgaCommand(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1076. url := fmt.Sprintf("%s/servers/%s/qga-command", host.ManagerUri, guest.Id)
  1077. httpClient := httputils.GetDefaultClient()
  1078. header := mcclient.GetTokenHeaders(userCred)
  1079. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1080. if err != nil {
  1081. return nil, errors.Wrap(err, "host request")
  1082. }
  1083. return res, nil
  1084. }
  1085. func (self *SKVMGuestDriver) RequestGuestScreenDump(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) (jsonutils.JSONObject, error) {
  1086. url := fmt.Sprintf("%s/servers/%s/guest-screen-dump", host.ManagerUri, guest.Id)
  1087. httpClient := httputils.GetDefaultClient()
  1088. header := mcclient.GetTokenHeaders(userCred)
  1089. _, res, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, nil, false)
  1090. if err != nil {
  1091. return nil, errors.Wrap(err, "host request")
  1092. }
  1093. return res, nil
  1094. }
  1095. func (self *SKVMGuestDriver) FetchMonitorUrl(ctx context.Context, guest *models.SGuest) string {
  1096. if options.Options.KvmMonitorAgentUseMetadataService && !guest.IsSriov() {
  1097. var metadataIp string
  1098. strictIpv6, err := guest.IsStrictIpv6()
  1099. if err != nil {
  1100. log.Errorf("IsStrictIpv6 for guest %s error: %v", guest.Id, err)
  1101. }
  1102. if strictIpv6 {
  1103. metadataIp = "[" + options.Options.MetadataServerIp6s[0] + "]"
  1104. } else {
  1105. metadataIp = options.Options.MetadataServerIp4s[0]
  1106. }
  1107. return fmt.Sprintf(apis.MetaServiceMonitorAgentUrl, metadataIp)
  1108. }
  1109. return self.SVirtualizedGuestDriver.FetchMonitorUrl(ctx, guest)
  1110. }
  1111. func (self *SKVMGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error {
  1112. url := fmt.Sprintf("%s/servers/%s/reset-nic-traffic-limit", host.ManagerUri, guest.Id)
  1113. httpClient := httputils.GetDefaultClient()
  1114. header := task.GetTaskRequestHeader()
  1115. body := jsonutils.Marshal(input)
  1116. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1117. if err != nil {
  1118. return errors.Wrap(err, "host request")
  1119. }
  1120. return nil
  1121. }
  1122. func (self *SKVMGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error {
  1123. url := fmt.Sprintf("%s/servers/%s/set-nic-traffic-limit", host.ManagerUri, guest.Id)
  1124. httpClient := httputils.GetDefaultClient()
  1125. header := task.GetTaskRequestHeader()
  1126. body := jsonutils.Marshal(input)
  1127. _, _, err := httputils.JSONRequest(httpClient, ctx, "POST", url, header, body, false)
  1128. if err != nil {
  1129. return errors.Wrap(err, "host request")
  1130. }
  1131. return nil
  1132. }
  1133. func (self *SKVMGuestDriver) RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error {
  1134. header := self.getTaskRequestHeader(task)
  1135. client := httputils.GetDefaultClient()
  1136. url := fmt.Sprintf("%s/servers/%s/start-rescue", host.ManagerUri, guest.Id)
  1137. _, _, err := httputils.JSONRequest(client, ctx, "POST", url, header, body, false)
  1138. if err != nil {
  1139. return err
  1140. }
  1141. return nil
  1142. }
  1143. func (self *SKVMGuestDriver) ValidateSyncOSInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
  1144. if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  1145. return httperrors.NewBadRequestError("can't sync guest os info in status %s", guest.Status)
  1146. }
  1147. return nil
  1148. }
  1149. func (kvm *SKVMGuestDriver) ValidateGuestChangeConfigInput(ctx context.Context, guest *models.SGuest, input api.ServerChangeConfigInput) (*api.ServerChangeConfigSettings, error) {
  1150. confs, err := kvm.SBaseGuestDriver.ValidateGuestChangeConfigInput(ctx, guest, input)
  1151. if err != nil {
  1152. return nil, errors.Wrap(err, "SBaseGuestDriver.ValidateGuestChangeConfigInput")
  1153. }
  1154. if confs.ExtraCpuChanged() && guest.Status != api.VM_READY {
  1155. return nil, httperrors.NewInvalidStatusError("Can't change extra cpus on vm status %s", guest.Status)
  1156. }
  1157. var resetNics []api.ServerNicTrafficLimit
  1158. var setNics []api.ServerNicTrafficLimit
  1159. for i := range input.ResetTrafficLimits {
  1160. input, needResetTraffic, err := guest.ValidateChangeNicBillingModeInput(ctx, input.ResetTrafficLimits[i], true)
  1161. if err != nil {
  1162. return nil, errors.Wrap(err, "ValidateChangeNicBillingModeInput")
  1163. }
  1164. if needResetTraffic {
  1165. resetNics = append(resetNics, input)
  1166. } else {
  1167. setNics = append(setNics, input)
  1168. }
  1169. }
  1170. for i := range input.SetTrafficLimits {
  1171. input, needResetTraffic, err := guest.ValidateChangeNicBillingModeInput(ctx, input.ResetTrafficLimits[i], false)
  1172. if err != nil {
  1173. return nil, errors.Wrap(err, "ValidateChangeNicBillingModeInput")
  1174. }
  1175. if needResetTraffic {
  1176. resetNics = append(resetNics, input)
  1177. } else {
  1178. setNics = append(setNics, input)
  1179. }
  1180. }
  1181. if len(resetNics) > 0 {
  1182. confs.ResetTrafficLimits = resetNics
  1183. }
  1184. if len(setNics) > 0 {
  1185. confs.SetTrafficLimits = setNics
  1186. }
  1187. return confs, nil
  1188. }
  1189. func (kvm *SKVMGuestDriver) ValidateGuestHotChangeConfigInput(ctx context.Context, guest *models.SGuest, confs *api.ServerChangeConfigSettings) (*api.ServerChangeConfigSettings, error) {
  1190. if guest.GetMetadata(ctx, api.VM_METADATA_HOTPLUG_CPU_MEM, nil) != "enable" {
  1191. return confs, errors.Wrap(errors.ErrInvalidStatus, "host plug cpu memory is disabled")
  1192. }
  1193. if apis.IsARM(guest.OsArch) || apis.IsRISCV(guest.OsArch) {
  1194. return confs, errors.Wrapf(errors.ErrInvalidStatus, "cpu architecture is %s", guest.OsArch)
  1195. }
  1196. return confs, nil
  1197. }
  1198. func (kvm *SKVMGuestDriver) GetRandomNetworkTypes() []api.TNetworkType {
  1199. return []api.TNetworkType{api.NETWORK_TYPE_GUEST, api.NETWORK_TYPE_HOSTLOCAL}
  1200. }
  1201. func (kvm *SKVMGuestDriver) RequestUploadGuestStatus(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  1202. host, _ := guest.GetHost()
  1203. url := fmt.Sprintf("%s/servers/%s/upload-status", host.ManagerUri, guest.Id)
  1204. body := jsonutils.NewDict()
  1205. header := kvm.getTaskRequestHeader(task)
  1206. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  1207. return err
  1208. }