disks.go 115 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558
  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. "database/sql"
  18. "fmt"
  19. "path"
  20. "path/filepath"
  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/gotypes"
  28. "yunion.io/x/pkg/tristate"
  29. "yunion.io/x/pkg/util/compare"
  30. "yunion.io/x/pkg/util/fileutils"
  31. "yunion.io/x/pkg/util/pinyinutils"
  32. "yunion.io/x/pkg/util/rbacscope"
  33. "yunion.io/x/pkg/utils"
  34. "yunion.io/x/sqlchemy"
  35. "yunion.io/x/onecloud/pkg/apis"
  36. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  37. api "yunion.io/x/onecloud/pkg/apis/compute"
  38. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  39. schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
  40. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  41. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  42. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  43. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  44. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  45. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  46. "yunion.io/x/onecloud/pkg/compute/options"
  47. "yunion.io/x/onecloud/pkg/httperrors"
  48. "yunion.io/x/onecloud/pkg/mcclient"
  49. "yunion.io/x/onecloud/pkg/mcclient/auth"
  50. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  51. "yunion.io/x/onecloud/pkg/util/logclient"
  52. "yunion.io/x/onecloud/pkg/util/stringutils2"
  53. )
  54. type SDiskManager struct {
  55. db.SVirtualResourceBaseManager
  56. db.SExternalizedResourceBaseManager
  57. SStorageResourceBaseManager
  58. SBillingResourceBaseManager
  59. db.SMultiArchResourceBaseManager
  60. db.SAutoDeleteResourceBaseManager
  61. db.SEncryptedResourceManager
  62. }
  63. var DiskManager *SDiskManager
  64. func init() {
  65. DiskManager = &SDiskManager{
  66. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  67. SDisk{},
  68. "disks_tbl",
  69. "disk",
  70. "disks",
  71. ),
  72. }
  73. DiskManager.SetVirtualObject(DiskManager)
  74. DiskManager.TableSpec().AddIndex(false, "deleted", "disk_size", "status", "storage_id")
  75. }
  76. type SDisk struct {
  77. db.SVirtualResourceBase
  78. db.SExternalizedResourceBase
  79. SBillingResourceBase
  80. SStorageResourceBase `width:"128" charset:"ascii" nullable:"true" list:"admin" create:"optional"`
  81. db.SMultiArchResourceBase
  82. db.SAutoDeleteResourceBase
  83. db.SEncryptedResource
  84. // 磁盘存储类型
  85. // example: qcow2
  86. DiskFormat string `width:"32" charset:"ascii" nullable:"false" default:"qcow2" list:"user" json:"disk_format"`
  87. // 磁盘大小, 单位Mb
  88. // example: 10240
  89. DiskSize int `nullable:"false" list:"user" json:"disk_size"`
  90. // 磁盘路径
  91. AccessPath string `width:"256" charset:"utf8" nullable:"true" get:"user" json:"access_path"`
  92. PCIPath string `width:"256" charset:"utf8" nullable:"true" get:"user" json:"pci_path"`
  93. // 存储Id
  94. // StorageId string `width:"128" charset:"ascii" nullable:"true" list:"admin" create:"optional"`
  95. // 备份磁盘实例的存储ID
  96. BackupStorageId string `width:"128" charset:"ascii" nullable:"true" list:"admin" json:"backup_storage_id"`
  97. // 镜像Id
  98. TemplateId string `width:"256" charset:"ascii" nullable:"true" list:"user" json:"template_id"`
  99. // 快照Id
  100. SnapshotId string `width:"256" charset:"ascii" nullable:"true" list:"user" json:"snapshot_id"`
  101. // 备份Id
  102. BackupId string `width:"256" charset:"ascii" nullable:"true" list:"user" json:"backup_id"`
  103. // 设备名称
  104. Device string `width:"32" charset:"ascii" nullable:"true" get:"user" list:"user"`
  105. // 文件系统
  106. FsFormat string `width:"32" charset:"ascii" nullable:"true" list:"user" json:"fs_format"`
  107. // 文件系统特性
  108. FsFeatures *api.DiskFsFeatures `length:"medium" nullable:"true" list:"user" json:"fs_features"`
  109. // 磁盘类型
  110. // sys: 系统盘
  111. // data: 数据盘
  112. // swap: 交换盘
  113. // example: sys
  114. DiskType string `width:"32" charset:"ascii" nullable:"true" list:"user" update:"admin" json:"disk_type"`
  115. // 预分配策略
  116. // off: 关闭预分配,默认关闭
  117. // metadata: 精简制备
  118. // falloc: 厚制制备延迟置零
  119. // full: 厚制备快速置零
  120. Preallocation string `width:"12" default:"off" charset:"ascii" nullable:"true" list:"user" update:"admin" json:"preallocation"`
  121. // # is persistent
  122. Nonpersistent bool `default:"false" list:"user" json:"nonpersistent"`
  123. // auto reset disk after guest shutdown
  124. AutoReset bool `default:"false" list:"user" update:"user" json:"auto_reset"`
  125. // 是否标记为SSD磁盘
  126. IsSsd bool `nullable:"false" default:"false" list:"user" update:"user" create:"optional"`
  127. // 最大连接数
  128. Iops int `nullable:"true" list:"user" create:"optional"`
  129. // 磁盘吞吐量
  130. Throughput int `nullable:"true" list:"user" create:"optional"`
  131. }
  132. func (manager *SDiskManager) GetContextManagers() [][]db.IModelManager {
  133. return [][]db.IModelManager{
  134. {StorageManager},
  135. }
  136. }
  137. func (manager *SDiskManager) FetchDiskById(diskId string) *SDisk {
  138. disk, err := manager.FetchById(diskId)
  139. if err != nil {
  140. log.Errorf("FetchById fail %s", err)
  141. return nil
  142. }
  143. return disk.(*SDisk)
  144. }
  145. // 磁盘列表
  146. func (manager *SDiskManager) ListItemFilter(
  147. ctx context.Context,
  148. q *sqlchemy.SQuery,
  149. userCred mcclient.TokenCredential,
  150. query api.DiskListInput,
  151. ) (*sqlchemy.SQuery, error) {
  152. var err error
  153. q, err = manager.SStorageResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StorageFilterListInput)
  154. if err != nil {
  155. return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemFilter")
  156. }
  157. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  158. if err != nil {
  159. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  160. }
  161. q, err = manager.SBillingResourceBaseManager.ListItemFilter(ctx, q, userCred, query.BillingResourceListInput)
  162. if err != nil {
  163. return nil, errors.Wrap(err, "SBillingResourceBaseManager.ListItemFilter")
  164. }
  165. q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  166. if err != nil {
  167. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  168. }
  169. q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput)
  170. if err != nil {
  171. return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
  172. }
  173. q, err = manager.SAutoDeleteResourceBaseManager.ListItemFilter(ctx, q, userCred, query.AutoDeleteResourceBaseListInput)
  174. if err != nil {
  175. return nil, errors.Wrapf(err, "SAutoDeleteResourceBaseManager.ListItemFilter")
  176. }
  177. if query.Unused != nil {
  178. guestdisks := GuestdiskManager.Query().SubQuery()
  179. sq := guestdisks.Query(guestdisks.Field("disk_id"))
  180. if *query.Unused {
  181. q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
  182. } else {
  183. q = q.Filter(sqlchemy.In(q.Field("id"), sq))
  184. }
  185. }
  186. if query.BindingSnapshotpolicy != nil {
  187. spjsq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
  188. if *query.BindingSnapshotpolicy {
  189. q = q.In("id", spjsq)
  190. } else {
  191. q = q.NotIn("id", spjsq)
  192. }
  193. }
  194. if query.BindingServerSnapshotpolicy != nil {
  195. guestDisks := GuestdiskManager.Query("disk_id")
  196. sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
  197. gdsq := guestDisks.Join(sq, sqlchemy.Equals(guestDisks.Field("guest_id"), sq.Field("resource_id"))).SubQuery()
  198. if *query.BindingServerSnapshotpolicy {
  199. q = q.In("id", gdsq)
  200. } else {
  201. q = q.NotIn("id", gdsq)
  202. }
  203. }
  204. guestId := query.ServerId
  205. if len(guestId) > 0 {
  206. guests := GuestManager.Query("id")
  207. sq := guests.Filter(
  208. sqlchemy.OR(
  209. sqlchemy.In(guests.Field("id"), guestId),
  210. sqlchemy.In(guests.Field("name"), guestId),
  211. ),
  212. ).SubQuery()
  213. guestDisks := GuestdiskManager.Query().SubQuery()
  214. q = q.Join(guestDisks, sqlchemy.AND(
  215. sqlchemy.Equals(guestDisks.Field("disk_id"), q.Field("id")),
  216. sqlchemy.In(guestDisks.Field("guest_id"), sq),
  217. )).Asc(guestDisks.Field("index"))
  218. }
  219. if diskType := query.DiskType; diskType != "" {
  220. q = q.Filter(sqlchemy.Equals(q.Field("disk_type"), diskType))
  221. }
  222. if len(query.SnapshotpolicyId) > 0 {
  223. _, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &query.SnapshotpolicyId)
  224. if err != nil {
  225. return nil, err
  226. }
  227. sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).Equals("snapshotpolicy_id", query.SnapshotpolicyId).SubQuery()
  228. q = q.In("id", sq)
  229. }
  230. if len(query.DiskFormat) > 0 {
  231. q = q.Equals("disk_format", query.DiskFormat)
  232. }
  233. if query.DiskSize > 0 {
  234. q = q.Equals("disk_size", query.DiskSize)
  235. }
  236. if len(query.FsFormat) > 0 {
  237. q = q.Equals("fs_format", query.FsFormat)
  238. }
  239. if len(query.ImageId) > 0 {
  240. img, err := CachedimageManager.getImageInfo(ctx, userCred, query.ImageId, false)
  241. if err != nil {
  242. return nil, errors.Wrap(err, "CachedimageManager.getImageInfo")
  243. }
  244. q = q.Equals("template_id", img.Id)
  245. }
  246. if len(query.SnapshotId) > 0 {
  247. _, err := validators.ValidateModel(ctx, userCred, SnapshotManager, &query.SnapshotId)
  248. if err != nil {
  249. return nil, err
  250. }
  251. q = q.Equals("snapshot_id", query.SnapshotId)
  252. }
  253. if len(query.GuestStatus) > 0 {
  254. guests := GuestManager.Query("id").Equals("status", query.GuestStatus).SubQuery()
  255. sq := GuestdiskManager.Query().In("guest_id", guests).SubQuery()
  256. q = q.Join(sq, sqlchemy.Equals(sq.Field("disk_id"), q.Field("id")))
  257. }
  258. return q, nil
  259. }
  260. func (manager *SDiskManager) OrderByExtraFields(
  261. ctx context.Context,
  262. q *sqlchemy.SQuery,
  263. userCred mcclient.TokenCredential,
  264. query api.DiskListInput,
  265. ) (*sqlchemy.SQuery, error) {
  266. var err error
  267. q, err = manager.SStorageResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StorageFilterListInput)
  268. if err != nil {
  269. return nil, errors.Wrap(err, "SStorageResourceBaseManager.OrderByExtraFields")
  270. }
  271. q, err = manager.SBillingResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.BillingResourceListInput)
  272. if err != nil {
  273. return nil, errors.Wrap(err, "SBillingResourceBaseManager.OrderByExtraFields")
  274. }
  275. if db.NeedOrderQuery([]string{query.OrderByServer}) {
  276. guestDiskQuery := GuestdiskManager.Query("disk_id", "guest_id").SubQuery()
  277. q = q.LeftJoin(guestDiskQuery, sqlchemy.Equals(q.Field("id"), guestDiskQuery.Field("disk_id")))
  278. guestQuery := GuestManager.Query().SubQuery()
  279. q.AppendField(q.QueryFields()...)
  280. q.AppendField(guestQuery.Field("name", "guest_name"))
  281. q.Join(guestQuery, sqlchemy.Equals(guestQuery.Field("id"), guestDiskQuery.Field("guest_id")))
  282. db.OrderByFields(q, []string{query.OrderByServer}, []sqlchemy.IQueryField{guestQuery.Field("name")})
  283. }
  284. if db.NeedOrderQuery([]string{query.OrderByGuestCount}) {
  285. guestdisks := GuestdiskManager.Query().SubQuery()
  286. disks := DiskManager.Query().SubQuery()
  287. guestdiskQ := guestdisks.Query(
  288. guestdisks.Field("guest_id"),
  289. guestdisks.Field("disk_id"),
  290. sqlchemy.COUNT("guest_count", guestdisks.Field("guest_id")),
  291. )
  292. guestdiskQ = guestdiskQ.LeftJoin(disks, sqlchemy.Equals(guestdiskQ.Field("disk_id"), disks.Field("id")))
  293. guestdiskSQ := guestdiskQ.GroupBy(guestdiskQ.Field("disk_id")).SubQuery()
  294. q.AppendField(q.QueryFields()...)
  295. q.AppendField(guestdiskSQ.Field("guest_count"))
  296. q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(q.Field("id"), guestdiskSQ.Field("disk_id")))
  297. db.OrderByFields(q, []string{query.OrderByGuestCount}, []sqlchemy.IQueryField{guestdiskQ.Field("guest_count")})
  298. }
  299. q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
  300. if err != nil {
  301. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
  302. }
  303. return q, nil
  304. }
  305. func (manager *SDiskManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  306. var err error
  307. q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  308. if err == nil {
  309. return q, nil
  310. }
  311. if field == "guest_status" {
  312. guestDiskQuery := GuestdiskManager.Query("disk_id", "guest_id").SubQuery()
  313. q = q.LeftJoin(guestDiskQuery, sqlchemy.Equals(q.Field("id"), guestDiskQuery.Field("disk_id")))
  314. guestQuery := GuestManager.Query().SubQuery()
  315. q.AppendField(guestQuery.Field("status", field)).Distinct()
  316. q.Join(guestQuery, sqlchemy.Equals(guestQuery.Field("id"), guestDiskQuery.Field("guest_id")))
  317. return q, nil
  318. }
  319. if field == "server" {
  320. guestDiskQuery := GuestdiskManager.Query("disk_id", "guest_id").SubQuery()
  321. q = q.LeftJoin(guestDiskQuery, sqlchemy.Equals(q.Field("id"), guestDiskQuery.Field("disk_id")))
  322. guestQuery := GuestManager.Query().SubQuery()
  323. q.AppendField(guestQuery.Field("name", field)).Distinct()
  324. q.Join(guestQuery, sqlchemy.Equals(guestQuery.Field("id"), guestDiskQuery.Field("guest_id")))
  325. return q, nil
  326. }
  327. q, err = manager.SStorageResourceBaseManager.QueryDistinctExtraField(q, field)
  328. if err == nil {
  329. return q, nil
  330. }
  331. return q, httperrors.ErrNotFound
  332. }
  333. func (manager *SDiskManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
  334. switch resource {
  335. case GuestManager.Keyword():
  336. guestdisks := GuestdiskManager.Query("disk_id", "guest_id").SubQuery()
  337. q = q.LeftJoin(guestdisks, sqlchemy.Equals(q.Field("id"), guestdisks.Field("disk_id")))
  338. guestQuery := GuestManager.Query().SubQuery()
  339. for _, field := range fields {
  340. q = q.AppendField(guestQuery.Field(field))
  341. }
  342. q = q.Join(guestQuery, sqlchemy.Equals(q.Field("guest_id"), guestQuery.Field("id")))
  343. return q, nil
  344. }
  345. return q, httperrors.ErrNotFound
  346. }
  347. func (disk *SDisk) GetGuestDiskQuery() *sqlchemy.SQuery {
  348. guestdisks := GuestdiskManager.Query()
  349. guests := GuestManager.Query().SubQuery()
  350. guestdisks = guestdisks.Join(guests, sqlchemy.Equals(guestdisks.Field("guest_id"), guests.Field("id")))
  351. return guestdisks.Equals("disk_id", disk.Id)
  352. }
  353. func (self *SDisk) GetGuestDiskCount() (int, error) {
  354. return self.GetGuestDiskQuery().CountWithError()
  355. }
  356. func (disk *SDisk) GetGuestDisk() (*SGuestdisk, error) {
  357. guestdisk := &SGuestdisk{}
  358. err := disk.GetGuestDiskQuery().First(guestdisk)
  359. if err != nil {
  360. return nil, errors.Wrap(err, "First")
  361. }
  362. return guestdisk, nil
  363. }
  364. func (self *SDisk) isAttached() (bool, error) {
  365. cnt, err := self.GetGuestDiskCount()
  366. if err != nil {
  367. return false, err
  368. }
  369. return cnt > 0, nil
  370. }
  371. func (self *SDisk) GetGuestdisks() []SGuestdisk {
  372. guestdisks := make([]SGuestdisk, 0)
  373. q := GuestdiskManager.Query().Equals("disk_id", self.Id)
  374. err := db.FetchModelObjects(GuestdiskManager, q, &guestdisks)
  375. if err != nil {
  376. log.Errorf("%s", err)
  377. return nil
  378. }
  379. return guestdisks
  380. }
  381. func (self *SDisk) GetGuests() []SGuest {
  382. result := make([]SGuest, 0)
  383. query := GuestManager.Query()
  384. guestdisks := GuestdiskManager.Query().SubQuery()
  385. q := query.Join(guestdisks, sqlchemy.AND(
  386. sqlchemy.Equals(guestdisks.Field("guest_id"), query.Field("id")))).
  387. Filter(sqlchemy.Equals(guestdisks.Field("disk_id"), self.Id))
  388. err := db.FetchModelObjects(GuestManager, q, &result)
  389. if err != nil {
  390. log.Errorln(err)
  391. return nil
  392. }
  393. return result
  394. }
  395. func (self *SDisk) GetGuest() *SGuest {
  396. guests := self.GetGuests()
  397. if len(guests) > 0 {
  398. return &guests[0]
  399. }
  400. return nil
  401. }
  402. func (self *SDisk) GetGuestsCount() (int, error) {
  403. guests := GuestManager.Query().SubQuery()
  404. guestdisks := GuestdiskManager.Query().SubQuery()
  405. return guests.Query().Join(guestdisks, sqlchemy.AND(
  406. sqlchemy.Equals(guestdisks.Field("guest_id"), guests.Field("id")))).
  407. Filter(sqlchemy.Equals(guestdisks.Field("disk_id"), self.Id)).CountWithError()
  408. }
  409. func (self *SDisk) GetRuningGuestCount() (int, error) {
  410. guests := GuestManager.Query().SubQuery()
  411. guestdisks := GuestdiskManager.Query().SubQuery()
  412. return guests.Query().Join(guestdisks, sqlchemy.AND(
  413. sqlchemy.Equals(guestdisks.Field("guest_id"), guests.Field("id")))).
  414. Filter(sqlchemy.Equals(guestdisks.Field("disk_id"), self.Id)).
  415. Filter(sqlchemy.Equals(guests.Field("status"), api.VM_RUNNING)).CountWithError()
  416. }
  417. func (self *SDisk) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  418. input := new(api.DiskCreateInput)
  419. if err := data.Unmarshal(input); err != nil {
  420. return errors.Wrap(err, "Unmarshal json")
  421. }
  422. if err := self.fetchDiskInfo(input.DiskConfig); err != nil {
  423. return errors.Wrap(err, "fetch disk info")
  424. }
  425. err := self.SEncryptedResource.CustomizeCreate(ctx, userCred, ownerId, data, "disk-"+pinyinutils.Text2Pinyin(self.Name))
  426. if err != nil {
  427. return errors.Wrap(err, "SEncryptedResource.CustomizeCreate")
  428. }
  429. return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  430. }
  431. func (self *SDisk) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.DiskUpdateInput) (*api.DiskUpdateInput, error) {
  432. var err error
  433. if input.DiskType != "" {
  434. if !utils.IsInStringArray(input.DiskType, []string{api.DISK_TYPE_DATA, api.DISK_TYPE_VOLUME, api.DISK_TYPE_SYS}) {
  435. return input, httperrors.NewInputParameterError("not support update disk_type %s", input.DiskType)
  436. }
  437. }
  438. if input.AutoReset != nil && *input.AutoReset != self.AutoReset {
  439. if guest := self.GetGuest(); guest != nil && guest.Status != api.VM_READY {
  440. return input, httperrors.NewBadRequestError("Can't set disk auto_reset on guest status %s", guest.Status)
  441. }
  442. }
  443. storage, _ := self.GetStorage()
  444. if storage == nil {
  445. return input, httperrors.NewNotFoundError("failed to find storage for disk %s", self.Name)
  446. }
  447. host, err := storage.GetMasterHost()
  448. if err != nil {
  449. return nil, errors.Wrapf(err, "GetMasterHost")
  450. }
  451. driver, err := host.GetHostDriver()
  452. if err != nil {
  453. return nil, errors.Wrapf(err, "GetHostDriver")
  454. }
  455. input, err = driver.ValidateUpdateDisk(ctx, userCred, input)
  456. if err != nil {
  457. return input, errors.Wrap(err, "GetHostDriver().ValidateUpdateDisk")
  458. }
  459. input.VirtualResourceBaseUpdateInput, err = self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput)
  460. if err != nil {
  461. return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData")
  462. }
  463. return input, nil
  464. }
  465. func (man *SDiskManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DiskCreateInput) (*jsonutils.JSONDict, error) {
  466. input, err := man.ValidateCreateData(ctx, userCred, ownerId, query, input)
  467. if err != nil {
  468. return nil, err
  469. }
  470. return input.JSON(input), nil
  471. }
  472. func diskCreateInput2ComputeQuotaKeys(input api.DiskCreateInput, ownerId mcclient.IIdentityProvider) (SComputeResourceKeys, error) {
  473. var keys SComputeResourceKeys
  474. if len(input.PreferHost) > 0 {
  475. hostObj, err := HostManager.FetchById(input.PreferHost)
  476. if err != nil {
  477. return keys, err
  478. }
  479. host := hostObj.(*SHost)
  480. input.PreferZone = host.ZoneId
  481. keys.ZoneId = host.ZoneId
  482. }
  483. if len(input.PreferWire) > 0 {
  484. wireObj, err := WireManager.FetchById(input.PreferWire)
  485. if err != nil {
  486. return keys, err
  487. }
  488. wire := wireObj.(*SWire)
  489. if len(wire.ZoneId) > 0 {
  490. input.PreferZone = wire.ZoneId
  491. keys.ZoneId = wire.ZoneId
  492. }
  493. }
  494. if len(input.PreferZone) > 0 {
  495. zoneObj, err := ZoneManager.FetchById(input.PreferZone)
  496. if err != nil {
  497. return keys, err
  498. }
  499. zone := zoneObj.(*SZone)
  500. input.PreferRegion = zone.CloudregionId
  501. keys.ZoneId = zone.Id
  502. keys.RegionId = zone.CloudregionId
  503. }
  504. if len(input.PreferRegion) > 0 {
  505. regionObj, err := CloudregionManager.FetchById(input.PreferRegion)
  506. if err != nil {
  507. return keys, err
  508. }
  509. region := regionObj.(*SCloudregion)
  510. keys.RegionId = region.GetId()
  511. keys.Brand = region.Provider
  512. }
  513. return keys, nil
  514. }
  515. func (manager *SDiskManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DiskCreateInput) (api.DiskCreateInput, error) {
  516. diskConfig := input.DiskConfig
  517. diskConfig, err := parseDiskInfo(ctx, userCred, diskConfig)
  518. if err != nil {
  519. return input, err
  520. }
  521. if input.ExistingPath != "" && input.Storage == "" {
  522. return input, httperrors.NewInputParameterError("disk create from existing disk must give storage")
  523. }
  524. input.ProjectId = ownerId.GetProjectId()
  525. input.ProjectDomainId = ownerId.GetProjectDomainId()
  526. var quotaKey quotas.IQuotaKeys
  527. storageID := input.Storage
  528. if storageID != "" {
  529. storageObj, err := StorageManager.FetchByIdOrName(ctx, nil, storageID)
  530. if err != nil {
  531. return input, httperrors.NewResourceNotFoundError("Storage %s not found", storageID)
  532. }
  533. storage := storageObj.(*SStorage)
  534. provider := storage.GetCloudprovider()
  535. if provider != nil && !provider.IsAvailable() {
  536. return input, httperrors.NewResourceNotReadyError("cloudprovider %s not available", provider.Name)
  537. }
  538. host, err := storage.GetMasterHost()
  539. if err != nil {
  540. return input, errors.Wrapf(err, "GetMasterHost")
  541. }
  542. hostDriver, err := host.GetHostDriver()
  543. if err != nil {
  544. return input, errors.Wrapf(err, "GetHostDriver")
  545. }
  546. input.Hypervisor = hostDriver.GetHypervisor()
  547. if len(diskConfig.Backend) == 0 {
  548. diskConfig.Backend = storage.StorageType
  549. }
  550. err = manager.validateDiskOnStorage(diskConfig, storage)
  551. if err != nil {
  552. return input, err
  553. }
  554. input.Storage = storage.Id
  555. zone, _ := storage.getZone()
  556. quotaKey = fetchComputeQuotaKeys(
  557. rbacscope.ScopeProject,
  558. ownerId,
  559. zone,
  560. provider,
  561. input.Hypervisor,
  562. )
  563. } else {
  564. if len(diskConfig.Backend) == 0 {
  565. diskConfig.Backend = api.STORAGE_LOCAL
  566. }
  567. if len(input.PreferManager) > 0 {
  568. _manager, err := CloudproviderManager.FetchByIdOrName(ctx, userCred, input.PreferManager)
  569. if err != nil {
  570. if errors.Cause(err) == sql.ErrNoRows {
  571. return input, httperrors.NewResourceNotFoundError2("cloudprovider", input.PreferManager)
  572. }
  573. return input, httperrors.NewGeneralError(err)
  574. }
  575. manager := _manager.(*SCloudprovider)
  576. if !manager.IsAvailable() {
  577. return input, httperrors.NewResourceNotReadyError("cloudprovider %s not available", manager.Name)
  578. }
  579. input.PreferManager = manager.Id
  580. }
  581. serverInput, err := ValidateScheduleCreateData(ctx, userCred, input.ToServerCreateInput(), input.Hypervisor)
  582. if err != nil {
  583. return input, err
  584. }
  585. // preserve encrypt info
  586. encInput := input.EncryptedResourceCreateInput
  587. input = *serverInput.ToDiskCreateInput()
  588. input.EncryptedResourceCreateInput = encInput
  589. quotaKey, _ = diskCreateInput2ComputeQuotaKeys(input, ownerId)
  590. }
  591. input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
  592. if err != nil {
  593. return input, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData")
  594. }
  595. input.EncryptedResourceCreateInput, err = manager.SEncryptedResourceManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EncryptedResourceCreateInput)
  596. if err != nil {
  597. return input, errors.Wrap(err, "SEncryptedResourceManager.ValidateCreateData")
  598. }
  599. if err := manager.ValidateFsFeatures(input.Fs, input.FsFeatures); err != nil {
  600. return input, err
  601. }
  602. pendingUsage := SQuota{Storage: diskConfig.SizeMb}
  603. pendingUsage.SetKeys(quotaKey)
  604. if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil {
  605. return input, httperrors.NewOutOfQuotaError("%s", err)
  606. }
  607. return input, nil
  608. }
  609. func (manager *SDiskManager) ValidateFsFeatures(fsType string, feature *api.DiskFsFeatures) error {
  610. if feature == nil {
  611. return nil
  612. }
  613. if feature.Ext4 != nil {
  614. if fsType != "ext4" {
  615. return httperrors.NewInputParameterError("only ext4 fs can set fs_features.ext4, current is %q", fsType)
  616. }
  617. if feature.Ext4.ReservedBlocksPercentage < 0 || feature.Ext4.ReservedBlocksPercentage >= 100 {
  618. return httperrors.NewInputParameterError("ext4.reserved_blocks_percentage must in range [1, 99]")
  619. }
  620. }
  621. if feature.F2fs != nil {
  622. if fsType != "f2fs" {
  623. return httperrors.NewInputParameterError("only f2fs fs can set fs_features.f2fs, current is %q", fsType)
  624. }
  625. if feature.F2fs.OverprovisionRatioPercentage < 0 || feature.F2fs.OverprovisionRatioPercentage >= 100 {
  626. return httperrors.NewInputParameterError("f2fs.reserved_blocks_percentage must in range [1, 99]")
  627. }
  628. }
  629. return nil
  630. }
  631. func (manager *SDiskManager) validateDiskOnStorage(diskConfig *api.DiskConfig, storage *SStorage) error {
  632. if storage.Enabled.IsFalse() {
  633. return httperrors.NewInputParameterError("Cannot create disk with disabled storage[%s]", storage.Name)
  634. }
  635. if !utils.IsInStringArray(storage.Status, []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}) {
  636. return httperrors.NewInputParameterError("Cannot create disk with offline storage[%s]", storage.Name)
  637. }
  638. if storage.StorageType != diskConfig.Backend {
  639. return httperrors.NewInputParameterError("Storage type[%s] not match backend %s", storage.StorageType, diskConfig.Backend)
  640. }
  641. if diskConfig.ExistingPath != "" {
  642. if !utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
  643. return httperrors.NewInputParameterError(
  644. "Disk create from existing path, unsupport storage type %s", storage.StorageType)
  645. }
  646. }
  647. var guestdriver IGuestDriver = nil
  648. if host, _ := storage.GetMasterHost(); host != nil {
  649. //公有云磁盘大小检查。
  650. hostDriver, err := host.GetHostDriver()
  651. if err != nil {
  652. return errors.Wrapf(err, "GetHostDriver")
  653. }
  654. if err := hostDriver.ValidateDiskSize(storage, diskConfig.SizeMb>>10); err != nil {
  655. return httperrors.NewInputParameterError("%v", err)
  656. }
  657. guestdriver, _ = GetDriver(hostDriver.GetHypervisor(), hostDriver.GetProvider())
  658. }
  659. hoststorages := HoststorageManager.Query().SubQuery()
  660. hoststorage := make([]SHoststorage, 0)
  661. if err := hoststorages.Query().Equals("storage_id", storage.Id).All(&hoststorage); err != nil {
  662. return err
  663. }
  664. if len(hoststorage) == 0 {
  665. return httperrors.NewInputParameterError("Storage[%s] must attach to a host", storage.Name)
  666. }
  667. if guestdriver == nil || guestdriver.DoScheduleStorageFilter() {
  668. if int64(diskConfig.SizeMb) > storage.GetFreeCapacity() && !storage.IsEmulated {
  669. return httperrors.NewInputParameterError("Not enough free space")
  670. }
  671. }
  672. return nil
  673. }
  674. func (disk *SDisk) SetStorage(storageId string, diskConfig *api.DiskConfig) error {
  675. backend := diskConfig.Backend
  676. if backend == "" {
  677. return fmt.Errorf("Backend is empty")
  678. }
  679. storage := StorageManager.FetchStorageById(storageId)
  680. if storage == nil {
  681. return fmt.Errorf("Not found backend %s storage %s", backend, storageId)
  682. }
  683. err := DiskManager.validateDiskOnStorage(diskConfig, storage)
  684. if err != nil {
  685. return err
  686. }
  687. _, err = db.Update(disk, func() error {
  688. disk.StorageId = storage.Id
  689. return nil
  690. })
  691. return err
  692. }
  693. func (disk *SDisk) SetStorageByHost(hostId string, diskConfig *api.DiskConfig, storageIds []string) error {
  694. host := HostManager.FetchHostById(hostId)
  695. backend := diskConfig.Backend
  696. if backend == "" {
  697. return fmt.Errorf("Backend is empty")
  698. }
  699. var storage *SStorage
  700. if len(storageIds) != 0 {
  701. storage = StorageManager.FetchStorageById(storageIds[0])
  702. } else if utils.IsInStringArray(backend, api.STORAGE_LIMITED_TYPES) {
  703. storage = host.GetLeastUsedStorage(backend)
  704. } else {
  705. // unlimited pulic cloud storages
  706. storages := host.GetAttachedEnabledHostStorages(nil)
  707. for _, s := range storages {
  708. if s.StorageType == backend {
  709. tmpS := s
  710. storage = &tmpS
  711. }
  712. }
  713. }
  714. if storage == nil {
  715. return fmt.Errorf("Not found host %s backend %s storage", host.Name, backend)
  716. }
  717. err := DiskManager.validateDiskOnStorage(diskConfig, storage)
  718. if err != nil {
  719. return err
  720. }
  721. _, err = db.Update(disk, func() error {
  722. disk.StorageId = storage.Id
  723. disk.IsSsd = (storage.MediumType == api.DISK_TYPE_SSD)
  724. return nil
  725. })
  726. return err
  727. }
  728. func getDiskResourceRequirements(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.DiskCreateInput, count int) SQuota {
  729. req := SQuota{
  730. Storage: input.SizeMb * count,
  731. }
  732. var quotaKey SComputeResourceKeys
  733. if len(input.Storage) > 0 {
  734. storageObj, _ := StorageManager.FetchById(input.Storage)
  735. storage := storageObj.(*SStorage)
  736. zone, _ := storage.getZone()
  737. quotaKey = fetchComputeQuotaKeys(
  738. rbacscope.ScopeProject,
  739. ownerId,
  740. zone,
  741. storage.GetCloudprovider(),
  742. input.Hypervisor,
  743. )
  744. } else {
  745. quotaKey, _ = diskCreateInput2ComputeQuotaKeys(input, ownerId)
  746. }
  747. req.SetKeys(quotaKey)
  748. return req
  749. }
  750. func (disk *SDisk) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) {
  751. if len(disk.ExternalId) == 0 || options.Options.KeepTagLocalization {
  752. return
  753. }
  754. err := disk.StartRemoteUpdateTask(ctx, userCred, true, "")
  755. if err != nil {
  756. log.Errorf("StartRemoteUpdateTask fail: %s", err)
  757. }
  758. }
  759. func (disk *SDisk) StartRemoteUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, replaceTags bool, parentTaskId string) error {
  760. data := jsonutils.NewDict()
  761. if replaceTags {
  762. data.Add(jsonutils.JSONTrue, "replace_tags")
  763. }
  764. task, err := taskman.TaskManager.NewTask(ctx, "DiskRemoteUpdateTask", disk, userCred, data, parentTaskId, "", nil)
  765. if err != nil {
  766. return errors.Wrap(err, "Start DiskRemoteUpdateTask")
  767. }
  768. disk.SetStatus(ctx, userCred, apis.STATUS_UPDATE_TAGS, "StartRemoteUpdateTask")
  769. return task.ScheduleRun(nil)
  770. }
  771. func (disk *SDisk) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  772. disk.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  773. input := api.DiskCreateInput{}
  774. err := data.Unmarshal(&input)
  775. if err != nil {
  776. log.Errorf("!!!data.Unmarshal api.DiskCreateInput fail %s", err)
  777. }
  778. if input.ExistingPath != "" {
  779. disk.SetMetadata(ctx, api.DISK_META_EXISTING_PATH, input.ExistingPath, userCred)
  780. }
  781. }
  782. func (manager *SDiskManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) {
  783. input := api.DiskCreateInput{}
  784. err := data[0].Unmarshal(&input)
  785. if err != nil {
  786. log.Errorf("!!!data.Unmarshal api.DiskCreateInput fail %s", err)
  787. }
  788. pendingUsage := getDiskResourceRequirements(ctx, userCred, ownerId, input, len(items))
  789. parentTaskId, _ := data[0].GetString("parent_task_id")
  790. RunBatchCreateTask(ctx, items, userCred, data, pendingUsage, SRegionQuota{}, "DiskBatchCreateTask", parentTaskId)
  791. }
  792. func (self *SDisk) StartDiskCreateTask(ctx context.Context, userCred mcclient.TokenCredential, rebuild bool, snapshot string, parentTaskId string) error {
  793. kwargs := jsonutils.NewDict()
  794. if rebuild {
  795. kwargs.Add(jsonutils.JSONTrue, "rebuild")
  796. }
  797. if len(snapshot) > 0 {
  798. kwargs.Add(jsonutils.NewString(snapshot), "snapshot")
  799. }
  800. taskName := "DiskCreateTask"
  801. if self.BackupStorageId != "" {
  802. taskName = "HADiskCreateTask"
  803. }
  804. if task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, kwargs, parentTaskId, "", nil); err != nil {
  805. return err
  806. } else {
  807. task.ScheduleRun(nil)
  808. }
  809. return nil
  810. }
  811. func (self *SDisk) GetSnapshotFuseUrl() (string, error) {
  812. snapObj, err := SnapshotManager.FetchById(self.SnapshotId)
  813. if err != nil {
  814. return "", errors.Wrapf(err, "SnapshotManager.FetchById(%s)", self.SnapshotId)
  815. }
  816. snapshot := snapObj.(*SSnapshot)
  817. return snapshot.GetFuseUrl()
  818. }
  819. func (self *SDisk) GetSnapshotCount() (int, error) {
  820. q := SnapshotManager.Query()
  821. return q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), self.Id),
  822. sqlchemy.Equals(q.Field("fake_deleted"), false))).CountWithError()
  823. }
  824. func (self *SDisk) GetManualSnapshotCount() (int, error) {
  825. return SnapshotManager.Query().
  826. Equals("disk_id", self.Id).Equals("fake_deleted", false).
  827. Equals("created_by", api.SNAPSHOT_MANUAL).CountWithError()
  828. }
  829. func (self *SDisk) getDiskAllocateFromBackupInput(ctx context.Context, backupId string) (*api.DiskAllocateFromBackupInput, error) {
  830. ibackup, err := DiskBackupManager.FetchById(backupId)
  831. if err != nil {
  832. return nil, errors.Wrapf(err, "unable to get backup %s", backupId)
  833. }
  834. backup := ibackup.(*SDiskBackup)
  835. bs, err := backup.GetBackupStorage()
  836. if err != nil {
  837. return nil, errors.Wrapf(err, "unable to get backupstorage of backup %s", backupId)
  838. }
  839. accessInfo, err := bs.GetAccessInfo()
  840. if err != nil {
  841. return nil, errors.Wrap(err, "backupStorage.GetAccessInfo")
  842. }
  843. return &api.DiskAllocateFromBackupInput{
  844. BackupId: backupId,
  845. BackupStorageId: bs.GetId(),
  846. BackupStorageAccessInfo: jsonutils.Marshal(accessInfo).(*jsonutils.JSONDict),
  847. DiskConfig: &backup.DiskConfig.DiskConfig,
  848. BackupAsTar: backup.DiskConfig.BackupAsTar,
  849. }, nil
  850. }
  851. func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SStorage, taskId string, userCred mcclient.TokenCredential, rebuild bool, snapshot string, task taskman.ITask) error {
  852. log.Infof("Allocating disk on host %s ...", host.GetName())
  853. templateId := self.GetTemplateId()
  854. fsFormat := self.GetFsFormat()
  855. input := api.DiskAllocateInput{
  856. Format: self.DiskFormat,
  857. DiskSizeMb: self.DiskSize,
  858. SnapshotId: snapshot,
  859. }
  860. if self.BackupId != "" {
  861. allocateInput, err := self.getDiskAllocateFromBackupInput(ctx, self.BackupId)
  862. if err != nil {
  863. return errors.Wrap(err, "unable to getDiskAllocateFromBackupInput")
  864. }
  865. input.Backup = allocateInput
  866. }
  867. if len(snapshot) > 0 {
  868. if utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
  869. SnapshotManager.AddRefCount(self.SnapshotId, 1)
  870. self.SetMetadata(ctx, "merge_snapshot", jsonutils.JSONTrue, userCred)
  871. }
  872. } else if len(templateId) > 0 {
  873. input.ImageId = templateId
  874. s := auth.GetAdminSession(ctx, options.Options.Region)
  875. img, err := image.Images.Get(s, templateId, nil)
  876. if err != nil {
  877. return errors.Wrapf(err, "get image details from glance")
  878. }
  879. input.ImageFormat, _ = img.GetString("disk_format")
  880. }
  881. if len(fsFormat) > 0 {
  882. input.FsFormat = fsFormat
  883. if self.FsFeatures != nil {
  884. input.FsFeatures = self.FsFeatures
  885. }
  886. }
  887. if self.IsEncrypted() {
  888. var err error
  889. input.Encryption = true
  890. input.EncryptInfo, err = self.GetEncryptInfo(ctx, userCred)
  891. if err != nil {
  892. return errors.Wrap(err, "GetEncryptInfo")
  893. }
  894. }
  895. if ePath := self.GetMetadata(ctx, api.DISK_META_EXISTING_PATH, userCred); ePath != "" {
  896. input.ExistingPath = ePath
  897. }
  898. driver, err := host.GetHostDriver()
  899. if err != nil {
  900. return errors.Wrapf(err, "GetHostDriver")
  901. }
  902. if rebuild {
  903. return driver.RequestRebuildDiskOnStorage(ctx, host, storage, self, task, input)
  904. }
  905. return driver.RequestAllocateDiskOnStorage(ctx, userCred, host, storage, self, task, input)
  906. }
  907. // make snapshot after reset out of chain
  908. func (self *SDisk) CleanUpDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, snapshot *SSnapshot) error {
  909. dest := make([]SSnapshot, 0)
  910. query := SnapshotManager.Query()
  911. query.Filter(sqlchemy.Equals(query.Field("disk_id"), self.Id)).
  912. GT("created_at", snapshot.CreatedAt).Asc("created_at").All(&dest)
  913. if len(dest) == 0 {
  914. return nil
  915. }
  916. convertSnapshots := jsonutils.NewArray()
  917. deleteSnapshots := jsonutils.NewArray()
  918. for i := 0; i < len(dest); i++ {
  919. if !dest[i].FakeDeleted && !dest[i].OutOfChain {
  920. convertSnapshots.Add(jsonutils.NewString(dest[i].Id))
  921. } else if dest[i].FakeDeleted {
  922. deleteSnapshots.Add(jsonutils.NewString(dest[i].Id))
  923. }
  924. }
  925. params := jsonutils.NewDict()
  926. params.Set("convert_snapshots", convertSnapshots)
  927. params.Set("delete_snapshots", deleteSnapshots)
  928. task, err := taskman.TaskManager.NewTask(ctx, "DiskCleanUpSnapshotsTask", self, userCred, params, "", "", nil)
  929. if err != nil {
  930. return err
  931. } else {
  932. task.ScheduleRun(nil)
  933. }
  934. return nil
  935. }
  936. func (self *SDisk) PerformDiskReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.DiskResetInput) (jsonutils.JSONObject, error) {
  937. err := self.ValidateEncryption(ctx, userCred)
  938. if err != nil {
  939. return nil, errors.Wrap(err, "ValidateEncryption")
  940. }
  941. if !utils.IsInStringArray(self.Status, []string{api.DISK_READY}) {
  942. return nil, httperrors.NewInputParameterError("Cannot reset disk in status %s", self.Status)
  943. }
  944. storage, err := self.GetStorage()
  945. if err != nil {
  946. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetStorage"))
  947. }
  948. host, err := storage.GetMasterHost()
  949. if err != nil {
  950. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetMasterHost"))
  951. }
  952. snapshotObj, err := validators.ValidateModel(ctx, userCred, SnapshotManager, &input.SnapshotId)
  953. if err != nil {
  954. return nil, err
  955. }
  956. snapshot := snapshotObj.(*SSnapshot)
  957. if snapshot.Status != api.SNAPSHOT_READY {
  958. return nil, httperrors.NewBadRequestError("Cannot reset disk with snapshot in status %s", snapshot.Status)
  959. }
  960. if snapshot.DiskId != self.Id {
  961. return nil, httperrors.NewBadRequestError("Cannot reset disk %s(%s),Snapshot is belong to disk %s", self.Name, self.Id, snapshot.DiskId)
  962. }
  963. driver, err := host.GetHostDriver()
  964. if err != nil {
  965. return nil, errors.Wrapf(err, "GetHostDriver")
  966. }
  967. guests := self.GetGuests()
  968. input, err = driver.ValidateResetDisk(ctx, userCred, self, snapshot, guests, input)
  969. if err != nil {
  970. return nil, err
  971. }
  972. var guest *SGuest = nil
  973. if len(guests) > 0 {
  974. guest = &guests[0]
  975. }
  976. return nil, self.StartResetDisk(ctx, userCred, snapshot.Id, input.AutoStart, guest, "")
  977. }
  978. func (self *SDisk) validateMigrate(ctx context.Context, userCred mcclient.TokenCredential, input *api.DiskMigrateInput) error {
  979. hypervisor := self.getHypervisor()
  980. if !utils.IsInStringArray(hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_POD}) {
  981. return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", hypervisor)
  982. }
  983. if guest := self.GetGuest(); guest != nil {
  984. return httperrors.NewBadRequestError("Disk attached guest, cannot migrate")
  985. }
  986. if input.TargetStorageId != "" {
  987. srcStorage, err := self.GetStorage()
  988. if err != nil {
  989. return errors.Wrap(err, "get src storage")
  990. }
  991. iDstStorage, err := StorageManager.FetchByIdOrName(ctx, userCred, input.TargetStorageId)
  992. if err != nil {
  993. return errors.Wrap(err, "get target storage")
  994. }
  995. if srcStorage.StorageType != iDstStorage.(*SStorage).StorageType {
  996. return httperrors.NewBadRequestError("Cannot migrate disk from storage type %s to %s", srcStorage.StorageType, iDstStorage.(*SStorage).StorageType)
  997. }
  998. input.TargetStorageId = iDstStorage.GetId()
  999. }
  1000. return nil
  1001. }
  1002. func (self *SDisk) PerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.DiskMigrateInput) (jsonutils.JSONObject, error) {
  1003. err := self.validateMigrate(ctx, userCred, input)
  1004. if err != nil {
  1005. return nil, err
  1006. }
  1007. self.SetStatus(ctx, userCred, api.DISK_START_MIGRATE, "")
  1008. params := jsonutils.NewDict()
  1009. params.Set("target_storage_id", jsonutils.NewString(input.TargetStorageId))
  1010. task, err := taskman.TaskManager.NewTask(ctx, "DiskMigrateTask", self, userCred, params, "", "", nil)
  1011. if err != nil {
  1012. return nil, errors.Wrapf(err, "NewTask")
  1013. }
  1014. return nil, task.ScheduleRun(nil)
  1015. }
  1016. func (self *SDisk) PerformChangeStorageType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.DiskChagneStorageTypeInput) (jsonutils.JSONObject, error) {
  1017. if len(input.StorageType) == 0 {
  1018. return nil, httperrors.NewMissingParameterError("storage_type")
  1019. }
  1020. storage, err := self.GetStorage()
  1021. if err != nil {
  1022. return nil, errors.Wrapf(err, "GetStorage")
  1023. }
  1024. if storage.StorageType == input.StorageType {
  1025. return nil, httperrors.NewInputParameterError("Storage type is already %s", input.StorageType)
  1026. }
  1027. storages := []SStorage{}
  1028. q := StorageManager.Query().Equals("zone_id", storage.ZoneId).Equals("storage_type", input.StorageType).IsTrue("enabled").Equals("status", api.STORAGE_ONLINE).Equals("manager_id", storage.ManagerId)
  1029. err = q.All(&storages)
  1030. if err != nil {
  1031. return nil, errors.Wrapf(err, "CountWithError")
  1032. }
  1033. if len(storages) == 0 {
  1034. return nil, httperrors.NewInputParameterError("no available storage type %s to change", input.StorageType)
  1035. }
  1036. if len(storages) > 1 {
  1037. return nil, httperrors.NewInputParameterError("duplicate storage type %s found", input.StorageType)
  1038. }
  1039. self.SetStatus(ctx, userCred, api.DISK_MIGRATING, "")
  1040. params := jsonutils.NewDict()
  1041. params.Set("storage_id", jsonutils.NewString(storages[0].Id))
  1042. task, err := taskman.TaskManager.NewTask(ctx, "DiskChangeStorageTypeTask", self, userCred, params, "", "", nil)
  1043. if err != nil {
  1044. return nil, errors.Wrapf(err, "NewTask")
  1045. }
  1046. return nil, task.ScheduleRun(nil)
  1047. }
  1048. func (self *SDisk) GetSchedMigrateParams(targetStorageId string) (*schedapi.ScheduleInput, error) {
  1049. diskConfig := self.ToDiskConfig()
  1050. diskConfig.Medium = ""
  1051. diskConfig.Storage = targetStorageId
  1052. input := new(api.DiskCreateInput)
  1053. input.DiskConfig = diskConfig
  1054. srvInput := input.ToServerCreateInput()
  1055. ret := new(schedapi.ScheduleInput)
  1056. err := srvInput.JSON(srvInput).Unmarshal(ret)
  1057. if err != nil {
  1058. return nil, err
  1059. }
  1060. if targetStorageId == "" {
  1061. storage, err := self.GetStorage()
  1062. if err != nil {
  1063. return nil, err
  1064. }
  1065. host, err := storage.GetMasterHost()
  1066. if err != nil {
  1067. return nil, err
  1068. }
  1069. ret.HostId = host.Id
  1070. ret.LiveMigrate = false
  1071. }
  1072. return ret, err
  1073. }
  1074. func (self *SDisk) StartResetDisk(
  1075. ctx context.Context, userCred mcclient.TokenCredential,
  1076. snapshotId string, autoStart bool, guest *SGuest, parentTaskId string,
  1077. ) error {
  1078. self.SetStatus(ctx, userCred, api.DISK_RESET, "")
  1079. if guest != nil {
  1080. guest.SetStatus(ctx, userCred, api.VM_DISK_RESET, "disk reset")
  1081. }
  1082. params := jsonutils.NewDict()
  1083. params.Set("snapshot_id", jsonutils.NewString(snapshotId))
  1084. params.Set("auto_start", jsonutils.NewBool(autoStart))
  1085. task, err := taskman.TaskManager.NewTask(ctx, "DiskResetTask", self, userCred, params, parentTaskId, "", nil)
  1086. if err != nil {
  1087. return errors.Wrapf(err, "NewTask")
  1088. }
  1089. return task.ScheduleRun(nil)
  1090. }
  1091. func (disk *SDisk) PerformResize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskResizeInput) (jsonutils.JSONObject, error) {
  1092. guest := disk.GetGuest()
  1093. sizeMb, err := input.SizeMb()
  1094. if err != nil {
  1095. return nil, err
  1096. }
  1097. err = disk.doResize(ctx, userCred, sizeMb, guest)
  1098. if err != nil {
  1099. return nil, err
  1100. }
  1101. return nil, nil
  1102. }
  1103. func (disk *SDisk) getHypervisor() string {
  1104. storage, _ := disk.GetStorage()
  1105. if storage != nil {
  1106. host, _ := storage.GetMasterHost()
  1107. if host != nil {
  1108. driver, _ := host.GetHostDriver()
  1109. if driver != nil {
  1110. return driver.GetHypervisor()
  1111. }
  1112. }
  1113. }
  1114. hypervisor := disk.GetMetadata(context.Background(), "hypervisor", nil)
  1115. return hypervisor
  1116. }
  1117. func (disk *SDisk) GetQuotaKeys() (quotas.IQuotaKeys, error) {
  1118. storage, _ := disk.GetStorage()
  1119. if storage == nil {
  1120. return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid storage")
  1121. }
  1122. provider := storage.GetCloudprovider()
  1123. if provider == nil && len(storage.ManagerId) > 0 {
  1124. return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid manager")
  1125. }
  1126. zone, _ := storage.getZone()
  1127. if zone == nil {
  1128. return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid zone")
  1129. }
  1130. return fetchComputeQuotaKeys(
  1131. rbacscope.ScopeProject,
  1132. disk.GetOwnerId(),
  1133. zone,
  1134. provider,
  1135. disk.getHypervisor(),
  1136. ), nil
  1137. }
  1138. func (disk *SDisk) doResize(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int, guest *SGuest) error {
  1139. if disk.Status != api.DISK_READY {
  1140. return httperrors.NewResourceNotReadyError("Resize disk when disk is READY")
  1141. }
  1142. if sizeMb < disk.DiskSize {
  1143. return httperrors.NewUnsupportOperationError("Disk cannot be thrink")
  1144. }
  1145. if sizeMb == disk.DiskSize {
  1146. return nil
  1147. }
  1148. addDisk := sizeMb - disk.DiskSize
  1149. storage, _ := disk.GetStorage()
  1150. if storage == nil {
  1151. return httperrors.NewInternalServerError("disk has no valid storage")
  1152. }
  1153. var guestdriver IGuestDriver
  1154. if host, _ := storage.GetMasterHost(); host != nil {
  1155. hostDriver, err := host.GetHostDriver()
  1156. if err != nil {
  1157. return errors.Wrapf(err, "GetHostDriver")
  1158. }
  1159. if err := hostDriver.ValidateDiskSize(storage, sizeMb>>10); err != nil {
  1160. return httperrors.NewInputParameterError("%v", err)
  1161. }
  1162. guestdriver, _ = GetDriver(hostDriver.GetHypervisor(), hostDriver.GetProvider())
  1163. }
  1164. if guestdriver == nil || guestdriver.DoScheduleStorageFilter() {
  1165. if int64(addDisk) > storage.GetFreeCapacity() && !storage.IsEmulated {
  1166. return httperrors.NewOutOfResourceError("Not enough free space")
  1167. }
  1168. }
  1169. if guest != nil {
  1170. if err := guest.ValidateResizeDisk(disk, storage); err != nil {
  1171. return httperrors.NewInputParameterError("%v", err)
  1172. }
  1173. }
  1174. pendingUsage := SQuota{Storage: int(addDisk)}
  1175. keys, err := disk.GetQuotaKeys()
  1176. if err != nil {
  1177. return httperrors.NewInternalServerError("disk.GetQuotaKeys fail %s", err)
  1178. }
  1179. pendingUsage.SetKeys(keys)
  1180. err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage)
  1181. if err != nil {
  1182. return httperrors.NewGeneralError(err)
  1183. }
  1184. if guest != nil {
  1185. return guest.StartGuestDiskResizeTask(ctx, userCred, disk.Id, int64(sizeMb), "", &pendingUsage)
  1186. } else {
  1187. return disk.StartDiskResizeTask(ctx, userCred, int64(sizeMb), "", &pendingUsage)
  1188. }
  1189. }
  1190. func (self *SDisk) GetIStorage(ctx context.Context) (cloudprovider.ICloudStorage, error) {
  1191. storage, err := self.GetStorage()
  1192. if err != nil {
  1193. return nil, errors.Wrapf(err, "GetStorage")
  1194. }
  1195. istorage, err := storage.GetIStorage(ctx)
  1196. if err != nil {
  1197. return nil, err
  1198. }
  1199. return istorage, nil
  1200. }
  1201. func (self *SDisk) GetIDisk(ctx context.Context) (cloudprovider.ICloudDisk, error) {
  1202. if len(self.ExternalId) == 0 {
  1203. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty external id")
  1204. }
  1205. iStorage, err := self.GetIStorage(ctx)
  1206. if err != nil {
  1207. return nil, errors.Wrapf(err, "GetIStorage")
  1208. }
  1209. return iStorage.GetIDiskById(self.GetExternalId())
  1210. }
  1211. func (self *SDisk) GetZone() (*SZone, error) {
  1212. storage, err := self.GetStorage()
  1213. if err != nil {
  1214. return nil, err
  1215. }
  1216. return storage.getZone()
  1217. }
  1218. func (m *SDiskManager) CheckGlanceImage(ctx context.Context, userCred mcclient.TokenCredential, name string, generateName string) error {
  1219. if len(generateName) == 0 {
  1220. s := auth.GetAdminSession(ctx, options.Options.Region)
  1221. imageList, err := image.Images.List(s, jsonutils.Marshal(map[string]string{"name": name, "admin": "true"}))
  1222. if err != nil {
  1223. return err
  1224. }
  1225. if imageList.Total > 0 {
  1226. return httperrors.NewConflictError("Duplicate image name %s", name)
  1227. }
  1228. }
  1229. return nil
  1230. }
  1231. type CreateGlanceImageInput struct {
  1232. Name string
  1233. GenerateName string
  1234. VirtualSize int
  1235. DiskFormat string
  1236. OsArch string
  1237. Properties map[string]string
  1238. ProjectId string
  1239. EncryptKeyId string
  1240. ClassMetadata map[string]string
  1241. }
  1242. func (m *SDiskManager) CreateGlanceImage(ctx context.Context, userCred mcclient.TokenCredential, input *CreateGlanceImageInput) (string, error) {
  1243. if err := DiskManager.CheckGlanceImage(ctx, userCred, input.Name, input.GenerateName); err != nil {
  1244. return "", err
  1245. }
  1246. /*
  1247. no need to check quota anymore
  1248. session := auth.GetSession(userCred, options.Options.Region, "v2")
  1249. quota := image_models.SQuota{Image: 1}
  1250. if _, err := image.ImageQuotas.DoQuotaCheck(session, jsonutils.Marshal(&quota)); err != nil {
  1251. return "", err
  1252. }*/
  1253. us := auth.GetSession(ctx, userCred, options.Options.Region)
  1254. result, err := image.Images.Create(us, jsonutils.Marshal(input))
  1255. if err != nil {
  1256. return "", err
  1257. }
  1258. imageId, err := result.GetString("id")
  1259. if err != nil {
  1260. return "", err
  1261. }
  1262. if len(input.ClassMetadata) > 0 {
  1263. _, err = image.Images.PerformAction(us, imageId, "set-class-metadata", jsonutils.Marshal(input.ClassMetadata))
  1264. if err != nil {
  1265. return "", errors.Wrapf(err, "unable to SetClassMetadata for image %s", imageId)
  1266. }
  1267. }
  1268. return imageId, nil
  1269. }
  1270. func (self *SDisk) PrepareSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSaveImageInput) (string, error) {
  1271. zone, _ := self.GetZone()
  1272. if zone == nil {
  1273. return "", httperrors.NewResourceNotFoundError("No zone for this disk")
  1274. }
  1275. imageInput := &CreateGlanceImageInput{
  1276. Name: input.Name,
  1277. GenerateName: input.GenerateName,
  1278. VirtualSize: self.DiskSize,
  1279. DiskFormat: self.DiskFormat,
  1280. OsArch: input.OsArch,
  1281. Properties: map[string]string{
  1282. "notes": input.Notes,
  1283. "os_type": input.OsType,
  1284. "os_arch": input.OsArch,
  1285. },
  1286. // inherit the ownership of disk
  1287. ProjectId: self.ProjectId,
  1288. }
  1289. if self.IsEncrypted() {
  1290. encKey, err := self.GetEncryptInfo(ctx, userCred)
  1291. if err != nil {
  1292. return "", errors.Wrap(err, "GetEncryptInfo")
  1293. }
  1294. imageInput.EncryptKeyId = encKey.Id
  1295. }
  1296. // check class metadata
  1297. cm, err := self.GetAllClassMetadata()
  1298. if err != nil {
  1299. return "", errors.Wrap(err, "unable to GetAllClassMetadata")
  1300. }
  1301. imageInput.ClassMetadata = cm
  1302. return DiskManager.CreateGlanceImage(ctx, userCred, imageInput)
  1303. }
  1304. func (self *SDisk) PerformSave(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskSaveInput) (jsonutils.JSONObject, error) {
  1305. if self.Status != api.DISK_READY {
  1306. return nil, httperrors.NewResourceNotReadyError("Save disk when disk is READY")
  1307. }
  1308. cnt, err := self.GetRuningGuestCount()
  1309. if err != nil {
  1310. return nil, httperrors.NewInternalServerError("GetRuningGuestCount fail %s", err)
  1311. }
  1312. if cnt > 0 {
  1313. return nil, httperrors.NewResourceNotReadyError("Save disk when not being USED")
  1314. }
  1315. if len(input.Name) == 0 {
  1316. return nil, httperrors.NewInputParameterError("Image name is required")
  1317. }
  1318. opts := api.ServerSaveImageInput{
  1319. Name: input.Name,
  1320. }
  1321. input.ImageId, err = self.PrepareSaveImage(ctx, userCred, opts)
  1322. if err != nil {
  1323. return nil, errors.Wrapf(err, "PrepareSaveImage")
  1324. }
  1325. return nil, self.StartDiskSaveTask(ctx, userCred, input, "")
  1326. }
  1327. func (self *SDisk) StartDiskSaveTask(ctx context.Context, userCred mcclient.TokenCredential, input api.DiskSaveInput, parentTaskId string) error {
  1328. data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  1329. task, err := taskman.TaskManager.NewTask(ctx, "DiskSaveTask", self, userCred, data, parentTaskId, "", nil)
  1330. if err != nil {
  1331. return errors.Wrapf(err, "NewTask")
  1332. }
  1333. self.SetStatus(ctx, userCred, api.DISK_START_SAVE, "")
  1334. task.ScheduleRun(nil)
  1335. return nil
  1336. }
  1337. func (self *SDisk) ValidateDeleteCondition(ctx context.Context, info api.DiskDetails) error {
  1338. if len(info.Guests) > 0 {
  1339. return httperrors.NewNotEmptyError("Virtual disk %s(%s) used by virtual servers", self.Name, self.Id)
  1340. }
  1341. if self.IsNotDeletablePrePaid() {
  1342. return httperrors.NewForbiddenError("not allow to delete prepaid disk in valid status")
  1343. }
  1344. return self.SVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
  1345. }
  1346. func (self *SDisk) validateDeleteCondition(ctx context.Context, isPurge bool) error {
  1347. if !isPurge {
  1348. storage, _ := self.GetStorage()
  1349. if storage == nil {
  1350. // storage is empty, a dirty data, allow to delete
  1351. return nil
  1352. }
  1353. host, _ := storage.GetMasterHost()
  1354. if host == nil {
  1355. return httperrors.NewBadRequestError("storage of disk %s no valid host", self.Id)
  1356. }
  1357. }
  1358. cnt, err := self.GetGuestDiskCount()
  1359. if err != nil {
  1360. return httperrors.NewInternalServerError("GetGuestDiskCount for disk %s fail %s", self.Id, err)
  1361. }
  1362. if cnt > 0 {
  1363. return httperrors.NewNotEmptyError("Virtual disk %s(%s) used by virtual servers", self.Name, self.Id)
  1364. }
  1365. if !isPurge && self.IsNotDeletablePrePaid() {
  1366. return httperrors.NewForbiddenError("not allow to delete prepaid disk in valid status")
  1367. }
  1368. return self.SVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
  1369. }
  1370. func (self *SDisk) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
  1371. provider := self.GetCloudprovider()
  1372. if provider != nil {
  1373. if !provider.IsAvailable() {
  1374. return false
  1375. }
  1376. account, _ := provider.GetCloudaccount()
  1377. if account != nil && !account.IsAvailable() {
  1378. return false
  1379. }
  1380. }
  1381. overridePendingDelete := false
  1382. purge := false
  1383. if query != nil {
  1384. overridePendingDelete = jsonutils.QueryBoolean(query, "override_pending_delete", false)
  1385. purge = jsonutils.QueryBoolean(query, "purge", false)
  1386. }
  1387. if (overridePendingDelete || purge) && !db.IsAdminAllowDelete(ctx, userCred, self) {
  1388. return false
  1389. }
  1390. return self.IsOwner(userCred) || db.IsAdminAllowDelete(ctx, userCred, self)
  1391. }
  1392. func (self *SDisk) GetTemplateId() string {
  1393. if len(self.TemplateId) == 0 {
  1394. return ""
  1395. }
  1396. imageObj, err := CachedimageManager.FetchById(self.TemplateId)
  1397. if err != nil || imageObj == nil {
  1398. log.Errorf("failed to found disk %s(%s) templateId %s: %s", self.Name, self.Id, self.TemplateId, err)
  1399. return ""
  1400. }
  1401. return self.TemplateId
  1402. }
  1403. func (self *SDisk) IsLocal() bool {
  1404. storage, _ := self.GetStorage()
  1405. if storage != nil {
  1406. return storage.IsLocal()
  1407. }
  1408. return false
  1409. }
  1410. func (self *SDisk) GetCloudproviderId() string {
  1411. storage, _ := self.GetStorage()
  1412. if storage != nil {
  1413. return storage.GetCloudproviderId()
  1414. }
  1415. return ""
  1416. }
  1417. func (self *SDisk) GetStorage() (*SStorage, error) {
  1418. store, err := StorageManager.FetchById(self.StorageId)
  1419. if err != nil {
  1420. return nil, errors.Wrapf(err, "GetStorage(%s)", self.StorageId)
  1421. }
  1422. return store.(*SStorage), nil
  1423. }
  1424. func (self *SDisk) GetRegionDriver() (IRegionDriver, error) {
  1425. storage, _ := self.GetStorage()
  1426. if storage == nil {
  1427. return nil, fmt.Errorf("failed to found storage for disk %s(%s)", self.Name, self.Id)
  1428. }
  1429. return storage.GetRegionDriver()
  1430. }
  1431. func (self *SDisk) GetBackupStorage() *SStorage {
  1432. if len(self.BackupStorageId) == 0 {
  1433. return nil
  1434. }
  1435. store, _ := StorageManager.FetchById(self.BackupStorageId)
  1436. if store != nil {
  1437. return store.(*SStorage)
  1438. }
  1439. return nil
  1440. }
  1441. func (self *SDisk) GetCloudprovider() *SCloudprovider {
  1442. if storage, _ := self.GetStorage(); storage != nil {
  1443. return storage.GetCloudprovider()
  1444. }
  1445. return nil
  1446. }
  1447. func (self *SDisk) GetPathAtHost(host *SHost) string {
  1448. hostStorage := host.GetHoststorageOfId(self.StorageId)
  1449. if hostStorage != nil {
  1450. return path.Join(hostStorage.MountPoint, self.Id)
  1451. } else if len(self.BackupStorageId) > 0 {
  1452. hostStorage = host.GetHoststorageOfId(self.BackupStorageId)
  1453. if hostStorage != nil {
  1454. return path.Join(hostStorage.MountPoint, self.Id)
  1455. }
  1456. }
  1457. return ""
  1458. }
  1459. func (disk *SDisk) getCandidateHostIds() ([]string, error) {
  1460. hss, err := HoststorageManager.GetHostStoragesByStorageId(disk.StorageId)
  1461. if err != nil {
  1462. return nil, errors.Wrap(err, "GetHostStoragesByStorageId")
  1463. }
  1464. candidates := make([]string, 0)
  1465. for i := range hss {
  1466. candidates = append(candidates, hss[i].HostId)
  1467. }
  1468. return candidates, nil
  1469. }
  1470. func (self *SDisk) GetMasterHost(storage *SStorage) (*SHost, error) {
  1471. if storage.StorageType == api.STORAGE_SLVM {
  1472. if guest := self.GetGuest(); guest != nil {
  1473. return guest.GetHost()
  1474. }
  1475. }
  1476. if storage.MasterHost != "" {
  1477. return storage.GetMasterHost()
  1478. }
  1479. hosts := HostManager.Query().SubQuery()
  1480. hoststorages := HoststorageManager.Query().SubQuery()
  1481. q := hosts.Query().Join(hoststorages, sqlchemy.Equals(hoststorages.Field("host_id"), hosts.Field("id")))
  1482. q = q.Filter(sqlchemy.Equals(hoststorages.Field("storage_id"), self.StorageId))
  1483. q = q.IsTrue("enabled")
  1484. q = q.Equals("host_status", api.HOST_ONLINE).Asc("id")
  1485. guest := self.GetGuest()
  1486. if guest != nil && len(guest.OsArch) > 0 {
  1487. switch guest.OsArch {
  1488. case apis.OS_ARCH_X86:
  1489. q = q.In("cpu_architecture", apis.ARCH_X86)
  1490. case apis.OS_ARCH_ARM:
  1491. q = q.In("cpu_architecture", apis.ARCH_ARM)
  1492. case apis.OS_ARCH_RISCV:
  1493. q = q.In("cpu_architecture", apis.ARCH_RISCV)
  1494. }
  1495. }
  1496. host := SHost{}
  1497. host.SetModelManager(HostManager, &host)
  1498. err := q.First(&host)
  1499. if err != nil {
  1500. return nil, errors.Wrapf(err, "q.First")
  1501. }
  1502. return &host, nil
  1503. }
  1504. func (self *SDisk) GetFetchUrl() (string, error) {
  1505. storage, err := self.GetStorage()
  1506. if err != nil {
  1507. return "", errors.Wrapf(err, "self.GetStorage")
  1508. }
  1509. host, err := storage.GetMasterHost()
  1510. if err != nil {
  1511. return "", errors.Wrapf(err, "storage.GetMasterHost")
  1512. }
  1513. return fmt.Sprintf("%s/disks/%s", host.GetFetchUrl(true), self.Id), nil
  1514. }
  1515. func (self *SDisk) GetFsFormat() string {
  1516. return self.FsFormat
  1517. }
  1518. func (self *SDisk) GetCacheImageFormat(ctx context.Context) (string, error) {
  1519. if self.TemplateId != "" {
  1520. s := auth.GetAdminSession(ctx, options.Options.Region)
  1521. img, err := image.Images.Get(s, self.TemplateId, nil)
  1522. if err == nil {
  1523. diskFmt, err := img.GetString("disk_format")
  1524. if err != nil {
  1525. return "", errors.Wrapf(err, "not found disk_format of image %s", self.TemplateId)
  1526. }
  1527. if diskFmt == imageapi.IMAGE_DISK_FORMAT_TGZ {
  1528. return imageapi.IMAGE_DISK_FORMAT_TGZ, nil
  1529. }
  1530. }
  1531. }
  1532. if self.DiskFormat == "raw" {
  1533. return "qcow2", nil
  1534. }
  1535. return self.DiskFormat, nil
  1536. }
  1537. func (manager *SDiskManager) getDisksByStorage(storage *SStorage) ([]SDisk, error) {
  1538. disks := make([]SDisk, 0)
  1539. q := manager.Query().Equals("storage_id", storage.Id)
  1540. err := db.FetchModelObjects(manager, q, &disks)
  1541. if err != nil {
  1542. log.Errorf("%s", err)
  1543. return nil, err
  1544. }
  1545. return disks, nil
  1546. }
  1547. func (manager *SDiskManager) findOrCreateDisk(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, vdisk cloudprovider.ICloudDisk, index int, syncOwnerId mcclient.IIdentityProvider, managerId string) (*SDisk, error) {
  1548. diskId := vdisk.GetGlobalId()
  1549. diskObj, err := db.FetchByExternalIdAndManagerId(manager, diskId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1550. sq := StorageManager.Query().SubQuery()
  1551. return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("storage_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), managerId))
  1552. })
  1553. if err != nil {
  1554. if errors.Cause(err) != sql.ErrNoRows {
  1555. return nil, errors.Wrapf(err, "db.FetchByExternalIdAndManagerId %s", diskId)
  1556. }
  1557. vstorage, err := vdisk.GetIStorage()
  1558. if err != nil {
  1559. return nil, errors.Wrapf(err, "unable to GetIStorage of vdisk %q", vdisk.GetName())
  1560. }
  1561. storageObj, err := db.FetchByExternalIdAndManagerId(StorageManager, vstorage.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1562. return q.Equals("manager_id", managerId)
  1563. })
  1564. if err != nil {
  1565. return nil, errors.Wrapf(err, "cannot find storage of vdisk %s", vdisk.GetName())
  1566. }
  1567. storage := storageObj.(*SStorage)
  1568. return manager.newFromCloudDisk(ctx, userCred, provider, vdisk, storage, -1, syncOwnerId)
  1569. }
  1570. return diskObj.(*SDisk), nil
  1571. }
  1572. func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, storage *SStorage, disks []cloudprovider.ICloudDisk, syncOwnerId mcclient.IIdentityProvider, xor bool) ([]SDisk, []cloudprovider.ICloudDisk, compare.SyncResult) {
  1573. lockman.LockRawObject(ctx, manager.Keyword(), storage.Id)
  1574. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), storage.Id)
  1575. localDisks := make([]SDisk, 0)
  1576. remoteDisks := make([]cloudprovider.ICloudDisk, 0)
  1577. syncResult := compare.SyncResult{}
  1578. dbDisks, err := manager.getDisksByStorage(storage)
  1579. if err != nil {
  1580. syncResult.Error(err)
  1581. return nil, nil, syncResult
  1582. }
  1583. removed := make([]SDisk, 0)
  1584. commondb := make([]SDisk, 0)
  1585. commonext := make([]cloudprovider.ICloudDisk, 0)
  1586. added := make([]cloudprovider.ICloudDisk, 0)
  1587. err = compare.CompareSets(dbDisks, disks, &removed, &commondb, &commonext, &added)
  1588. if err != nil {
  1589. syncResult.Error(err)
  1590. return nil, nil, syncResult
  1591. }
  1592. for i := 0; i < len(removed); i += 1 {
  1593. err = removed[i].syncRemoveCloudDisk(ctx, userCred)
  1594. if err != nil {
  1595. // vm not sync, so skip disk used by vm error
  1596. if errors.Cause(err) == httperrors.ErrNotEmpty {
  1597. continue
  1598. }
  1599. syncResult.DeleteError(err)
  1600. } else {
  1601. syncResult.Delete()
  1602. }
  1603. }
  1604. for i := 0; i < len(commondb); i += 1 {
  1605. guest := commondb[i].GetGuest()
  1606. // 仅独立磁盘判断是否需要通过标签跳过同步,避免虚拟机有标签,磁盘没标签导致磁盘不断同步删除后再同步
  1607. if gotypes.IsNil(guest) {
  1608. skip, key := IsNeedSkipSync(commonext[i])
  1609. if skip {
  1610. log.Infof("delete disk %s(%s) with tag key or value: %s", commonext[i].GetName(), commonext[i].GetGlobalId(), key)
  1611. err := commondb[i].RealDelete(ctx, userCred)
  1612. if err != nil {
  1613. syncResult.DeleteError(err)
  1614. continue
  1615. }
  1616. syncResult.Delete()
  1617. continue
  1618. }
  1619. }
  1620. if !xor {
  1621. err = commondb[i].syncWithCloudDisk(ctx, userCred, provider, commonext[i], -1, syncOwnerId, storage.ManagerId)
  1622. if err != nil {
  1623. syncResult.UpdateError(err)
  1624. continue
  1625. }
  1626. }
  1627. localDisks = append(localDisks, commondb[i])
  1628. remoteDisks = append(remoteDisks, commonext[i])
  1629. syncResult.Update()
  1630. }
  1631. for i := 0; i < len(added); i += 1 {
  1632. skip, key := IsNeedSkipSync(added[i])
  1633. if skip {
  1634. log.Infof("skip disk %s(%s) sync with tag key or value: %s", added[i].GetName(), added[i].GetGlobalId(), key)
  1635. continue
  1636. }
  1637. extId := added[i].GetGlobalId()
  1638. _disk, err := db.FetchByExternalIdAndManagerId(manager, extId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1639. sq := StorageManager.Query().SubQuery()
  1640. return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("storage_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), storage.ManagerId))
  1641. })
  1642. if err != nil && err != sql.ErrNoRows {
  1643. //主要是显示duplicate err及 general err,方便排错
  1644. msg := fmt.Errorf("failed to found disk by external Id %s error: %v", extId, err)
  1645. syncResult.Error(msg)
  1646. continue
  1647. }
  1648. if _disk != nil {
  1649. disk := _disk.(*SDisk)
  1650. err = disk.syncDiskStorage(ctx, userCred, added[i], storage.ManagerId)
  1651. if err != nil {
  1652. syncResult.UpdateError(err)
  1653. } else {
  1654. syncResult.Update()
  1655. }
  1656. continue
  1657. }
  1658. new, err := manager.newFromCloudDisk(ctx, userCred, provider, added[i], storage, -1, syncOwnerId)
  1659. if err != nil {
  1660. syncResult.AddError(err)
  1661. } else {
  1662. localDisks = append(localDisks, *new)
  1663. remoteDisks = append(remoteDisks, added[i])
  1664. syncResult.Add()
  1665. }
  1666. }
  1667. return localDisks, remoteDisks, syncResult
  1668. }
  1669. func (self *SDisk) syncDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, idisk cloudprovider.ICloudDisk, managerId string) error {
  1670. istorage, err := idisk.GetIStorage()
  1671. if err != nil {
  1672. return errors.Wrapf(err, "idisk.GetIStorage %s", idisk.GetGlobalId())
  1673. }
  1674. storageExtId := istorage.GetGlobalId()
  1675. storage, err := db.FetchByExternalIdAndManagerId(StorageManager, storageExtId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1676. return q.Equals("manager_id", managerId)
  1677. })
  1678. if err != nil {
  1679. return errors.Wrapf(err, "storage db.FetchByExternalIdAndManagerId(%s)", storageExtId)
  1680. }
  1681. diff, err := db.UpdateWithLock(ctx, self, func() error {
  1682. self.StorageId = storage.GetId()
  1683. self.Status = idisk.GetStatus()
  1684. return nil
  1685. })
  1686. if err != nil {
  1687. return errors.Wrapf(err, "db.UpdateWithLock")
  1688. }
  1689. db.OpsLog.LogSyncUpdate(self, diff, userCred)
  1690. return nil
  1691. }
  1692. func (self *SDisk) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
  1693. storage, _ := self.GetStorage()
  1694. if storage == nil {
  1695. return nil, fmt.Errorf("failed to get storage for disk %s(%s)", self.Name, self.Id)
  1696. }
  1697. provider, err := storage.GetDriver(ctx)
  1698. if err != nil {
  1699. return nil, fmt.Errorf("No cloudprovider for storage %s(%s) error: %v", storage.Name, storage.Id, err)
  1700. }
  1701. if provider.GetFactory().IsOnPremise() {
  1702. return provider.GetOnPremiseIRegion()
  1703. }
  1704. region, err := storage.GetRegion()
  1705. if err != nil {
  1706. return nil, err
  1707. }
  1708. return provider.GetIRegionById(region.ExternalId)
  1709. }
  1710. func (self *SDisk) syncRemoveCloudDisk(ctx context.Context, userCred mcclient.TokenCredential) error {
  1711. lockman.LockObject(ctx, self)
  1712. defer lockman.ReleaseObject(ctx, self)
  1713. iregion, err := self.GetIRegion(ctx)
  1714. if err != nil {
  1715. return err
  1716. }
  1717. iDisk, err := iregion.GetIDiskById(self.ExternalId)
  1718. if err == nil {
  1719. if storageId := iDisk.GetIStorageId(); len(storageId) > 0 {
  1720. storage, err := db.FetchByExternalIdAndManagerId(StorageManager, storageId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1721. if s, _ := self.GetStorage(); s != nil {
  1722. return q.Equals("manager_id", s.ManagerId)
  1723. }
  1724. return q
  1725. })
  1726. if err == nil {
  1727. _, err = db.Update(self, func() error {
  1728. self.StorageId = storage.GetId()
  1729. return nil
  1730. })
  1731. return err
  1732. }
  1733. }
  1734. } else if errors.Cause(err) != cloudprovider.ErrNotFound {
  1735. return err
  1736. }
  1737. err = self.validateDeleteCondition(ctx, true)
  1738. if err != nil {
  1739. self.SetStatus(ctx, userCred, api.DISK_UNKNOWN, "missing original disk after sync")
  1740. return err
  1741. }
  1742. err = self.RealDelete(ctx, userCred)
  1743. if err != nil {
  1744. return err
  1745. }
  1746. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  1747. Obj: self,
  1748. Action: notifyclient.ActionSyncDelete,
  1749. })
  1750. return nil
  1751. }
  1752. func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, extDisk cloudprovider.ICloudDisk, index int, syncOwnerId mcclient.IIdentityProvider, managerId string) error {
  1753. recycle := false
  1754. guests := self.GetGuests()
  1755. if provider.GetFactory().IsSupportPrepaidResources() && len(guests) == 1 && guests[0].IsPrepaidRecycle() {
  1756. recycle = true
  1757. }
  1758. diff, err := db.UpdateWithLock(ctx, self, func() error {
  1759. if options.Options.EnableSyncName {
  1760. newName, _ := db.GenerateAlterName(self, extDisk.GetName())
  1761. if len(newName) > 0 {
  1762. self.Name = newName
  1763. }
  1764. }
  1765. self.Status = extDisk.GetStatus()
  1766. self.DiskFormat = extDisk.GetDiskFormat()
  1767. self.DiskSize = extDisk.GetDiskSizeMB()
  1768. self.AccessPath = extDisk.GetAccessPath()
  1769. self.Preallocation = extDisk.GetPreallocation()
  1770. if iops := extDisk.GetIops(); iops > 0 {
  1771. self.Iops = iops
  1772. }
  1773. if extDisk.GetIsAutoDelete() {
  1774. self.AutoDelete = true
  1775. }
  1776. if device := extDisk.GetDeviceName(); len(device) > 0 {
  1777. self.Device = device
  1778. }
  1779. // self.TemplateId = extDisk.GetTemplateId() no sync template ID
  1780. if templateId := extDisk.GetTemplateId(); len(templateId) > 0 {
  1781. cachedImage, err := db.FetchByExternalId(CachedimageManager, templateId)
  1782. if err == nil && cachedImage != nil {
  1783. self.TemplateId = cachedImage.GetId()
  1784. }
  1785. }
  1786. self.DiskType = extDisk.GetDiskType()
  1787. if index == 0 {
  1788. self.DiskType = api.DISK_TYPE_SYS
  1789. }
  1790. // self.FsFormat = extDisk.GetFsFormat()
  1791. self.Nonpersistent = extDisk.GetIsNonPersistent()
  1792. self.IsEmulated = extDisk.IsEmulated()
  1793. if provider.GetFactory().IsSupportPrepaidResources() && !recycle {
  1794. if billintType := extDisk.GetBillingType(); len(billintType) > 0 {
  1795. self.BillingType = billing_api.TBillingType(extDisk.GetBillingType())
  1796. self.ExpiredAt = time.Time{}
  1797. self.AutoRenew = false
  1798. if self.BillingType == billing_api.BILLING_TYPE_PREPAID {
  1799. self.ExpiredAt = extDisk.GetExpiredAt()
  1800. self.AutoRenew = extDisk.IsAutoRenew()
  1801. }
  1802. }
  1803. }
  1804. if createdAt := extDisk.GetCreatedAt(); !createdAt.IsZero() {
  1805. self.CreatedAt = createdAt
  1806. }
  1807. return nil
  1808. })
  1809. if err != nil {
  1810. return errors.Wrapf(err, "db.UpdateWithLock")
  1811. }
  1812. storage, err := self.GetStorage()
  1813. if err != nil {
  1814. return errors.Wrapf(err, "GetStorage")
  1815. }
  1816. db.OpsLog.LogSyncUpdate(self, diff, userCred)
  1817. if len(diff) > 0 {
  1818. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  1819. Obj: self,
  1820. Action: notifyclient.ActionSyncUpdate,
  1821. })
  1822. }
  1823. if account := storage.GetCloudaccount(); account != nil {
  1824. syncVirtualResourceMetadata(ctx, userCred, self, extDisk, account.ReadOnly)
  1825. }
  1826. if len(guests) == 0 {
  1827. if provider := storage.GetCloudprovider(); provider != nil {
  1828. SyncCloudProject(ctx, userCred, self, syncOwnerId, extDisk, provider)
  1829. }
  1830. } else {
  1831. self.SyncCloudProjectId(userCred, guests[0].GetOwnerId())
  1832. }
  1833. return nil
  1834. }
  1835. func (manager *SDiskManager) newFromCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, extDisk cloudprovider.ICloudDisk, storage *SStorage, index int, syncOwnerId mcclient.IIdentityProvider) (*SDisk, error) {
  1836. disk := SDisk{}
  1837. disk.SetModelManager(manager, &disk)
  1838. disk.Status = extDisk.GetStatus()
  1839. disk.ExternalId = extDisk.GetGlobalId()
  1840. disk.StorageId = storage.Id
  1841. disk.Iops = extDisk.GetIops()
  1842. disk.DiskFormat = extDisk.GetDiskFormat()
  1843. disk.DiskSize = extDisk.GetDiskSizeMB()
  1844. disk.AutoDelete = extDisk.GetIsAutoDelete()
  1845. disk.Preallocation = extDisk.GetPreallocation()
  1846. disk.DiskType = extDisk.GetDiskType()
  1847. if index == 0 {
  1848. disk.DiskType = api.DISK_TYPE_SYS
  1849. }
  1850. disk.Nonpersistent = extDisk.GetIsNonPersistent()
  1851. disk.Device = extDisk.GetDeviceName()
  1852. disk.IsEmulated = extDisk.IsEmulated()
  1853. if templateId := extDisk.GetTemplateId(); len(templateId) > 0 {
  1854. cachedImage, err := db.FetchByExternalId(CachedimageManager, templateId)
  1855. if err == nil && cachedImage != nil {
  1856. disk.TemplateId = cachedImage.GetId()
  1857. }
  1858. }
  1859. if provider.GetFactory().IsSupportPrepaidResources() {
  1860. disk.BillingType = billing_api.TBillingType(extDisk.GetBillingType())
  1861. if expired := extDisk.GetExpiredAt(); !expired.IsZero() {
  1862. disk.ExpiredAt = expired
  1863. }
  1864. disk.AutoRenew = extDisk.IsAutoRenew()
  1865. }
  1866. if createAt := extDisk.GetCreatedAt(); !createAt.IsZero() {
  1867. disk.CreatedAt = createAt
  1868. }
  1869. var err = func() error {
  1870. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  1871. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  1872. newName, err := db.GenerateName(ctx, manager, syncOwnerId, extDisk.GetName())
  1873. if err != nil {
  1874. return err
  1875. }
  1876. disk.Name = newName
  1877. return manager.TableSpec().Insert(ctx, &disk)
  1878. }()
  1879. if err != nil {
  1880. return nil, errors.Wrapf(err, "newFromCloudDisk")
  1881. }
  1882. syncVirtualResourceMetadata(ctx, userCred, &disk, extDisk, false)
  1883. if provider := storage.GetCloudprovider(); provider != nil {
  1884. SyncCloudProject(ctx, userCred, &disk, syncOwnerId, extDisk, provider)
  1885. }
  1886. db.OpsLog.LogEvent(&disk, db.ACT_CREATE, disk.GetShortDesc(ctx), userCred)
  1887. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  1888. Obj: &disk,
  1889. Action: notifyclient.ActionSyncCreate,
  1890. })
  1891. return &disk, nil
  1892. }
  1893. func totalDiskSize(
  1894. scope rbacscope.TRbacScope,
  1895. ownerId mcclient.IIdentityProvider,
  1896. active tristate.TriState,
  1897. ready tristate.TriState,
  1898. includeSystem bool,
  1899. pendingDelete bool,
  1900. rangeObjs []db.IStandaloneModel,
  1901. providers []string,
  1902. brands []string,
  1903. cloudEnv string,
  1904. hypervisors []string,
  1905. ) int {
  1906. disks := DiskManager.Query().SubQuery()
  1907. q := disks.Query(sqlchemy.SUM("total", disks.Field("disk_size")))
  1908. storages := StorageManager.Query().SubQuery()
  1909. q = q.Join(storages, sqlchemy.Equals(storages.Field("id"), disks.Field("storage_id")))
  1910. q = CloudProviderFilter(q, storages.Field("manager_id"), providers, brands, cloudEnv)
  1911. q = RangeObjectsFilter(q, rangeObjs, nil, storages.Field("zone_id"), storages.Field("manager_id"), nil, storages.Field("id"))
  1912. if len(hypervisors) > 0 {
  1913. hoststorages := HoststorageManager.Query().SubQuery()
  1914. hosts := HostManager.Query().SubQuery()
  1915. q = q.Join(hoststorages, sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")))
  1916. q = q.Join(hosts, sqlchemy.Equals(hoststorages.Field("host_id"), hosts.Field("id")))
  1917. q = q.Filter(sqlchemy.In(hosts.Field("host_type"), Hypervisors2HostTypes(hypervisors)))
  1918. }
  1919. if !active.IsNone() {
  1920. if active.IsTrue() {
  1921. q = q.Filter(sqlchemy.In(storages.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}))
  1922. } else {
  1923. q = q.Filter(sqlchemy.NotIn(storages.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}))
  1924. }
  1925. }
  1926. switch scope {
  1927. case rbacscope.ScopeSystem:
  1928. // do nothing
  1929. case rbacscope.ScopeDomain:
  1930. q = q.Filter(sqlchemy.Equals(disks.Field("domain_id"), ownerId.GetProjectDomainId()))
  1931. case rbacscope.ScopeProject:
  1932. q = q.Filter(sqlchemy.Equals(disks.Field("tenant_id"), ownerId.GetProjectId()))
  1933. }
  1934. if !ready.IsNone() {
  1935. if ready.IsTrue() {
  1936. q = q.Filter(sqlchemy.Equals(disks.Field("status"), api.DISK_READY))
  1937. } else {
  1938. q = q.Filter(sqlchemy.NotEquals(disks.Field("status"), api.DISK_READY))
  1939. }
  1940. }
  1941. if !includeSystem {
  1942. q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(disks.Field("is_system")),
  1943. sqlchemy.IsFalse(disks.Field("is_system"))))
  1944. }
  1945. if pendingDelete {
  1946. q = q.Filter(sqlchemy.IsTrue(disks.Field("pending_deleted")))
  1947. } else {
  1948. q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(disks.Field("pending_deleted")), sqlchemy.IsFalse(disks.Field("pending_deleted"))))
  1949. }
  1950. row := q.Row()
  1951. size := sql.NullInt64{}
  1952. err := row.Scan(&size)
  1953. if err != nil {
  1954. log.Errorf("totalDiskSize error %s: %s", err, q.String())
  1955. return 0
  1956. }
  1957. if size.Valid {
  1958. return int(size.Int64)
  1959. } else {
  1960. return 0
  1961. }
  1962. }
  1963. func parseDiskInfo(ctx context.Context, userCred mcclient.TokenCredential, info *api.DiskConfig) (*api.DiskConfig, error) {
  1964. if info.Storage != "" {
  1965. if err := fillDiskConfigByStorage(ctx, userCred, info, info.Storage); err != nil {
  1966. return nil, errors.Wrap(err, "fillDiskConfigByStorage")
  1967. }
  1968. }
  1969. if info.DiskId != "" {
  1970. if err := fillDiskConfigByDisk(ctx, userCred, info, info.DiskId); err != nil {
  1971. return nil, errors.Wrap(err, "fillDiskConfigByDisk")
  1972. }
  1973. }
  1974. if info.SnapshotId != "" {
  1975. if err := fillDiskConfigBySnapshot(ctx, userCred, info, info.SnapshotId); err != nil {
  1976. return nil, errors.Wrap(err, "fillDiskConfigBySnapshot")
  1977. }
  1978. }
  1979. if info.BackupId != "" {
  1980. if err := fillDiskConfigByBackup(ctx, userCred, info, info.BackupId); err != nil {
  1981. return nil, errors.Wrap(err, "fillDiskConfigByBackup")
  1982. }
  1983. }
  1984. if info.ImageId != "" {
  1985. if err := fillDiskConfigByImage(ctx, userCred, info, info.ImageId); err != nil {
  1986. if len(info.SnapshotId) == 0 && len(info.BackupId) == 0 {
  1987. // return error only if no valid snapshotId and backId
  1988. // otherwise, the disk was crated by snapshot or backup, not depends on vald image info
  1989. return nil, errors.Wrap(err, "fillDiskConfigByImage")
  1990. }
  1991. }
  1992. }
  1993. if info.ExistingPath != "" {
  1994. info.ExistingPath = strings.TrimSpace(info.ExistingPath)
  1995. _, err := filepath.Rel("/", info.ExistingPath)
  1996. if err != nil {
  1997. return nil, errors.Wrap(err, "invaild existing path")
  1998. }
  1999. }
  2000. // XXX: do not set default disk size here, set it by each hypervisor driver
  2001. // if len(diskConfig.ImageId) > 0 && diskConfig.SizeMb == 0 {
  2002. // diskConfig.SizeMb = options.Options.DefaultDiskSize // MB
  2003. // else
  2004. if len(info.ImageId) == 0 && info.SizeMb == 0 && info.ExistingPath == "" && info.NVMEDevice == nil {
  2005. return nil, httperrors.NewInputParameterError("Diskinfo index %d: both imageID and size are absent", info.Index)
  2006. }
  2007. return info, nil
  2008. }
  2009. func fillDiskConfigBySnapshot(ctx context.Context, userCred mcclient.TokenCredential, diskConfig *api.DiskConfig, snapshotId string) error {
  2010. iSnapshot, err := SnapshotManager.FetchByIdOrName(ctx, userCred, snapshotId)
  2011. if err != nil {
  2012. if err == sql.ErrNoRows {
  2013. return httperrors.NewNotFoundError("Snapshot %s not found", snapshotId)
  2014. }
  2015. return err
  2016. }
  2017. var snapshot = iSnapshot.(*SSnapshot)
  2018. if storage := StorageManager.FetchStorageById(snapshot.StorageId); storage == nil {
  2019. return httperrors.NewBadRequestError("Snapshot %s storage %s not found, is public cloud?",
  2020. snapshotId, snapshot.StorageId)
  2021. } else {
  2022. if disk := DiskManager.FetchDiskById(snapshot.DiskId); disk != nil {
  2023. diskConfig.Fs = disk.FsFormat
  2024. if len(diskConfig.Format) == 0 {
  2025. diskConfig.Format = disk.DiskFormat
  2026. }
  2027. }
  2028. diskConfig.SnapshotId = snapshot.Id
  2029. diskConfig.DiskType = snapshot.DiskType
  2030. diskConfig.SizeMb = snapshot.VirtualSize
  2031. diskConfig.Backend = storage.StorageType
  2032. diskConfig.Fs = ""
  2033. diskConfig.Mountpoint = ""
  2034. diskConfig.OsArch = snapshot.OsArch
  2035. }
  2036. return nil
  2037. }
  2038. func fillDiskConfigByBackup(ctx context.Context, userCred mcclient.TokenCredential, diskConfig *api.DiskConfig, backupId string) error {
  2039. iBakcup, err := DiskBackupManager.FetchByIdOrName(ctx, userCred, backupId)
  2040. if err != nil {
  2041. if err == sql.ErrNoRows {
  2042. return httperrors.NewNotFoundError("Backup %s not found", backupId)
  2043. }
  2044. return err
  2045. }
  2046. backup := iBakcup.(*SDiskBackup)
  2047. if diskConfig.DiskType == "" {
  2048. diskConfig.DiskType = backup.DiskType
  2049. }
  2050. diskConfig.BackupId = backup.GetId()
  2051. return nil
  2052. }
  2053. func fillDiskConfigByImage(ctx context.Context, userCred mcclient.TokenCredential,
  2054. diskConfig *api.DiskConfig, imageId string) error {
  2055. if userCred == nil {
  2056. diskConfig.ImageId = imageId
  2057. } else {
  2058. image, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
  2059. if err != nil {
  2060. log.Errorf("getImageInfo %s fail %s", imageId, err)
  2061. return err
  2062. }
  2063. if image.Status != cloudprovider.IMAGE_STATUS_ACTIVE {
  2064. return httperrors.NewInvalidStatusError("Image status is not active")
  2065. }
  2066. diskConfig.ImageId = image.Id
  2067. diskConfig.ImageEncryptKeyId = image.EncryptKeyId
  2068. diskConfig.ImageProperties = image.Properties
  2069. diskConfig.ImageProperties[imageapi.IMAGE_DISK_FORMAT] = image.DiskFormat
  2070. // if len(diskConfig.Format) == 0 {
  2071. // diskConfig.Format = image.DiskFormat
  2072. // }
  2073. // diskConfig.ImageDiskFormat = image.DiskFormat
  2074. CachedimageManager.ImageAddRefCount(image.Id)
  2075. if diskConfig.SizeMb != api.DISK_SIZE_AUTOEXTEND && diskConfig.SizeMb < image.MinDiskMB {
  2076. diskConfig.SizeMb = image.MinDiskMB // MB
  2077. }
  2078. if strings.Contains(image.Properties["os_arch"], "aarch") {
  2079. diskConfig.OsArch = apis.OS_ARCH_AARCH64
  2080. } else {
  2081. diskConfig.OsArch = image.Properties["os_arch"]
  2082. }
  2083. }
  2084. return nil
  2085. }
  2086. func fillDiskConfigByDisk(ctx context.Context, userCred mcclient.TokenCredential,
  2087. diskConfig *api.DiskConfig, diskId string) error {
  2088. diskObj, err := DiskManager.FetchByIdOrName(ctx, userCred, diskId)
  2089. if err != nil {
  2090. if errors.Cause(err) == sql.ErrNoRows {
  2091. return httperrors.NewResourceNotFoundError2("disk", diskId)
  2092. } else {
  2093. return errors.Wrapf(err, "DiskManager.FetchByIdOrName %s", diskId)
  2094. }
  2095. }
  2096. disk := diskObj.(*SDisk)
  2097. if disk.Status != api.DISK_READY {
  2098. return errors.Wrapf(httperrors.ErrInvalidStatus, "disk status %s not ready", disk.Status)
  2099. }
  2100. guests := disk.GetGuests()
  2101. if len(guests) > 0 {
  2102. return errors.Wrapf(httperrors.ErrInvalidStatus, "disk %s has been used", diskId)
  2103. }
  2104. diskConfig.DiskId = disk.Id
  2105. diskConfig.SizeMb = disk.DiskSize
  2106. if disk.SnapshotId != "" {
  2107. diskConfig.SnapshotId = disk.SnapshotId
  2108. }
  2109. if disk.TemplateId != "" {
  2110. diskConfig.ImageId = disk.TemplateId
  2111. }
  2112. if disk.OsArch != "" {
  2113. diskConfig.OsArch = disk.OsArch
  2114. }
  2115. storage, err := disk.GetStorage()
  2116. if err != nil {
  2117. return errors.Wrap(err, "disk.GetStorage")
  2118. }
  2119. if !storage.Enabled.IsTrue() {
  2120. return errors.Wrap(httperrors.ErrInvalidStatus, "storage not enabled")
  2121. }
  2122. if storage.Status != api.STORAGE_ONLINE {
  2123. return errors.Wrap(httperrors.ErrInvalidStatus, "storage not online")
  2124. }
  2125. diskConfig.Storage = disk.StorageId
  2126. return nil
  2127. }
  2128. func fillDiskConfigByStorage(ctx context.Context, userCred mcclient.TokenCredential,
  2129. diskConfig *api.DiskConfig, storageId string) error {
  2130. storageObj, err := StorageManager.FetchByIdOrName(ctx, userCred, storageId)
  2131. if err != nil {
  2132. if errors.Cause(err) == sql.ErrNoRows {
  2133. return httperrors.NewResourceNotFoundError2("storage", storageId)
  2134. } else {
  2135. return errors.Wrapf(err, "StorageManager.FetchByIdOrName %s", storageId)
  2136. }
  2137. }
  2138. storage := storageObj.(*SStorage)
  2139. if !storage.Enabled.IsTrue() {
  2140. return errors.Wrap(httperrors.ErrInvalidStatus, "storage not enabled")
  2141. }
  2142. if storage.Status != api.STORAGE_ONLINE {
  2143. return errors.Wrap(httperrors.ErrInvalidStatus, "storage not online")
  2144. }
  2145. if storage.StorageType == api.STORAGE_NVME_PT {
  2146. return httperrors.NewBadRequestError("storage type %s require assign isolated device", api.STORAGE_NVME_PT)
  2147. }
  2148. diskConfig.Storage = storage.Id
  2149. diskConfig.Backend = storage.StorageType
  2150. return nil
  2151. }
  2152. func parseIsoInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string) (*cloudprovider.SImage, error) {
  2153. image, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
  2154. if err != nil {
  2155. log.Errorf("getImageInfo fail %s", err)
  2156. return nil, err
  2157. }
  2158. if image.Status != cloudprovider.IMAGE_STATUS_ACTIVE {
  2159. return nil, httperrors.NewInvalidStatusError("Image status is not active")
  2160. }
  2161. return image, nil
  2162. }
  2163. func (self *SDisk) fetchDiskInfo(diskConfig *api.DiskConfig) error {
  2164. if len(diskConfig.SnapshotId) > 0 {
  2165. self.SnapshotId = diskConfig.SnapshotId
  2166. self.DiskType = diskConfig.DiskType
  2167. }
  2168. if len(diskConfig.BackupId) > 0 {
  2169. self.BackupId = diskConfig.BackupId
  2170. self.DiskType = diskConfig.DiskType
  2171. }
  2172. if len(diskConfig.ImageId) > 0 {
  2173. self.TemplateId = diskConfig.ImageId
  2174. // support for create vm from guest image
  2175. if len(diskConfig.DiskType) == 0 {
  2176. self.DiskType = api.DISK_TYPE_SYS
  2177. } else {
  2178. self.DiskType = diskConfig.DiskType
  2179. }
  2180. }
  2181. if len(diskConfig.Fs) > 0 {
  2182. self.FsFormat = diskConfig.Fs
  2183. }
  2184. self.FsFeatures = diskConfig.FsFeatures
  2185. if err := DiskManager.ValidateFsFeatures(self.FsFormat, diskConfig.FsFeatures); err != nil {
  2186. return err
  2187. }
  2188. if self.FsFormat == "swap" {
  2189. self.DiskType = api.DISK_TYPE_SWAP
  2190. self.Nonpersistent = true
  2191. } else {
  2192. if len(self.DiskType) == 0 {
  2193. diskType := api.DISK_TYPE_DATA
  2194. if diskConfig.DiskType == api.DISK_TYPE_VOLUME {
  2195. diskType = api.DISK_TYPE_VOLUME
  2196. }
  2197. self.DiskType = diskType
  2198. }
  2199. self.Nonpersistent = false
  2200. }
  2201. if len(diskConfig.DiskId) > 0 && utils.IsMatchUUID(diskConfig.DiskId) {
  2202. self.Id = diskConfig.DiskId
  2203. }
  2204. self.DiskFormat = diskConfig.Format
  2205. self.DiskSize = diskConfig.SizeMb
  2206. self.OsArch = diskConfig.OsArch
  2207. return nil
  2208. }
  2209. type DiskInfo struct {
  2210. ImageId string
  2211. Fs string
  2212. MountPoint string
  2213. Format string
  2214. Size int64
  2215. Storage string
  2216. Backend string
  2217. MediumType string
  2218. Driver string
  2219. Cache string
  2220. DiskType string
  2221. }
  2222. // DEPRECATE: will be remove in future, use ToDiskConfig
  2223. func (self *SDisk) ToDiskInfo() DiskInfo {
  2224. ret := DiskInfo{
  2225. ImageId: self.GetTemplateId(),
  2226. Fs: self.GetFsFormat(),
  2227. MountPoint: self.GetMountPoint(),
  2228. Format: self.DiskFormat,
  2229. Size: int64(self.DiskSize),
  2230. DiskType: self.DiskType,
  2231. }
  2232. storage, _ := self.GetStorage()
  2233. if storage == nil {
  2234. return ret
  2235. }
  2236. ret.Storage = storage.Id
  2237. ret.Backend = storage.StorageType
  2238. ret.MediumType = storage.MediumType
  2239. return ret
  2240. }
  2241. func (self *SDisk) ToDiskConfig() *api.DiskConfig {
  2242. ret := &api.DiskConfig{
  2243. Index: -1,
  2244. ImageId: self.GetTemplateId(),
  2245. Fs: self.GetFsFormat(),
  2246. Mountpoint: self.GetMountPoint(),
  2247. Format: self.DiskFormat,
  2248. SizeMb: self.DiskSize,
  2249. DiskType: self.DiskType,
  2250. }
  2251. storage, _ := self.GetStorage()
  2252. if storage == nil {
  2253. return ret
  2254. }
  2255. ret.Storage = storage.Id
  2256. ret.Backend = storage.StorageType
  2257. ret.Medium = storage.MediumType
  2258. return ret
  2259. }
  2260. func (self *SDisk) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  2261. // override
  2262. log.Infof("disk delete do nothing")
  2263. return nil
  2264. }
  2265. func (self *SDisk) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  2266. // diskbackups := DiskBackupManager.Query("id").Equals("disk_id", self.Id)
  2267. guestdisks := GuestdiskManager.Query("row_id").Equals("disk_id", self.Id)
  2268. err := SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  2269. if err != nil {
  2270. return errors.Wrapf(err, "RemoveByResource")
  2271. }
  2272. pairs := []purgePair{
  2273. // {manager: DiskBackupManager, key: "id", q: diskbackups},
  2274. {manager: GuestdiskManager, key: "row_id", q: guestdisks},
  2275. }
  2276. for i := range pairs {
  2277. err := pairs[i].purgeAll(ctx)
  2278. if err != nil {
  2279. return err
  2280. }
  2281. }
  2282. SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  2283. return self.SVirtualResourceBase.Delete(ctx, userCred)
  2284. }
  2285. func (self *SDisk) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  2286. SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  2287. return self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
  2288. }
  2289. func (self *SDisk) RecordLastAttachedHost(ctx context.Context, userCred mcclient.TokenCredential, hostId string) error {
  2290. storage, err := self.GetStorage()
  2291. if err != nil {
  2292. return err
  2293. }
  2294. if storage.StorageType != api.STORAGE_SLVM {
  2295. return nil
  2296. }
  2297. return self.SetMetadata(ctx, api.DISK_META_LAST_ATTACHED_HOST, hostId, userCred)
  2298. }
  2299. func (self *SDisk) GetLastAttachedHost(ctx context.Context, userCred mcclient.TokenCredential) string {
  2300. return self.GetMetadata(ctx, api.DISK_META_LAST_ATTACHED_HOST, userCred)
  2301. }
  2302. func (self *SDisk) RecordDiskSnapshotsLastHost(ctx context.Context, userCred mcclient.TokenCredential, hostId string) error {
  2303. storage, err := self.GetStorage()
  2304. if err != nil {
  2305. return err
  2306. }
  2307. if storage.StorageType != api.STORAGE_SLVM {
  2308. return nil
  2309. }
  2310. // record disk snapshots master host
  2311. snaps := SnapshotManager.GetDiskSnapshots(self.Id)
  2312. for i := range snaps {
  2313. err = snaps[i].SetMetadata(ctx, api.DISK_META_LAST_ATTACHED_HOST, hostId, userCred)
  2314. if err != nil {
  2315. log.Errorf("snapshot %s failed set last attached host: %s", snaps[i].Id, err)
  2316. }
  2317. }
  2318. return nil
  2319. }
  2320. // 同步磁盘状态
  2321. func (self *SDisk) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskSyncstatusInput) (jsonutils.JSONObject, error) {
  2322. var openTask = true
  2323. count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
  2324. if err != nil {
  2325. return nil, err
  2326. }
  2327. if count > 0 {
  2328. return nil, httperrors.NewBadRequestError("Disk has %d task active, can't sync status", count)
  2329. }
  2330. return nil, self.StartSyncstatus(ctx, userCred, "")
  2331. }
  2332. func (disk *SDisk) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  2333. return StartResourceSyncStatusTask(ctx, userCred, disk, "DiskSyncstatusTask", parentTaskId)
  2334. }
  2335. func (self *SDisk) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  2336. err := self.validateDeleteCondition(ctx, true)
  2337. if err != nil {
  2338. return nil, err
  2339. }
  2340. provider := self.GetCloudprovider()
  2341. if provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
  2342. cnt, err := self.GetSnapshotCount()
  2343. if err != nil {
  2344. return nil, httperrors.NewInternalServerError("GetSnapshotCount fail %s", err)
  2345. }
  2346. if cnt > 0 {
  2347. return nil, httperrors.NewForbiddenError("not allow to purge. Virtual disk must not have snapshots")
  2348. }
  2349. }
  2350. return nil, self.StartDiskDeleteTask(ctx, userCred, "", true, false, false)
  2351. }
  2352. func (self *SDisk) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  2353. if !jsonutils.QueryBoolean(query, "delete_snapshots", false) {
  2354. if provider := self.GetCloudprovider(); provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
  2355. cnt, err := self.GetSnapshotCount()
  2356. if err != nil {
  2357. return httperrors.NewInternalServerError("GetSnapshotCount fail %s", err)
  2358. }
  2359. if cnt > 0 {
  2360. return httperrors.NewForbiddenError("not allow to delete. Virtual disk must not have snapshots")
  2361. }
  2362. } else if storage, _ := self.GetStorage(); storage != nil && storage.StorageType == api.STORAGE_RBD {
  2363. scnt, err := self.GetSnapshotCount()
  2364. if err != nil {
  2365. return err
  2366. }
  2367. if scnt > 0 {
  2368. return httperrors.NewBadRequestError("not allow to delete %s disk with snapshots", storage.StorageType)
  2369. }
  2370. }
  2371. }
  2372. return self.StartDiskDeleteTask(ctx, userCred, "", false,
  2373. jsonutils.QueryBoolean(query, "override_pending_delete", false),
  2374. jsonutils.QueryBoolean(query, "delete_snapshots", false))
  2375. }
  2376. func (manager *SDiskManager) FetchCustomizeColumns(
  2377. ctx context.Context,
  2378. userCred mcclient.TokenCredential,
  2379. query jsonutils.JSONObject,
  2380. objs []interface{},
  2381. fields stringutils2.SSortedStrings,
  2382. isList bool,
  2383. ) []api.DiskDetails {
  2384. rows := make([]api.DiskDetails, len(objs))
  2385. virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  2386. storeRows := manager.SStorageResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  2387. encRows := manager.SEncryptedResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  2388. diskIds := make([]string, len(objs))
  2389. for i := range rows {
  2390. rows[i] = api.DiskDetails{
  2391. VirtualResourceDetails: virtRows[i],
  2392. StorageResourceInfo: storeRows[i],
  2393. EncryptedResourceDetails: encRows[i],
  2394. }
  2395. disk := objs[i].(*SDisk)
  2396. if disk.PendingDeleted {
  2397. pendingDeletedAt := disk.PendingDeletedAt.Add(time.Second * time.Duration(options.Options.PendingDeleteExpireSeconds))
  2398. rows[i].AutoDeleteAt = pendingDeletedAt
  2399. }
  2400. diskIds[i] = disk.Id
  2401. }
  2402. guestSQ := GuestManager.Query().SubQuery()
  2403. gds := GuestdiskManager.Query().SubQuery()
  2404. q := guestSQ.Query(
  2405. guestSQ.Field("id"),
  2406. guestSQ.Field("name"),
  2407. guestSQ.Field("status"),
  2408. guestSQ.Field("billing_type"),
  2409. gds.Field("disk_id"),
  2410. gds.Field("index"),
  2411. gds.Field("driver"),
  2412. gds.Field("cache_mode"),
  2413. gds.Field("iops"),
  2414. gds.Field("bps"),
  2415. ).
  2416. Join(gds, sqlchemy.Equals(gds.Field("guest_id"), guestSQ.Field("id"))).
  2417. Filter(sqlchemy.In(gds.Field("disk_id"), diskIds))
  2418. guestInfo := []struct {
  2419. Id string
  2420. Name string
  2421. Status string
  2422. DiskId string
  2423. Index int
  2424. Driver string
  2425. CacheMode string
  2426. Iops int
  2427. Bps int
  2428. BillingType string
  2429. }{}
  2430. err := q.All(&guestInfo)
  2431. if err != nil {
  2432. log.Errorf("query disk guest info error: %v", err)
  2433. return rows
  2434. }
  2435. guests, guestIds := map[string][]api.SimpleGuest{}, []string{}
  2436. for _, guest := range guestInfo {
  2437. _, ok := guests[guest.DiskId]
  2438. if !ok {
  2439. guests[guest.DiskId] = []api.SimpleGuest{}
  2440. guestIds = append(guestIds, guest.Id)
  2441. }
  2442. guests[guest.DiskId] = append(guests[guest.DiskId], api.SimpleGuest{
  2443. Id: guest.Id,
  2444. Name: guest.Name,
  2445. Status: guest.Status,
  2446. Index: guest.Index,
  2447. Driver: guest.Driver,
  2448. CacheMode: guest.CacheMode,
  2449. Iops: guest.Iops,
  2450. Bps: guest.Bps,
  2451. BillingType: guest.BillingType,
  2452. })
  2453. }
  2454. policySQ := SnapshotPolicyManager.Query().SubQuery()
  2455. dps := SnapshotPolicyResourceManager.Query().SubQuery()
  2456. q = policySQ.Query(
  2457. policySQ.Field("id"),
  2458. policySQ.Field("name"),
  2459. policySQ.Field("time_points"),
  2460. policySQ.Field("repeat_weekdays"),
  2461. dps.Field("resource_id"),
  2462. dps.Field("resource_type"),
  2463. ).Join(dps, sqlchemy.Equals(dps.Field("snapshotpolicy_id"), policySQ.Field("id"))).
  2464. Filter(sqlchemy.OR(sqlchemy.In(dps.Field("resource_id"), diskIds), sqlchemy.In(dps.Field("resource_id"), guestIds)))
  2465. policyInfo := []struct {
  2466. Id string
  2467. Name string
  2468. Status string
  2469. TimePoints []int
  2470. RepeatWeekdays []int
  2471. ResourceId string
  2472. ResourceType string
  2473. }{}
  2474. err = q.All(&policyInfo)
  2475. if err != nil {
  2476. log.Errorf("query disk snapshot policy info error: %v", err)
  2477. return rows
  2478. }
  2479. policies := map[string][]api.SimpleSnapshotPolicy{}
  2480. for _, policy := range policyInfo {
  2481. _, ok := policies[policy.ResourceId]
  2482. if !ok {
  2483. policies[policy.ResourceId] = []api.SimpleSnapshotPolicy{}
  2484. }
  2485. policies[policy.ResourceId] = append(policies[policy.ResourceId], api.SimpleSnapshotPolicy{
  2486. Id: policy.Id,
  2487. Name: policy.Name,
  2488. RepeatWeekdays: policy.RepeatWeekdays,
  2489. TimePoints: policy.TimePoints,
  2490. ResourceType: policy.ResourceType,
  2491. })
  2492. }
  2493. for i := range rows {
  2494. rows[i].Guests, _ = guests[diskIds[i]]
  2495. names, status, billingTypes := []string{}, []string{}, []string{}
  2496. var iops, bps int
  2497. for j := range rows[i].Guests {
  2498. guest := rows[i].Guests[j]
  2499. names = append(names, guest.Name)
  2500. status = append(status, guest.Status)
  2501. iops = guest.Iops
  2502. bps = guest.Bps
  2503. billingTypes = append(billingTypes, guest.BillingType)
  2504. rows[i].Guests[j].Snapshotpolicy, _ = policies[guest.Id]
  2505. rows[i].GuestSnapshotpolicyCount += len(rows[i].Guests[j].Snapshotpolicy)
  2506. }
  2507. rows[i].GuestCount = len(rows[i].Guests)
  2508. rows[i].Guest = strings.Join(names, ",")
  2509. rows[i].GuestStatus = strings.Join(status, ",")
  2510. rows[i].GuestBillingType = strings.Join(billingTypes, ",")
  2511. rows[i].Snapshotpolicies, _ = policies[diskIds[i]]
  2512. disk := objs[i].(*SDisk)
  2513. if len(disk.StorageId) == 0 && disk.Status == api.VM_SCHEDULE_FAILED {
  2514. rows[i].Brand = "Unknown"
  2515. rows[i].Provider = "Unknown"
  2516. }
  2517. // 仅kvm使用
  2518. if len(rows[i].ManagerId) == 0 {
  2519. disk.Iops = iops
  2520. disk.Throughput = bps
  2521. }
  2522. }
  2523. return rows
  2524. }
  2525. func (self *SDisk) StartDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota) error {
  2526. self.SetStatus(ctx, userCred, api.DISK_START_RESIZE, "StartDiskResizeTask")
  2527. params := jsonutils.NewDict()
  2528. params.Add(jsonutils.NewInt(sizeMb), "size")
  2529. task, err := taskman.TaskManager.NewTask(ctx, "DiskResizeTask", self, userCred, params, parentTaskId, "", pendingUsage)
  2530. if err != nil {
  2531. return err
  2532. }
  2533. task.ScheduleRun(nil)
  2534. return nil
  2535. }
  2536. func (self *SDisk) StartDiskDeleteTask(
  2537. ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string,
  2538. isPurge, overridePendingDelete, deleteSnapshots bool,
  2539. ) error {
  2540. params := jsonutils.NewDict()
  2541. if isPurge {
  2542. params.Add(jsonutils.JSONTrue, "purge")
  2543. }
  2544. if overridePendingDelete {
  2545. params.Add(jsonutils.JSONTrue, "override_pending_delete")
  2546. }
  2547. if deleteSnapshots {
  2548. params.Add(jsonutils.JSONTrue, "delete_snapshots")
  2549. }
  2550. task, err := taskman.TaskManager.NewTask(ctx, "DiskDeleteTask", self, userCred, params, parentTaskId, "", nil)
  2551. if err != nil {
  2552. log.Errorf("%s", err)
  2553. return err
  2554. }
  2555. task.ScheduleRun(nil)
  2556. return nil
  2557. }
  2558. func (self *SDisk) GetAttachedGuests() []SGuest {
  2559. guests := GuestManager.Query().SubQuery()
  2560. guestdisks := GuestdiskManager.Query().SubQuery()
  2561. q := guests.Query()
  2562. q = q.Join(guestdisks, sqlchemy.AND(sqlchemy.Equals(guestdisks.Field("guest_id"), guests.Field("id")),
  2563. sqlchemy.IsFalse(guestdisks.Field("deleted"))))
  2564. q = q.Filter(sqlchemy.Equals(guestdisks.Field("disk_id"), self.Id))
  2565. ret := make([]SGuest, 0)
  2566. if err := db.FetchModelObjects(GuestManager, q, &ret); err != nil {
  2567. log.Errorf("Fetch Geusts Objects %v", err)
  2568. return nil
  2569. }
  2570. return ret
  2571. }
  2572. func (self *SDisk) SetDiskReady(ctx context.Context, userCred mcclient.TokenCredential, reason string) {
  2573. self.SetStatus(ctx, userCred, api.DISK_READY, reason)
  2574. guests := self.GetAttachedGuests()
  2575. if guests != nil {
  2576. for _, guest := range guests {
  2577. guest.StartSyncstatus(ctx, userCred, "")
  2578. }
  2579. }
  2580. }
  2581. func (self *SDisk) SwitchToBackup(userCred mcclient.TokenCredential) error {
  2582. diff, err := db.Update(self, func() error {
  2583. self.StorageId, self.BackupStorageId = self.BackupStorageId, self.StorageId
  2584. return nil
  2585. })
  2586. if err != nil {
  2587. log.Errorf("SwitchToBackup fail %s", err)
  2588. return err
  2589. }
  2590. db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
  2591. return nil
  2592. }
  2593. func (self *SDisk) ClearHostSchedCache() error {
  2594. storage, _ := self.GetStorage()
  2595. if storage == nil {
  2596. return fmt.Errorf("no valid storage")
  2597. }
  2598. hosts := storage.GetAllAttachingHosts()
  2599. if hosts == nil {
  2600. return fmt.Errorf("get attaching host error")
  2601. }
  2602. for _, h := range hosts {
  2603. err := h.ClearSchedDescCache()
  2604. if err != nil {
  2605. return err
  2606. }
  2607. }
  2608. return nil
  2609. }
  2610. func (self *SDisk) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  2611. desc := self.SVirtualResourceBase.GetShortDesc(ctx)
  2612. desc.Add(jsonutils.NewInt(int64(self.DiskSize)), "size")
  2613. desc.Add(jsonutils.NewString(self.DiskType), "disk_type")
  2614. storage, _ := self.GetStorage()
  2615. if storage != nil {
  2616. desc.Add(jsonutils.NewString(storage.StorageType), "storage_type")
  2617. desc.Add(jsonutils.NewString(storage.MediumType), "medium_type")
  2618. }
  2619. if hypervisor := self.GetMetadata(ctx, "hypervisor", nil); len(hypervisor) > 0 {
  2620. desc.Add(jsonutils.NewString(hypervisor), "hypervisor")
  2621. }
  2622. if len(self.ExternalId) > 0 {
  2623. desc.Add(jsonutils.NewString(self.ExternalId), "externalId")
  2624. }
  2625. if self.IsSsd {
  2626. desc.Add(jsonutils.JSONTrue, "is_ssd")
  2627. }
  2628. fs := self.GetFsFormat()
  2629. if len(fs) > 0 {
  2630. desc.Add(jsonutils.NewString(fs), "fs_format")
  2631. }
  2632. tid := self.GetTemplateId()
  2633. if len(tid) > 0 {
  2634. desc.Add(jsonutils.NewString(tid), "template_id")
  2635. }
  2636. var billingInfo SCloudBillingInfo
  2637. if storage != nil {
  2638. billingInfo.SCloudProviderInfo = storage.getCloudProviderInfo()
  2639. }
  2640. if priceKey := self.GetMetadata(ctx, "ext:price_key", nil); len(priceKey) > 0 {
  2641. billingInfo.PriceKey = priceKey
  2642. }
  2643. billingInfo.SBillingBaseInfo = self.getBillingBaseInfo()
  2644. desc.Update(jsonutils.Marshal(billingInfo))
  2645. return desc
  2646. }
  2647. func (self *SDisk) getDev() string {
  2648. return self.GetMetadata(context.Background(), "dev", nil)
  2649. }
  2650. func (self *SDisk) GetMountPoint() string {
  2651. return self.GetMetadata(context.Background(), "mountpoint", nil)
  2652. }
  2653. func (self *SDisk) isReady() bool {
  2654. return self.Status == api.DISK_READY
  2655. }
  2656. func (self *SDisk) isInit() bool {
  2657. return self.Status == api.DISK_INIT
  2658. }
  2659. func (self *SDisk) PerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  2660. if self.PendingDeleted && !self.Deleted {
  2661. err := self.DoCancelPendingDelete(ctx, userCred)
  2662. if err != nil {
  2663. return nil, err
  2664. }
  2665. self.RecoverUsages(ctx, userCred)
  2666. }
  2667. return nil, nil
  2668. }
  2669. func (manager *SDiskManager) getExpiredPendingDeleteDisks() []SDisk {
  2670. deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds*-1) * time.Second)
  2671. q := manager.Query()
  2672. q = q.IsTrue("pending_deleted").LT("pending_deleted_at", deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize)
  2673. disks := make([]SDisk, 0)
  2674. err := db.FetchModelObjects(DiskManager, q, &disks)
  2675. if err != nil {
  2676. log.Errorf("fetch disks error %s", err)
  2677. return nil
  2678. }
  2679. return disks
  2680. }
  2681. func (manager *SDiskManager) CleanPendingDeleteDisks(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  2682. disks := manager.getExpiredPendingDeleteDisks()
  2683. if disks == nil {
  2684. return
  2685. }
  2686. for i := 0; i < len(disks); i += 1 {
  2687. disks[i].StartDiskDeleteTask(ctx, userCred, "", false, true, false)
  2688. }
  2689. }
  2690. func (manager *SDiskManager) GetNeedAutoSnapshotDisks() ([]SSnapshotPolicyResource, error) {
  2691. tz, _ := time.LoadLocation(options.Options.TimeZone)
  2692. t := time.Now().In(tz)
  2693. week := t.Weekday()
  2694. if week == 0 { // sunday is zero
  2695. week += 7
  2696. }
  2697. timePoint := t.Hour()
  2698. policy := SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_DISK).Equals("cloudregion_id", api.DEFAULT_REGION_ID)
  2699. policy = policy.Filter(sqlchemy.Contains(policy.Field("repeat_weekdays"), fmt.Sprintf("%d", week)))
  2700. sq := policy.Filter(
  2701. sqlchemy.OR(
  2702. sqlchemy.Contains(policy.Field("time_points"), fmt.Sprintf(",%d,", timePoint)),
  2703. sqlchemy.Startswith(policy.Field("time_points"), fmt.Sprintf("[%d,", timePoint)),
  2704. sqlchemy.Endswith(policy.Field("time_points"), fmt.Sprintf(",%d]", timePoint)),
  2705. sqlchemy.Equals(policy.Field("time_points"), fmt.Sprintf("[%d]", timePoint)),
  2706. ),
  2707. ).SubQuery()
  2708. disks := DiskManager.Query().SubQuery()
  2709. q := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK)
  2710. q = q.Join(sq, sqlchemy.Equals(q.Field("snapshotpolicy_id"), sq.Field("id")))
  2711. q = q.Join(disks, sqlchemy.Equals(q.Field("resource_id"), disks.Field("id")))
  2712. ret := []SSnapshotPolicyResource{}
  2713. err := db.FetchModelObjects(SnapshotPolicyResourceManager, q, &ret)
  2714. if err != nil {
  2715. return nil, err
  2716. }
  2717. return ret, nil
  2718. }
  2719. func (disk *SDisk) validateDiskAutoCreateSnapshot() error {
  2720. guests := disk.GetGuests()
  2721. if len(guests) == 0 {
  2722. return fmt.Errorf("Disks %s not attach guest, can't create snapshot", disk.GetName())
  2723. }
  2724. storage, err := disk.GetStorage()
  2725. if err != nil {
  2726. return errors.Wrapf(err, "GetStorage")
  2727. }
  2728. if len(guests) == 1 && utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
  2729. if !utils.IsInStringArray(guests[0].Status, []string{api.VM_RUNNING, api.VM_READY}) {
  2730. return fmt.Errorf("Guest(%s) in status(%s) cannot do disk snapshot", guests[0].Id, guests[0].Status)
  2731. }
  2732. }
  2733. if storageFree := storage.GetFreeCapacity(); storageFree < int64(disk.DiskSize) {
  2734. return fmt.Errorf("Storage(%s) space not enough", storage.GetName())
  2735. }
  2736. return nil
  2737. }
  2738. func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  2739. disks, err := manager.GetNeedAutoSnapshotDisks()
  2740. if err != nil {
  2741. log.Errorf("Get auto snapshot disks id failed: %s", err)
  2742. return
  2743. }
  2744. log.Infof("auto snapshot %d disks", len(disks))
  2745. guestDps := map[string][]SSnapshotPolicyResource{}
  2746. for i := 0; i < len(disks); i++ {
  2747. disk, err := disks[i].GetDisk()
  2748. if err != nil {
  2749. log.Errorf("get disk error: %v", err)
  2750. continue
  2751. }
  2752. if guest := disk.GetGuest(); guest != nil {
  2753. if dps, ok := guestDps[guest.Id]; ok {
  2754. guestDps[guest.Id] = append(dps, disks[i])
  2755. } else {
  2756. guestDps[guest.Id] = []SSnapshotPolicyResource{disks[i]}
  2757. }
  2758. continue
  2759. }
  2760. err = manager.DoAutoSnapshot(ctx, userCred, &disks[i], disk, "")
  2761. if err != nil {
  2762. log.Errorf("auto snapshot %s error: %v", disk.Name, err)
  2763. db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, err.Error(), userCred)
  2764. notifyclient.NotifySystemErrorWithCtx(ctx, disk.Id, disk.Name, db.ACT_DISK_AUTO_SNAPSHOT_FAIL, errors.Wrapf(err, "Disk auto create snapshot").Error())
  2765. }
  2766. }
  2767. for gid, diskSnapshotPolicies := range guestDps {
  2768. guest := GuestManager.FetchGuestById(gid)
  2769. err = manager.OrderCreateDisksSnapshotsBySnapshotPolicy(ctx, userCred, guest, diskSnapshotPolicies)
  2770. if err != nil {
  2771. log.Errorf("failed start OrderCreateDisksSnapshotsBySnapshotPolicy")
  2772. }
  2773. }
  2774. }
  2775. func (manager *SDiskManager) OrderCreateDisksSnapshotsBySnapshotPolicy(
  2776. ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, snapshotPolicyDisks []SSnapshotPolicyResource,
  2777. ) error {
  2778. params := jsonutils.NewDict()
  2779. params.Set("snapshot_policy_disks", jsonutils.Marshal(snapshotPolicyDisks))
  2780. task, err := taskman.TaskManager.NewTask(ctx, "GuestDisksSnapshotPolicyExecuteTask", guest, userCred, params, "", "", nil)
  2781. if err != nil {
  2782. return errors.Wrapf(err, "NewTask")
  2783. }
  2784. return task.ScheduleRun(nil)
  2785. }
  2786. func (manager *SDiskManager) DoAutoSnapshot(
  2787. ctx context.Context, userCred mcclient.TokenCredential,
  2788. diskSnapshotPolicy *SSnapshotPolicyResource, disk *SDisk, parentTaskId string,
  2789. ) error {
  2790. policy, err := diskSnapshotPolicy.GetSnapshotPolicy()
  2791. if err != nil {
  2792. return errors.Wrapf(err, "GetSnapshotPolicy")
  2793. }
  2794. if len(disk.ExternalId) == 0 {
  2795. err = disk.validateDiskAutoCreateSnapshot()
  2796. if err != nil {
  2797. return errors.Wrapf(err, "validateDiskAutoCreateSnapshot")
  2798. }
  2799. }
  2800. snapshot, err := disk.CreateSnapshotAuto(ctx, userCred, policy, parentTaskId)
  2801. if err != nil {
  2802. return errors.Wrapf(err, "CreateSnapshotAuto")
  2803. }
  2804. db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT, snapshot.Name, userCred)
  2805. policy.ExecuteNotify(ctx, userCred, disk.GetName())
  2806. return nil
  2807. }
  2808. func (self *SDisk) CreateSnapshotAuto(
  2809. ctx context.Context, userCred mcclient.TokenCredential, policy *SSnapshotPolicy, parentTaskId string,
  2810. ) (*SSnapshot, error) {
  2811. storage, err := self.GetStorage()
  2812. if err != nil {
  2813. return nil, errors.Wrapf(err, "GetStorage")
  2814. }
  2815. snapshot := &SSnapshot{}
  2816. snapshot.SetModelManager(SnapshotManager, snapshot)
  2817. snapshot.ProjectId = self.ProjectId
  2818. snapshot.DomainId = self.DomainId
  2819. snapshot.DiskId = self.Id
  2820. if len(self.ExternalId) == 0 {
  2821. snapshot.StorageId = self.StorageId
  2822. }
  2823. // inherit encrypt_key_id
  2824. snapshot.EncryptKeyId = self.EncryptKeyId
  2825. driver, err := storage.GetRegionDriver()
  2826. if err != nil {
  2827. return nil, errors.Wrapf(err, "GetRegionDriver")
  2828. }
  2829. snapshot.OutOfChain = driver.SnapshotIsOutOfChain(self)
  2830. snapshot.VirtualSize = self.DiskSize
  2831. snapshot.DiskType = self.DiskType
  2832. snapshot.Location = ""
  2833. snapshot.CreatedBy = api.SNAPSHOT_AUTO
  2834. snapshot.ManagerId = storage.ManagerId
  2835. if cloudregion, _ := storage.GetRegion(); cloudregion != nil {
  2836. snapshot.CloudregionId = cloudregion.GetId()
  2837. }
  2838. snapshot.Name = fmt.Sprintf("%s-auto-snapshot-%d", self.Name, time.Now().Unix())
  2839. snapshot.Status = api.SNAPSHOT_CREATING
  2840. if policy.RetentionDays > 0 {
  2841. snapshot.ExpiredAt = time.Now().AddDate(0, 0, policy.RetentionDays)
  2842. }
  2843. snapshot.IsSystem = self.IsSystem
  2844. err = SnapshotManager.TableSpec().Insert(ctx, snapshot)
  2845. if err != nil {
  2846. return nil, errors.Wrapf(err, "Insert")
  2847. }
  2848. db.OpsLog.LogEvent(snapshot, db.ACT_CREATE, "disk create snapshot auto", userCred)
  2849. err = snapshot.StartSnapshotCreateTask(ctx, userCred, nil, parentTaskId)
  2850. if err != nil {
  2851. return nil, errors.Wrap(err, "disk auto snapshot start snapshot task")
  2852. }
  2853. return snapshot, nil
  2854. }
  2855. func (self *SDisk) StartCreateBackupTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  2856. if task, err := taskman.TaskManager.NewTask(ctx, "DiskCreateBackupTask", self, userCred, nil, parentTaskId, "", nil); err != nil {
  2857. log.Errorln(err)
  2858. return err
  2859. } else {
  2860. task.ScheduleRun(nil)
  2861. }
  2862. return nil
  2863. }
  2864. func (self *SDisk) DeleteSnapshots(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  2865. if task, err := taskman.TaskManager.NewTask(ctx, "DiskDeleteSnapshotsTask", self, userCred, nil, parentTaskId, "", nil); err != nil {
  2866. log.Errorln(err)
  2867. return err
  2868. } else {
  2869. task.ScheduleRun(nil)
  2870. }
  2871. return nil
  2872. }
  2873. func (self *SDisk) IsDetachable() bool {
  2874. storage, _ := self.GetStorage()
  2875. if storage == nil {
  2876. return true
  2877. }
  2878. if storage.IsLocal() {
  2879. return false
  2880. }
  2881. if self.BillingType == billing_api.BILLING_TYPE_PREPAID {
  2882. return false
  2883. }
  2884. if utils.IsInStringArray(self.DiskType, []string{api.DISK_TYPE_SYS, api.DISK_TYPE_SWAP}) {
  2885. return false
  2886. }
  2887. if self.AutoDelete {
  2888. return false
  2889. }
  2890. return true
  2891. }
  2892. func (self *SDisk) GetDynamicConditionInput() *jsonutils.JSONDict {
  2893. conf := self.ToDiskConfig()
  2894. return conf.JSON(conf)
  2895. }
  2896. func (self *SDisk) IsNeedWaitSnapshotsDeleted() (bool, error) {
  2897. storage, _ := self.GetStorage()
  2898. if storage == nil {
  2899. return false, fmt.Errorf("no valid storage")
  2900. }
  2901. if storage.StorageType == api.STORAGE_RBD {
  2902. scnt, err := self.GetSnapshotCount()
  2903. if err != nil {
  2904. return false, err
  2905. }
  2906. if scnt > 0 {
  2907. return true, nil
  2908. }
  2909. }
  2910. return false, nil
  2911. }
  2912. func (self *SDisk) UpdataSnapshotsBackingDisk(backingDiskId string) error {
  2913. snapshots := make([]SSnapshot, 0)
  2914. err := SnapshotManager.Query().Equals("disk_id", self.Id).IsNullOrEmpty("backing_disk_id").All(&snapshots)
  2915. if err != nil {
  2916. return err
  2917. }
  2918. for i := 0; i < len(snapshots); i++ {
  2919. snapshots[i].SetModelManager(SnapshotManager, &snapshots[i])
  2920. _, err := db.Update(&snapshots[i], func() error {
  2921. snapshots[i].BackingDiskId = backingDiskId
  2922. return nil
  2923. })
  2924. if err != nil {
  2925. log.Errorln(err)
  2926. }
  2927. }
  2928. return nil
  2929. }
  2930. func (self *SDisk) GetSnapshotsNotInInstanceSnapshot() ([]SSnapshot, error) {
  2931. snapshots := make([]SSnapshot, 0)
  2932. sq := InstanceSnapshotJointManager.Query("snapshot_id").SubQuery()
  2933. q := SnapshotManager.Query().IsFalse("fake_deleted").Equals("disk_id", self.Id)
  2934. q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("snapshot_id"))).
  2935. Filter(sqlchemy.IsNull(sq.Field("snapshot_id")))
  2936. err := db.FetchModelObjects(SnapshotManager, q, &snapshots)
  2937. if err != nil {
  2938. log.Errorf("Fetch db snapshots failed %s", err)
  2939. return nil, err
  2940. }
  2941. return snapshots, nil
  2942. }
  2943. func (self *SDisk) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  2944. input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
  2945. _, err := self.SVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input)
  2946. if err != nil {
  2947. return nil, err
  2948. }
  2949. snapshotQuery := SnapshotManager.Query().Equals("disk_id", self.Id)
  2950. snapshots := make([]SSnapshot, 0, 1)
  2951. err = db.FetchModelObjects(SnapshotManager, snapshotQuery, &snapshots)
  2952. if err != nil {
  2953. return nil, errors.Wrapf(err, "fail to fetch snapshots of disk %s", self.Id)
  2954. }
  2955. for i := range snapshots {
  2956. snapshot := snapshots[i]
  2957. err := func() error {
  2958. lockman.LockObject(ctx, &snapshot)
  2959. defer lockman.ReleaseObject(ctx, &snapshot)
  2960. _, err := snapshot.PerformChangeOwner(ctx, userCred, query, input)
  2961. if err != nil {
  2962. return err
  2963. }
  2964. return nil
  2965. }()
  2966. if err != nil {
  2967. return nil, errors.Wrapf(err, "fail to change owner of this disk(%s)'s snapshot %s", self.Id, snapshot.Id)
  2968. }
  2969. }
  2970. return nil, nil
  2971. }
  2972. func (disk *SDisk) GetUsages() []db.IUsage {
  2973. if disk.PendingDeleted || disk.Deleted {
  2974. return nil
  2975. }
  2976. usage := SQuota{Storage: disk.DiskSize}
  2977. keys, err := disk.GetQuotaKeys()
  2978. if err != nil {
  2979. log.Errorf("disk.GetQuotaKeys fail %s", err)
  2980. return nil
  2981. }
  2982. usage.SetKeys(keys)
  2983. return []db.IUsage{
  2984. &usage,
  2985. }
  2986. }
  2987. // 绑定磁盘快照策略
  2988. // 磁盘只能绑定一个快照策略,已绑定时报错
  2989. // 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
  2990. func (disk *SDisk) PerformBindSnapshotpolicy(
  2991. ctx context.Context,
  2992. userCred mcclient.TokenCredential,
  2993. query jsonutils.JSONObject,
  2994. input *api.DiskSnapshotpolicyInput,
  2995. ) (jsonutils.JSONObject, error) {
  2996. // 磁盘只能绑定一个快照策略,已绑定时报错
  2997. cnt, err := SnapshotPolicyResourceManager.GetBindingCount(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  2998. if err != nil {
  2999. return nil, errors.Wrap(err, "GetBindingCount")
  3000. }
  3001. if cnt > 0 {
  3002. return nil, httperrors.NewConflictError("disk already bound to a snapshot policy")
  3003. }
  3004. // 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
  3005. if guest := disk.GetGuest(); guest != nil {
  3006. guestCnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
  3007. if err != nil {
  3008. return nil, errors.Wrap(err, "GetBindingCount for guest")
  3009. }
  3010. if guestCnt > 0 {
  3011. return nil, httperrors.NewConflictError("guest already has server snapshot policy, disk cannot bind snapshot policy")
  3012. }
  3013. }
  3014. spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
  3015. if err != nil {
  3016. return nil, err
  3017. }
  3018. sp := spObj.(*SSnapshotPolicy)
  3019. if len(sp.ManagerId) > 0 {
  3020. storage, err := disk.GetStorage()
  3021. if err != nil {
  3022. return nil, errors.Wrapf(err, "GetStorage")
  3023. }
  3024. if storage.ManagerId != sp.ManagerId {
  3025. return nil, httperrors.NewConflictError("The snapshot policy %s and disk account are different", sp.Name)
  3026. }
  3027. zone, err := storage.GetZone()
  3028. if err != nil {
  3029. return nil, errors.Wrapf(err, "GetZone")
  3030. }
  3031. if sp.CloudregionId != zone.CloudregionId {
  3032. return nil, httperrors.NewConflictError("The snapshot policy %s and the disk are in different region", sp.Name)
  3033. }
  3034. }
  3035. return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
  3036. }
  3037. // 设置磁盘快照策略
  3038. // 可覆盖当前磁盘绑定的快照策略,若磁盘所属主机已绑定主机快照策略,则自动解除主机快照策略
  3039. func (disk *SDisk) PerformSetSnapshotpolicy(
  3040. ctx context.Context,
  3041. userCred mcclient.TokenCredential,
  3042. query jsonutils.JSONObject,
  3043. input *api.DiskSnapshotpolicyInput,
  3044. ) (jsonutils.JSONObject, error) {
  3045. spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
  3046. if err != nil {
  3047. return nil, err
  3048. }
  3049. sp := spObj.(*SSnapshotPolicy)
  3050. if sp.Type != api.SNAPSHOT_POLICY_TYPE_DISK {
  3051. return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a disk snapshot policy", sp.Name)
  3052. }
  3053. if len(sp.ManagerId) > 0 {
  3054. storage, err := disk.GetStorage()
  3055. if err != nil {
  3056. return nil, errors.Wrapf(err, "GetStorage")
  3057. }
  3058. if storage.ManagerId != sp.ManagerId {
  3059. return nil, httperrors.NewConflictError("The snapshot policy %s and disk account are different", sp.Name)
  3060. }
  3061. zone, err := storage.GetZone()
  3062. if err != nil {
  3063. return nil, errors.Wrapf(err, "GetZone")
  3064. }
  3065. if sp.CloudregionId != zone.CloudregionId {
  3066. return nil, httperrors.NewConflictError("The snapshot policy %s and the disk are in different region", sp.Name)
  3067. }
  3068. }
  3069. // 先解除当前绑定再绑定新策略
  3070. if err := SnapshotPolicyResourceManager.RemoveByResource(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
  3071. return nil, errors.Wrap(err, "RemoveByResource")
  3072. }
  3073. // 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
  3074. if guest := disk.GetGuest(); guest != nil {
  3075. if err := SnapshotPolicyResourceManager.RemoveByResource(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
  3076. return nil, errors.Wrap(err, "RemoveByResource")
  3077. }
  3078. }
  3079. return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
  3080. }
  3081. // 解绑自动快照策略
  3082. func (disk *SDisk) PerformUnbindSnapshotpolicy(
  3083. ctx context.Context,
  3084. userCred mcclient.TokenCredential,
  3085. query jsonutils.JSONObject,
  3086. input *api.DiskSnapshotpolicyInput,
  3087. ) (jsonutils.JSONObject, error) {
  3088. spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
  3089. if err != nil {
  3090. return nil, err
  3091. }
  3092. sp := spObj.(*SSnapshotPolicy)
  3093. return nil, sp.StartUnbindDisksTask(ctx, userCred, []string{disk.Id})
  3094. }
  3095. func (manager *SDiskManager) ListItemExportKeys(ctx context.Context,
  3096. q *sqlchemy.SQuery,
  3097. userCred mcclient.TokenCredential,
  3098. keys stringutils2.SSortedStrings,
  3099. ) (*sqlchemy.SQuery, error) {
  3100. var err error
  3101. q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  3102. if err != nil {
  3103. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
  3104. }
  3105. if keys.ContainsAny(manager.SStorageResourceBaseManager.GetExportKeys()...) {
  3106. q, err = manager.SStorageResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  3107. if err != nil {
  3108. return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemExportKeys")
  3109. }
  3110. }
  3111. return q, nil
  3112. }
  3113. func (disk *SDisk) PerformRebuild(
  3114. ctx context.Context,
  3115. userCred mcclient.TokenCredential,
  3116. query jsonutils.JSONObject,
  3117. input api.DiskRebuildInput,
  3118. ) (jsonutils.JSONObject, error) {
  3119. guests := disk.GetGuests()
  3120. for _, guest := range guests {
  3121. if guest.GetStatus() != api.VM_READY {
  3122. return nil, httperrors.NewInvalidStatusError("Guest %s status is %s", guest.GetId(), guest.GetStatus())
  3123. }
  3124. guest.SetStatus(ctx, userCred, api.VM_DISK_RESET, "disk rebuild")
  3125. }
  3126. err := disk.resetDiskinfo(ctx, userCred, input)
  3127. if err != nil {
  3128. return nil, errors.Wrap(err, "disk.resetDiskinfo")
  3129. }
  3130. disk.SetStatus(ctx, userCred, api.DISK_REBUILD, "disk rebuild")
  3131. return nil, disk.StartDiskCreateTask(ctx, userCred, true, disk.SnapshotId, "")
  3132. }
  3133. func (disk *SDisk) resetDiskinfo(
  3134. ctx context.Context,
  3135. userCred mcclient.TokenCredential,
  3136. input api.DiskRebuildInput,
  3137. ) error {
  3138. if disk.DiskFormat != "raw" {
  3139. return errors.Wrapf(errors.ErrInvalidStatus, "disk_format must be raw, not %s", disk.DiskFormat)
  3140. }
  3141. if len(disk.FsFormat) == 0 {
  3142. return errors.Wrap(errors.ErrInvalidStatus, "fs_format must be set")
  3143. }
  3144. if input.Fs != nil {
  3145. if disk.FsFeatures != nil {
  3146. err := DiskManager.ValidateFsFeatures(*input.Fs, input.FsFeatures)
  3147. if err != nil {
  3148. return err
  3149. }
  3150. }
  3151. }
  3152. if input.TemplateId != nil {
  3153. if len(*input.TemplateId) > 0 {
  3154. imageObj, err := CachedimageManager.FetchByIdOrName(ctx, userCred, *input.TemplateId)
  3155. if err != nil {
  3156. if errors.Cause(err) == sql.ErrNoRows {
  3157. return httperrors.NewResourceNotFoundError2(CachedimageManager.Keyword(), *input.TemplateId)
  3158. } else {
  3159. return errors.Wrap(err, "CachedimageManager.FetchById")
  3160. }
  3161. }
  3162. image := imageObj.(*SCachedimage)
  3163. input.TemplateId = &image.Id
  3164. }
  3165. }
  3166. if input.BackupId != nil {
  3167. if len(*input.BackupId) > 0 {
  3168. bkObj, err := DiskBackupManager.FetchByIdOrName(ctx, userCred, *input.BackupId)
  3169. if err != nil {
  3170. if errors.Cause(err) == sql.ErrNoRows {
  3171. return httperrors.NewResourceNotFoundError2(DiskBackupManager.Keyword(), *input.BackupId)
  3172. } else {
  3173. return errors.Wrap(err, "DiskBackupManager.FetchByIdOrName")
  3174. }
  3175. }
  3176. backup := bkObj.(*SDiskBackup)
  3177. input.BackupId = &backup.Id
  3178. }
  3179. }
  3180. diskSize := 0
  3181. if input.Size != nil {
  3182. size, err := fileutils.GetSizeMb(*input.Size, 'M', 1024)
  3183. if err != nil {
  3184. return errors.Wrapf(err, "GetSizeMb %s", *input.Size)
  3185. }
  3186. diskSize = size
  3187. }
  3188. notes, err := db.Update(disk, func() error {
  3189. if input.TemplateId != nil {
  3190. disk.TemplateId = *input.TemplateId
  3191. }
  3192. if input.BackupId != nil {
  3193. disk.BackupId = *input.BackupId
  3194. }
  3195. if diskSize > 0 {
  3196. disk.DiskSize = diskSize
  3197. }
  3198. if input.Fs != nil {
  3199. disk.FsFormat = *input.Fs
  3200. disk.FsFeatures = input.FsFeatures
  3201. }
  3202. return nil
  3203. })
  3204. if err != nil {
  3205. logclient.AddActionLogWithContext(ctx, disk, logclient.ACT_UPDATE, err, userCred, false)
  3206. return errors.Wrap(err, "Update")
  3207. }
  3208. logclient.AddActionLogWithContext(ctx, disk, logclient.ACT_UPDATE, err, userCred, true)
  3209. db.OpsLog.LogEvent(disk, db.ACT_UPDATE, notes, userCred)
  3210. return nil
  3211. }
  3212. func (disk *SDisk) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.DiskChangeBillingTypeInput) (jsonutils.JSONObject, error) {
  3213. if !utils.IsInStringArray(disk.Status, []string{api.DISK_READY}) {
  3214. return nil, httperrors.NewServerStatusError("Cannot change disk billing type in status %s", disk.Status)
  3215. }
  3216. if len(input.BillingType) == 0 {
  3217. return nil, httperrors.NewMissingParameterError("billing_type")
  3218. }
  3219. if !utils.IsInStringArray(string(input.BillingType), []string{string(billing_api.BILLING_TYPE_POSTPAID), string(billing_api.BILLING_TYPE_PREPAID)}) {
  3220. return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType)
  3221. }
  3222. if disk.BillingType == input.BillingType {
  3223. return nil, nil
  3224. }
  3225. return nil, disk.StartChangeBillingTypeTask(ctx, userCred, "")
  3226. }
  3227. func (disk *SDisk) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  3228. disk.SetStatus(ctx, userCred, apis.STATUS_CHANGE_BILLING_TYPE, "")
  3229. kwargs := jsonutils.NewDict()
  3230. task, err := taskman.TaskManager.NewTask(ctx, "DiskChangeBillingTypeTask", disk, userCred, kwargs, parentTaskId, "", nil)
  3231. if err != nil {
  3232. return err
  3233. }
  3234. return task.ScheduleRun(nil)
  3235. }