host_recycle.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  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 models
  15. import (
  16. "context"
  17. "fmt" // "strings"
  18. "time"
  19. "yunion.io/x/cloudmux/pkg/apis/compute"
  20. "yunion.io/x/cloudmux/pkg/cloudprovider"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/tristate"
  25. "yunion.io/x/pkg/util/billing"
  26. "yunion.io/x/sqlchemy"
  27. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  28. api "yunion.io/x/onecloud/pkg/apis/compute"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  32. "yunion.io/x/onecloud/pkg/compute/baremetal"
  33. "yunion.io/x/onecloud/pkg/httperrors"
  34. "yunion.io/x/onecloud/pkg/mcclient"
  35. "yunion.io/x/onecloud/pkg/util/logclient"
  36. )
  37. func (self *SHost) GetResourceType() string {
  38. if len(self.ResourceType) > 0 {
  39. return self.ResourceType
  40. }
  41. return api.HostResourceTypeDefault
  42. }
  43. func (self *SGuest) CanPerformPrepaidRecycle() error {
  44. if self.BillingType != billing_api.BILLING_TYPE_PREPAID {
  45. return fmt.Errorf("recycle prepaid server only")
  46. }
  47. if self.ExpiredAt.Before(time.Now()) {
  48. return fmt.Errorf("prepaid expired")
  49. }
  50. host, _ := self.GetHost()
  51. if host == nil {
  52. return fmt.Errorf("no host")
  53. }
  54. if !host.IsManaged() {
  55. return fmt.Errorf("only managed prepaid server can be pooled")
  56. }
  57. return nil
  58. }
  59. func (self *SGuest) PerformPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  60. if !self.IsInStatus(api.VM_READY, api.VM_RUNNING) {
  61. return nil, httperrors.NewInvalidStatusError("cannot recycle in status %s", self.Status)
  62. }
  63. err := self.CanPerformPrepaidRecycle()
  64. if err != nil {
  65. return nil, httperrors.NewInvalidStatusError("%v", err)
  66. }
  67. return self.DoPerformPrepaidRecycle(ctx, userCred, jsonutils.QueryBoolean(data, "auto_delete", false))
  68. }
  69. func (self *SGuest) DoPerformPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, autoDelete bool) (jsonutils.JSONObject, error) {
  70. err := self.doPrepaidRecycle(ctx, userCred)
  71. if err != nil {
  72. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, false)
  73. return nil, httperrors.NewGeneralError(err)
  74. }
  75. db.OpsLog.LogEvent(self, db.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred)
  76. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, true)
  77. if autoDelete {
  78. opts := api.ServerDeleteInput{
  79. OverridePendingDelete: true,
  80. }
  81. self.StartDeleteGuestTask(ctx, userCred, "", opts)
  82. }
  83. return nil, nil
  84. }
  85. func (self *SGuest) doPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential) error {
  86. lockman.LockClass(ctx, HostManager, userCred.GetProjectId())
  87. defer lockman.ReleaseClass(ctx, HostManager, userCred.GetProjectId())
  88. return self.doPrepaidRecycleNoLock(ctx, userCred)
  89. }
  90. func (self *SGuest) doPrepaidRecycleNoLock(ctx context.Context, userCred mcclient.TokenCredential) error {
  91. oHost, _ := self.GetHost()
  92. fakeHost := SHost{}
  93. fakeHost.SetModelManager(HostManager, &fakeHost)
  94. fakeHost.Name = fmt.Sprintf("%s-host", self.Name)
  95. fakeHost.CpuCount = self.VcpuCount
  96. fakeHost.NodeCount = 1
  97. fakeHost.CpuCmtbound = 1.0
  98. fakeHost.MemCmtbound = 1.0
  99. fakeHost.MemReserved = 0
  100. fakeHost.MemSize = self.VmemSize
  101. guestdisks, _ := self.GetGuestDisks()
  102. storageInfo := make([]baremetal.BaremetalStorage, 0)
  103. totalSize := int64(0)
  104. for i := 0; i < len(guestdisks); i += 1 {
  105. disk := guestdisks[i].GetDisk()
  106. storage, _ := disk.GetStorage()
  107. totalSize += int64(disk.DiskSize)
  108. if len(fakeHost.StorageType) == 0 {
  109. fakeHost.StorageType = storage.StorageType
  110. }
  111. info := baremetal.BaremetalStorage{}
  112. info.Size = int64(disk.DiskSize)
  113. info.Index = int64(i)
  114. info.Slot = i
  115. info.Driver = baremetal.DISK_DRIVER_LINUX
  116. info.Rotate = (storage.MediumType != api.DISK_TYPE_SSD)
  117. storageInfo = append(storageInfo, info)
  118. }
  119. fakeHost.StorageDriver = baremetal.DISK_DRIVER_LINUX
  120. fakeHost.StorageSize = totalSize
  121. fakeHost.StorageInfo = jsonutils.Marshal(&storageInfo)
  122. zone, _ := self.getZone()
  123. fakeHost.ZoneId = zone.GetId()
  124. fakeHost.IsBaremetal = false
  125. fakeHost.IsMaintenance = false
  126. fakeHost.ResourceType = api.HostResourceTypePrepaidRecycle
  127. guestnics, err := self.GetNetworks("")
  128. if err != nil || len(guestnics) == 0 {
  129. msg := fmt.Sprintf("no network info on guest???? %v", err)
  130. log.Errorf("%s", msg)
  131. return fmt.Errorf("%s", msg)
  132. }
  133. fakeHost.AccessIp = guestnics[0].IpAddr
  134. fakeHost.AccessMac = guestnics[0].MacAddr
  135. fakeHost.BillingType = billing_api.BILLING_TYPE_PREPAID
  136. fakeHost.BillingCycle = self.BillingCycle
  137. fakeHost.ExpiredAt = self.ExpiredAt
  138. fakeHost.Status = api.HOST_STATUS_RUNNING
  139. fakeHost.HostStatus = api.HOST_ONLINE
  140. fakeHost.SetEnabled(true)
  141. fakeHost.HostType = oHost.HostType
  142. fakeHost.ExternalId = oHost.ExternalId
  143. fakeHost.RealExternalId = self.ExternalId
  144. fakeHost.ManagerId = oHost.ManagerId
  145. fakeHost.IsEmulated = true
  146. fakeHost.Description = "fake host for prepaid vm recycling"
  147. err = HostManager.TableSpec().Insert(ctx, &fakeHost)
  148. if err != nil {
  149. log.Errorf("fail to insert fake host %s", err)
  150. return err
  151. }
  152. for i := 0; i < len(guestnics); i += 1 {
  153. var nicType compute.TNicType
  154. if i == 0 {
  155. nicType = api.NIC_TYPE_ADMIN
  156. }
  157. ifname := fmt.Sprintf("eth%d", i)
  158. brname := fmt.Sprintf("br%d", i)
  159. net, err := guestnics[i].GetNetwork()
  160. if err != nil {
  161. return errors.Wrapf(err, "GetNetwork")
  162. }
  163. err = fakeHost.addNetif(ctx, userCred,
  164. guestnics[i].MacAddr,
  165. 1,
  166. net.WireId,
  167. "",
  168. "",
  169. 1000,
  170. nicType,
  171. i,
  172. tristate.True,
  173. 1500,
  174. false,
  175. &ifname,
  176. &brname,
  177. false,
  178. false,
  179. false,
  180. false,
  181. )
  182. if err != nil {
  183. log.Errorf("fail to addNetInterface %d: %s", i, err)
  184. fakeHost.RealDelete(ctx, userCred)
  185. return err
  186. }
  187. }
  188. storageSize := int64(0)
  189. var externalId string
  190. for i := 0; i < len(guestdisks); i += 1 {
  191. disk := guestdisks[i].GetDisk()
  192. storage, _ := disk.GetStorage()
  193. if disk.BillingType == billing_api.BILLING_TYPE_PREPAID {
  194. storageSize += int64(disk.DiskSize)
  195. if len(externalId) == 0 {
  196. externalId = storage.ExternalId
  197. } else {
  198. if externalId != storage.ExternalId {
  199. msg := "inconsistent storage !!!!"
  200. log.Errorf("%s", msg)
  201. fakeHost.RealDelete(ctx, userCred)
  202. return errors.Wrap(httperrors.ErrConflict, msg)
  203. }
  204. }
  205. }
  206. }
  207. sysStorage, _ := guestdisks[0].GetDisk().GetStorage()
  208. fakeStorage := SStorage{}
  209. fakeStorage.SetModelManager(StorageManager, &fakeStorage)
  210. fakeStorage.Name = fmt.Sprintf("%s-storage", self.Name)
  211. fakeStorage.Capacity = storageSize
  212. fakeStorage.StorageType = api.STORAGE_LOCAL
  213. fakeStorage.MediumType = sysStorage.MediumType
  214. fakeStorage.Cmtbound = 1.0
  215. fakeStorage.ZoneId = fakeHost.ZoneId
  216. fakeStorage.StoragecacheId = sysStorage.StoragecacheId
  217. fakeStorage.Enabled = tristate.True
  218. fakeStorage.Status = api.STORAGE_ONLINE
  219. fakeStorage.Description = "fake storage for prepaid vm recycling"
  220. fakeStorage.IsEmulated = true
  221. fakeStorage.ManagerId = sysStorage.ManagerId
  222. fakeStorage.ExternalId = externalId
  223. err = StorageManager.TableSpec().Insert(ctx, &fakeStorage)
  224. if err != nil {
  225. log.Errorf("fail to insert fake storage %s", err)
  226. fakeHost.RealDelete(ctx, userCred)
  227. return err
  228. }
  229. err = fakeHost.Attach2Storage(ctx, userCred, &fakeStorage, "")
  230. if err != nil {
  231. log.Errorf("fail to add fake storage: %s", err)
  232. fakeHost.RealDelete(ctx, userCred)
  233. return err
  234. }
  235. _, err = db.Update(self, func() error {
  236. // clear billing information
  237. self.BillingType = billing_api.BILLING_TYPE_POSTPAID
  238. self.BillingCycle = ""
  239. self.ExpiredAt = time.Time{}
  240. // switch to fakeHost
  241. self.HostId = fakeHost.Id
  242. return nil
  243. })
  244. if err != nil {
  245. log.Errorf("clear billing information fail: %s", err)
  246. fakeHost.RealDelete(ctx, userCred)
  247. return err
  248. }
  249. for i := 0; i < len(guestdisks); i += 1 {
  250. disk := guestdisks[i].GetDisk()
  251. if disk.BillingType == billing_api.BILLING_TYPE_PREPAID {
  252. _, err = db.Update(disk, func() error {
  253. disk.BillingType = billing_api.BILLING_TYPE_POSTPAID
  254. disk.BillingCycle = ""
  255. disk.ExpiredAt = time.Time{}
  256. disk.StorageId = fakeStorage.Id
  257. return nil
  258. })
  259. if err != nil {
  260. log.Errorf("clear billing information for %d %s disk fail: %s", i, disk.DiskType, err)
  261. fakeHost.RealDelete(ctx, userCred)
  262. return err
  263. }
  264. }
  265. }
  266. return nil
  267. }
  268. func (self *SGuest) PerformUndoPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  269. if !self.IsInStatus(api.VM_READY, api.VM_RUNNING) {
  270. return nil, httperrors.NewInvalidStatusError("cannot undo recycle in status %s", self.Status)
  271. }
  272. host, _ := self.GetHost()
  273. if host == nil {
  274. return nil, httperrors.NewInvalidStatusError("no valid host")
  275. }
  276. if host.GetEnabled() {
  277. return nil, httperrors.NewInvalidStatusError("host should be disabled")
  278. }
  279. if host.ResourceType != api.HostResourceTypePrepaidRecycle || host.BillingType != billing_api.BILLING_TYPE_PREPAID {
  280. return nil, httperrors.NewInvalidStatusError("host is not a prepaid recycle host")
  281. }
  282. err := doUndoPrepaidRecycleLockHost(ctx, userCred, host, self)
  283. if err != nil {
  284. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, false)
  285. return nil, httperrors.NewGeneralError(err)
  286. }
  287. db.OpsLog.LogEvent(self, db.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred)
  288. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, true)
  289. return nil, nil
  290. }
  291. func (self *SHost) PerformUndoPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  292. if self.GetEnabled() {
  293. return nil, httperrors.NewInvalidStatusError("host should be disabled")
  294. }
  295. if self.ResourceType != api.HostResourceTypePrepaidRecycle || self.BillingType != billing_api.BILLING_TYPE_PREPAID {
  296. return nil, httperrors.NewInvalidStatusError("host is not a prepaid recycle host")
  297. }
  298. guests, err := self.GetGuests()
  299. if err != nil {
  300. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetGuests"))
  301. }
  302. if len(guests) == 0 {
  303. return nil, httperrors.NewInvalidStatusError("cannot delete a recycle host without active instance")
  304. }
  305. if len(guests) > 1 {
  306. return nil, httperrors.NewInvalidStatusError("a recycle host shoud not allocate more than 1 guest")
  307. }
  308. if !guests[0].IsInStatus(api.VM_READY, api.VM_RUNNING) {
  309. return nil, httperrors.NewInvalidStatusError("cannot undo recycle in status %s", guests[0].Status)
  310. }
  311. if guests[0].PendingDeleted {
  312. return nil, httperrors.NewInvalidStatusError("cannot undo a recycle host with pending_deleted guest")
  313. }
  314. err = doUndoPrepaidRecycleLockGuest(ctx, userCred, self, &guests[0])
  315. if err != nil {
  316. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, false)
  317. return nil, httperrors.NewGeneralError(err)
  318. }
  319. db.OpsLog.LogEvent(self, db.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred)
  320. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UNDO_RECYCLE_PREPAID, self.GetShortDesc(ctx), userCred, true)
  321. return nil, nil
  322. }
  323. func findIdiskById(idisks []cloudprovider.ICloudDisk, uuid string) cloudprovider.ICloudDisk {
  324. for i := 0; i < len(idisks); i += 1 {
  325. if idisks[i].GetGlobalId() == uuid {
  326. return idisks[i]
  327. }
  328. }
  329. return nil
  330. }
  331. func doUndoPrepaidRecycleLockGuest(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, server *SGuest) error {
  332. lockman.LockObject(ctx, server)
  333. defer lockman.ReleaseObject(ctx, server)
  334. return doUndoPrepaidRecycleNoLock(ctx, userCred, host, server)
  335. }
  336. func doUndoPrepaidRecycleLockHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, server *SGuest) error {
  337. lockman.LockObject(ctx, host)
  338. defer lockman.ReleaseObject(ctx, host)
  339. return doUndoPrepaidRecycleNoLock(ctx, userCred, host, server)
  340. }
  341. func doUndoPrepaidRecycleNoLock(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, server *SGuest) error {
  342. if host.RealExternalId != server.ExternalId {
  343. msg := "host and server external id not match!!!!"
  344. log.Errorf("%v", msg)
  345. return errors.Wrap(httperrors.ErrConflict, msg)
  346. }
  347. q := HostManager.Query()
  348. q = q.Equals("external_id", host.ExternalId)
  349. q = q.Equals("host_type", host.HostType)
  350. q = q.Filter(sqlchemy.OR(
  351. sqlchemy.IsNullOrEmpty(q.Field("resource_type")),
  352. sqlchemy.Equals(q.Field("resource_type"), api.HostResourceTypeShared),
  353. ))
  354. oHostCnt, err := q.CountWithError()
  355. if err != nil {
  356. return err
  357. }
  358. if oHostCnt == 0 {
  359. msg := "orthordox host not found???"
  360. log.Errorf("%s", msg)
  361. return errors.Wrap(httperrors.ErrConflict, msg)
  362. }
  363. if oHostCnt > 1 {
  364. msg := fmt.Sprintf("more than 1 (%d) orthordox host found???", oHostCnt)
  365. log.Errorf("%s", msg)
  366. return errors.Wrap(httperrors.ErrConflict, msg)
  367. }
  368. oHost := SHost{}
  369. oHost.SetModelManager(HostManager, &oHost)
  370. err = q.First(&oHost)
  371. if err != nil {
  372. msg := fmt.Sprintf("fail to query orthordox host %v", err)
  373. log.Errorf("%s", msg)
  374. return errors.Wrap(err, msg)
  375. }
  376. guestdisks, _ := server.GetGuestDisks()
  377. // check disk data integrity
  378. for i := 0; i < len(guestdisks); i += 1 {
  379. disk := guestdisks[i].GetDisk()
  380. storage, _ := disk.GetStorage()
  381. if storage.StorageType == api.STORAGE_LOCAL {
  382. oHostStorage := oHost.GetHoststorageByExternalId(storage.ExternalId)
  383. if oHostStorage == nil {
  384. msg := fmt.Sprintf("oHost.GetHoststorageByExternalId not found %s", storage.ExternalId)
  385. log.Errorf("%s", msg)
  386. return errors.Wrap(httperrors.ErrConflict, msg)
  387. }
  388. }
  389. }
  390. // check passed, do convert
  391. _, err = db.Update(server, func() error {
  392. // recover billing information
  393. server.BillingType = billing_api.BILLING_TYPE_PREPAID
  394. server.BillingCycle = host.BillingCycle
  395. server.ExpiredAt = host.ExpiredAt
  396. // switch to original Host
  397. server.HostId = oHost.Id
  398. return nil
  399. })
  400. if err != nil {
  401. log.Errorf("fail to recover vm hostId %s", err)
  402. return errors.Wrap(err, "Update")
  403. }
  404. for i := 0; i < len(guestdisks); i += 1 {
  405. disk := guestdisks[i].GetDisk()
  406. storage, _ := disk.GetStorage()
  407. if storage.StorageType == api.STORAGE_LOCAL {
  408. oHostStorage := oHost.GetHoststorageByExternalId(storage.ExternalId)
  409. if oHostStorage == nil {
  410. msg := fmt.Sprintf("oHost.GetHoststorageByExternalId not found %s", storage.ExternalId)
  411. log.Errorf("%s", msg)
  412. return errors.Wrap(httperrors.ErrConflict, msg)
  413. }
  414. oStorage := oHostStorage.GetStorage()
  415. _, err = db.Update(disk, func() error {
  416. disk.BillingType = billing_api.BILLING_TYPE_PREPAID
  417. disk.BillingCycle = host.BillingCycle
  418. disk.ExpiredAt = host.ExpiredAt
  419. disk.StorageId = oStorage.Id
  420. disk.AutoDelete = true
  421. return nil
  422. })
  423. if err != nil {
  424. log.Errorf("fail to recover prepaid disk info %s", err)
  425. return err
  426. }
  427. }
  428. }
  429. err = host.RealDelete(ctx, userCred)
  430. if err != nil {
  431. log.Errorf("fail to delete fake host")
  432. logclient.AddActionLogWithContext(ctx, server, logclient.ACT_UNDO_RECYCLE_PREPAID, err, userCred, false)
  433. return err
  434. }
  435. return nil
  436. }
  437. func (self *SGuest) IsPrepaidRecycle() bool {
  438. host, _ := self.GetHost()
  439. if host == nil {
  440. return false
  441. }
  442. return host.IsPrepaidRecycle()
  443. }
  444. func (host *SHost) IsPrepaidRecycle() bool {
  445. if host.ResourceType != api.HostResourceTypePrepaidRecycle {
  446. return false
  447. }
  448. if host.BillingType != billing_api.BILLING_TYPE_PREPAID {
  449. return false
  450. }
  451. return true
  452. }
  453. func (self *SHost) BorrowIpAddrsFromGuest(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
  454. guestnics, err := guest.GetNetworks("")
  455. if err != nil {
  456. return err
  457. }
  458. for i := 0; i < len(guestnics); i += 1 {
  459. err := guestnics[i].Detach(ctx, userCred)
  460. if err != nil {
  461. log.Errorf("fail to detach guest network %s", err)
  462. return err
  463. }
  464. netif := self.GetNetInterface(guestnics[i].MacAddr, 1)
  465. if netif == nil {
  466. msg := fmt.Sprintf("fail to find netinterface for mac %s", guestnics[i].MacAddr)
  467. log.Errorf("%s", msg)
  468. return fmt.Errorf("%s", msg)
  469. }
  470. err = self.EnableNetif(ctx, userCred, netif, "", guestnics[i].IpAddr, guestnics[i].Ip6Addr, "", "", false, false, false, false)
  471. if err != nil {
  472. log.Errorf("fail to enable netif %s %s", guestnics[i].IpAddr, err)
  473. return err
  474. }
  475. }
  476. return nil
  477. }
  478. func (host *SHost) SetGuestCreateNetworkAndDiskParams(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  479. ihost, err := host.GetIHost(ctx)
  480. if err != nil {
  481. return nil, errors.Wrapf(err, "GetIHost")
  482. }
  483. ivm, err := ihost.GetIVMById(host.RealExternalId)
  484. if err != nil {
  485. return nil, errors.Wrapf(err, "GetIVMById(%s)", host.RealExternalId)
  486. }
  487. idisks, err := ivm.GetIDisks()
  488. if err != nil {
  489. return nil, errors.Wrapf(err, "ivm.GetIDisks")
  490. }
  491. netifs := host.GetHostNetInterfaces()
  492. netIdx := 0
  493. input.Networks = make([]*api.NetworkConfig, 0)
  494. for i := 0; i < len(netifs); i += 1 {
  495. hn := netifs[i].GetHostNetwork()
  496. if hn != nil {
  497. err := host.DisableNetif(ctx, userCred, &netifs[i], true)
  498. if err != nil {
  499. return nil, err
  500. }
  501. // packedMac := strings.Replace(netifs[i].Mac, ":", "", -1)
  502. input.Networks = append(input.Networks, &api.NetworkConfig{
  503. Network: hn.NetworkId,
  504. Mac: netifs[i].Mac,
  505. Address: hn.IpAddr,
  506. Address6: hn.Ip6Addr,
  507. Reserved: true,
  508. })
  509. netIdx += 1
  510. }
  511. }
  512. //params.Set(fmt.Sprintf("net.%d", netIdx), jsonutils.JSONNull)
  513. for i := 0; i < len(idisks); i += 1 {
  514. /*istorage, err := idisks[i].GetIStorage()
  515. if err != nil {
  516. log.Errorf("idisks[i].GetIStorage fail %s", err)
  517. return nil, err
  518. }*/
  519. var diskConfig *api.DiskConfig
  520. if i < len(input.Disks) {
  521. diskConfig = input.Disks[i]
  522. diskConfig, err := parseDiskInfo(ctx, userCred, diskConfig)
  523. if err != nil {
  524. log.Debugf("parseDiskInfo %#v fail %s", diskConfig, err)
  525. return nil, err
  526. }
  527. diskConfig.SizeMb = idisks[i].GetDiskSizeMB()
  528. diskConfig.Backend = api.STORAGE_LOCAL
  529. input.Disks[i] = diskConfig
  530. } else {
  531. conf := &api.DiskConfig{
  532. SizeMb: idisks[i].GetDiskSizeMB(),
  533. Backend: api.STORAGE_LOCAL,
  534. }
  535. conf, err = parseDiskInfo(ctx, userCred, conf)
  536. if err != nil {
  537. return nil, err
  538. }
  539. input.Disks = append(input.Disks, conf)
  540. }
  541. }
  542. //params.Set(fmt.Sprintf("disk.%d", len(idisks)), jsonutils.JSONNull)
  543. // log.Debugf("params after rebuid: %s", params.String())
  544. return input, nil
  545. }
  546. func (host *SHost) RebuildRecycledGuest(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
  547. oHost := SHost{}
  548. oHost.SetModelManager(HostManager, &oHost)
  549. q := HostManager.Query()
  550. q = q.Equals("external_id", host.ExternalId)
  551. q = q.Filter(sqlchemy.OR(
  552. sqlchemy.IsNullOrEmpty(q.Field("resource_type")),
  553. sqlchemy.Equals(q.Field("resource_type"), api.HostResourceTypeShared),
  554. ))
  555. err := q.First(&oHost)
  556. if err != nil {
  557. log.Errorf("query oHost fail %s", err)
  558. return err
  559. }
  560. err = db.SetExternalId(guest, userCred, host.RealExternalId)
  561. if err != nil {
  562. log.Errorf("guest.SetExternalId fail %s", err)
  563. return err
  564. }
  565. extVM, err := guest.GetIVM(ctx)
  566. if err != nil {
  567. log.Errorf("guest.GetIVM fail %s", err)
  568. return err
  569. }
  570. iprovider, err := oHost.GetDriver(ctx)
  571. if err != nil {
  572. log.Errorf("oHost.GetDriver fail %s", err)
  573. return err
  574. }
  575. err = guest.syncWithCloudVM(ctx, userCred, iprovider, &oHost, extVM, nil, true)
  576. if err != nil {
  577. log.Errorf("guest.syncWithCloudVM fail %s", err)
  578. return err
  579. }
  580. idisks, err := extVM.GetIDisks()
  581. if err != nil {
  582. log.Errorf("extVM.GetIDisks fail %s", err)
  583. return err
  584. }
  585. guestdisks, _ := guest.GetGuestDisks()
  586. for i := 0; i < len(guestdisks); i += 1 {
  587. disk := guestdisks[i].GetDisk()
  588. err = db.SetExternalId(disk, userCred, idisks[i].GetGlobalId())
  589. if err != nil {
  590. log.Errorf("disk.SetExternalId fail %s", err)
  591. return err
  592. }
  593. err = disk.syncWithCloudDisk(ctx, userCred, iprovider, idisks[i], i, guest.GetOwnerId(), host.ManagerId)
  594. if err != nil {
  595. log.Errorf("disk.syncWithCloudDisk fail %s", err)
  596. return err
  597. }
  598. }
  599. return nil
  600. }
  601. func (manager *SHostManager) GetHostByRealExternalId(eid string) *SHost {
  602. q := manager.Query()
  603. q = q.Equals("real_external_id", eid)
  604. host := SHost{}
  605. host.SetModelManager(manager, &host)
  606. err := q.First(&host)
  607. if err != nil {
  608. return nil
  609. }
  610. return &host
  611. }
  612. func (self *SHost) PerformRenewPrepaidRecycle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  613. if !self.IsPrepaidRecycle() {
  614. return nil, httperrors.NewInputParameterError("Not a prepaid recycle host")
  615. }
  616. if len(self.RealExternalId) == 0 {
  617. return nil, httperrors.NewGeneralError(fmt.Errorf("host RealExternalId is empty"))
  618. }
  619. if len(self.ExternalId) == 0 {
  620. return nil, httperrors.NewGeneralError(fmt.Errorf("host ExternalId is empty"))
  621. }
  622. durationStr := jsonutils.GetAnyString(data, []string{"duration"})
  623. if len(durationStr) == 0 {
  624. return nil, httperrors.NewMissingParameterError("duration")
  625. }
  626. bc, err := billing.ParseBillingCycle(durationStr)
  627. if err != nil {
  628. return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err)
  629. }
  630. hostDriver, err := self.GetHostDriver()
  631. if err != nil {
  632. return nil, errors.Wrapf(err, "GetHostDriver")
  633. }
  634. driver, err := GetDriver(hostDriver.GetHypervisor(), hostDriver.GetProvider())
  635. if err != nil {
  636. return nil, err
  637. }
  638. if !driver.IsSupportedBillingCycle(bc) {
  639. return nil, httperrors.NewInputParameterError("unsupported duration %s", durationStr)
  640. }
  641. err = self.startPrepaidRecycleHostRenewTask(ctx, userCred, durationStr, "")
  642. if err != nil {
  643. return nil, err
  644. }
  645. return nil, nil
  646. }
  647. func (self *SHost) startPrepaidRecycleHostRenewTask(ctx context.Context, userCred mcclient.TokenCredential, duration string, parentTaskId string) error {
  648. data := jsonutils.NewDict()
  649. data.Add(jsonutils.NewString(duration), "duration")
  650. task, err := taskman.TaskManager.NewTask(ctx, "PrepaidRecycleHostRenewTask", self, userCred, data, parentTaskId, "", nil)
  651. if err != nil {
  652. log.Errorf("fail to crate GuestRenewTask %s", err)
  653. return err
  654. }
  655. task.ScheduleRun(nil)
  656. return nil
  657. }
  658. func (self *SHost) DoSaveRenewInfo(ctx context.Context, userCred mcclient.TokenCredential, bc *billing.SBillingCycle, expireAt *time.Time) error {
  659. _, err := db.Update(self, func() error {
  660. if self.BillingType != billing_api.BILLING_TYPE_PREPAID {
  661. self.BillingType = billing_api.BILLING_TYPE_PREPAID
  662. }
  663. if expireAt != nil && !expireAt.IsZero() {
  664. self.ExpiredAt = *expireAt
  665. } else {
  666. self.BillingCycle = bc.String()
  667. self.ExpiredAt = bc.EndAt(self.ExpiredAt)
  668. }
  669. return nil
  670. })
  671. if err != nil {
  672. log.Errorf("Update error %s", err)
  673. return err
  674. }
  675. db.OpsLog.LogEvent(self, db.ACT_RENEW, self.GetShortDesc(ctx), userCred)
  676. return nil
  677. }
  678. func (self *SHost) SyncWithRealPrepaidVM(ctx context.Context, userCred mcclient.TokenCredential, iVM cloudprovider.ICloudVM) error {
  679. lockman.LockObject(ctx, self)
  680. defer lockman.ReleaseObject(ctx, self)
  681. exp := iVM.GetExpiredAt()
  682. if self.ExpiredAt != exp {
  683. return self.DoSaveRenewInfo(ctx, userCred, nil, &exp)
  684. }
  685. return nil
  686. }