| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "database/sql"
- "fmt"
- "net"
- "net/http"
- "strconv"
- "strings"
- "time"
- "unicode"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/tristate"
- "yunion.io/x/pkg/util/billing"
- "yunion.io/x/pkg/util/httputils"
- "yunion.io/x/pkg/util/osprofile"
- "yunion.io/x/pkg/util/rand"
- "yunion.io/x/pkg/util/regutils"
- "yunion.io/x/pkg/util/sets"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- billing_api "yunion.io/x/onecloud/pkg/apis/billing"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- imageapi "yunion.io/x/onecloud/pkg/apis/image"
- noapi "yunion.io/x/onecloud/pkg/apis/notify"
- schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
- "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/cloudcommon/userdata"
- "yunion.io/x/onecloud/pkg/cloudcommon/validators"
- "yunion.io/x/onecloud/pkg/compute/baremetal"
- guestdriver_types "yunion.io/x/onecloud/pkg/compute/guestdrivers/types"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules/image"
- "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
- "yunion.io/x/onecloud/pkg/mcclient/modules/scheduler"
- "yunion.io/x/onecloud/pkg/util/bitmap"
- "yunion.io/x/onecloud/pkg/util/logclient"
- "yunion.io/x/onecloud/pkg/util/netutils2"
- "yunion.io/x/onecloud/pkg/util/seclib2"
- )
- // 获取VNC信息
- func (self *SGuest) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- ret := &cloudprovider.ServerVncOutput{}
- if self.PowerStates == api.VM_POWER_STATES_ON ||
- utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_MIGRATING}) {
- host, err := self.GetHost()
- if err != nil {
- return nil, httperrors.NewInternalServerError("get host %v", err)
- }
- if options.Options.ForceUseOriginVnc {
- input.Origin = true
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- ret, err = driver.GetGuestVncInfo(ctx, userCred, self, host, input)
- if err != nil {
- return nil, err
- }
- ret.Id = self.Id
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CONSOLE, ret, userCred, true)
- return ret, nil
- }
- return ret, nil
- }
- // 获取公有云可变更配置
- // 目前支持: 阿里云,腾讯云,华为云
- func (guest *SGuest) GetDetailsModificationTypes(ctx context.Context, userCred mcclient.TokenCredential, input jsonutils.JSONObject) (*api.ServerModificationTypesOutput, error) {
- if len(guest.ExternalId) == 0 {
- return nil, httperrors.NewNotFoundError("guest external id is empty")
- }
- vm, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "GetIVM")
- }
- ret := &api.ServerModificationTypesOutput{}
- ret.ModificationTypes = make([]api.ServerModificationType, 0)
- modificationTypes, err := vm.GetModificationTypes()
- if err != nil {
- return nil, errors.Wrapf(err, "GetModificationTypes")
- }
- for _, modificationType := range modificationTypes {
- ret.ModificationTypes = append(ret.ModificationTypes, api.ServerModificationType{Name: modificationType.InstanceType})
- }
- return ret, nil
- }
- func (self *SGuest) PreCheckPerformAction(
- ctx context.Context, userCred mcclient.TokenCredential,
- action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
- ) error {
- if err := self.SVirtualResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
- return err
- }
- if self.Hypervisor == api.HYPERVISOR_KVM {
- host, _ := self.GetHost()
- if host != nil && (host.HostStatus == api.HOST_OFFLINE || !host.Enabled.Bool()) &&
- utils.IsInStringArray(action,
- []string{
- "start", "restart", "stop", "reset", "rebuild-root",
- "change-config", "instance-snapshot", "snapshot-and-clone",
- "attach-isolated-device", "detach-isolated-deivce",
- "insert-iso", "eject-iso", "deploy", "create-backup",
- }) {
- return httperrors.NewInvalidStatusError(
- "host status %s and enabled %v, can't do server %s", host.HostStatus, host.Enabled.Bool(), action)
- }
- }
- return nil
- }
- // 远程执行QMP命令
- func (self *SGuest) PerformMonitor(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input *api.ServerMonitorInput,
- ) (jsonutils.JSONObject, error) {
- if self.PowerStates == api.VM_POWER_STATES_ON ||
- utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_MIGRATING}) {
- if input.COMMAND == "" {
- return nil, httperrors.NewMissingParameterError("command")
- }
- return self.SendMonitorCommand(ctx, userCred, input)
- }
- return nil, httperrors.NewInvalidStatusError("Cannot send command in status %s", self.Status)
- }
- // +onecloud:swagger-gen-ignore
- func (self *SGuest) PerformEvent(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- event, err := data.GetString("event")
- if err != nil {
- return nil, httperrors.NewMissingParameterError("event")
- }
- if event == "GUEST_PANICKED" {
- kwargs := jsonutils.NewDict()
- kwargs.Set("reason", jsonutils.NewString(event))
- if data.Contains("screen_dump_info") {
- screenDumpInfo := api.SGuestScreenDumpInfo{}
- if err := data.Unmarshal(&screenDumpInfo, "screen_dump_info"); err != nil {
- log.Errorf("failed unmarshal screen_dump_info %s", err)
- } else {
- kwargs.Set("screen_dump_name", jsonutils.NewString(screenDumpInfo.S3ObjectName))
- if _, err := self.SaveGuestScreenDump(ctx, userCred, &screenDumpInfo); err != nil {
- log.Errorf("SaveGuestScreenDump failed %s", err)
- }
- }
- }
- db.OpsLog.LogEvent(self, db.ACT_GUEST_PANICKED, kwargs.String(), userCred)
- logclient.AddSimpleActionLog(self, logclient.ACT_GUEST_PANICKED, kwargs.String(), userCred, true)
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: self,
- Action: notifyclient.ActionServerPanicked,
- IsFail: true,
- })
- }
- return nil, nil
- }
- func (self *SGuest) GetDetailsDesc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrapf(err, "GetHost")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- return driver.GetJsonDescAtHost(ctx, userCred, self, host, nil)
- }
- // 保存镜像
- func (self *SGuest) PerformSaveImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSaveImageInput) (api.ServerSaveImageInput, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return input, httperrors.NewInputParameterError("Cannot save image in status %s", self.Status)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return input, errors.Wrapf(err, "GetDriver")
- }
- input.Restart = ((self.Status == api.VM_RUNNING) || input.AutoStart) && !driver.IsAllowSaveImageOnRunning()
- if len(input.Name) == 0 && len(input.GenerateName) == 0 {
- return input, httperrors.NewInputParameterError("Image name is required")
- }
- disks := self.CategorizeDisks()
- if disks.Root == nil {
- return input, httperrors.NewInputParameterError("No root image")
- }
- input.OsType = self.OsType
- if len(input.OsType) == 0 {
- input.OsType = "Linux"
- }
- input.OsArch = self.OsArch
- if apis.IsARM(self.OsArch) || apis.IsRISCV(self.OsArch) {
- if osArch := self.GetMetadata(ctx, "os_arch", nil); len(osArch) == 0 {
- host, _ := self.GetHost()
- input.OsArch = host.CpuArchitecture
- }
- }
- factory, _ := cloudprovider.GetProviderFactory(driver.GetProvider())
- if factory == nil || factory.IsOnPremise() { // OneCloud or VMware
- lockman.LockObject(ctx, disks.Root)
- defer lockman.ReleaseObject(ctx, disks.Root)
- var err error
- input.ImageId, err = disks.Root.PrepareSaveImage(ctx, userCred, input)
- if err != nil {
- return input, errors.Wrapf(err, "PrepareSaveImage")
- }
- }
- if len(input.Name) == 0 {
- input.Name = input.GenerateName
- }
- logclient.AddSimpleActionLog(self, logclient.ACT_SAVE_IMAGE, input, userCred, true)
- return input, self.StartGuestSaveImage(ctx, userCred, input, "")
- }
- func (self *SGuest) StartGuestSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSaveImageInput, parentTaskId string) error {
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartGuestSaveImage(ctx, userCred, self, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId)
- }
- // 保存主机模板
- func (self *SGuest) PerformSaveGuestImage(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input api.ServerSaveGuestImageInput) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY}) {
- return nil, httperrors.NewBadRequestError("Cannot save image in status %s", self.Status)
- }
- if len(input.Name) == 0 && len(input.GenerateName) == 0 {
- return nil, httperrors.NewMissingParameterError("Image name is required")
- }
- if self.Hypervisor != api.HYPERVISOR_KVM {
- return nil, httperrors.NewBadRequestError("Support only by KVM Hypervisor")
- }
- disks := self.CategorizeDisks()
- if disks.Root == nil {
- return nil, httperrors.NewInternalServerError("No root image")
- }
- if len(self.EncryptKeyId) > 0 && (input.EncryptKeyId == nil || len(*input.EncryptKeyId) == 0) {
- // server encrypted, so image must be encrypted
- input.EncryptKeyId = &self.EncryptKeyId
- } else if len(self.EncryptKeyId) > 0 && input.EncryptKeyId != nil && len(*input.EncryptKeyId) > 0 && self.EncryptKeyId != *input.EncryptKeyId {
- return nil, errors.Wrap(httperrors.ErrConflict, "input encrypt key not match with server encrypt key")
- }
- diskList := append(disks.Data, disks.Root)
- kwargs := imageapi.GuestImageCreateInput{}
- kwargs.GuestImageCreateInputBase = input.GuestImageCreateInputBase
- kwargs.Properties = make(map[string]string)
- if len(kwargs.ProjectId) == 0 {
- kwargs.ProjectId = self.ProjectId
- }
- for _, disk := range diskList {
- kwargs.Images = append(kwargs.Images, imageapi.GuestImageCreateInputSubimage{
- DiskFormat: disk.DiskFormat,
- VirtualSize: disk.DiskSize,
- })
- }
- if len(input.Notes) > 0 {
- kwargs.Properties["notes"] = input.Notes
- }
- osType := self.OsType
- if len(osType) == 0 {
- osType = "Linux"
- }
- kwargs.Properties["os_type"] = osType
- if apis.IsARM(self.OsArch) || apis.IsRISCV(self.OsArch) {
- var osArch string
- if osArch = self.GetMetadata(ctx, "os_arch", nil); len(osArch) == 0 {
- host, _ := self.GetHost()
- osArch = host.CpuArchitecture
- }
- kwargs.Properties["os_arch"] = osArch
- kwargs.OsArch = self.OsArch
- }
- s := auth.GetSession(ctx, userCred, consts.GetRegion())
- ret, err := image.GuestImages.Create(s, jsonutils.Marshal(kwargs))
- if err != nil {
- return nil, err
- }
- guestImageId, _ := ret.GetString("id")
- // set class metadata
- cm, err := self.GetAllClassMetadata()
- if err != nil {
- return nil, errors.Wrap(err, "unable to GetAllClassMetadata")
- }
- if len(cm) > 0 {
- _, err = image.GuestImages.PerformAction(s, guestImageId, "set-class-metadata", jsonutils.Marshal(cm))
- if err != nil {
- return nil, errors.Wrapf(err, "unable to SetClassMetadata for guest image %s", guestImageId)
- }
- }
- guestImageInfo := struct {
- RootImage imageapi.SubImageInfo
- DataImages []imageapi.SubImageInfo
- }{}
- ret.Unmarshal(&guestImageInfo)
- if len(guestImageInfo.DataImages) != len(disks.Data) {
- return nil, fmt.Errorf("create subimage of guest image error")
- }
- imageIds := make([]string, 0, len(guestImageInfo.DataImages)+1)
- for _, info := range guestImageInfo.DataImages {
- imageIds = append(imageIds, info.ID)
- }
- imageIds = append(imageIds, guestImageInfo.RootImage.ID)
- taskParams := jsonutils.NewDict()
- if input.AutoStart != nil && *input.AutoStart {
- taskParams.Add(jsonutils.JSONTrue, "auto_start")
- }
- taskParams.Add(jsonutils.Marshal(imageIds), "image_ids")
- log.Infof("before StartGuestSaveGuestImage image_ids: %s", imageIds)
- return nil, self.StartGuestSaveGuestImage(ctx, userCred, taskParams, "")
- }
- func (self *SGuest) StartGuestSaveGuestImage(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
- driver, err := self.GetDriver()
- if err != nil {
- return err
- }
- return driver.StartGuestSaveGuestImage(ctx, userCred, self, data, parentTaskId)
- }
- // 同步配置
- func (self *SGuest) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewResourceBusyError("Cannot sync in status %s", self.Status)
- }
- if err := self.StartSyncTask(ctx, userCred, false, ""); err != nil {
- return nil, err
- }
- return nil, nil
- }
- func (self *SGuest) GetQemuVersion(userCred mcclient.TokenCredential) string {
- return self.GetMetadata(context.Background(), "__qemu_version", userCred)
- }
- func (self *SGuest) GetQemuCmdline(userCred mcclient.TokenCredential) string {
- return self.GetMetadata(context.Background(), "__qemu_cmdline", userCred)
- }
- func (self *SGuest) GetDetailsQemuInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*api.ServerQemuInfo, error) {
- version := self.GetQemuVersion(userCred)
- cmdline := self.GetQemuCmdline(userCred)
- return &api.ServerQemuInfo{
- Version: version,
- Cmdline: cmdline,
- }, nil
- }
- // if qemuVer >= compareVer return true
- func (self *SGuest) CheckQemuVersion(qemuVer, compareVer string) bool {
- if len(qemuVer) == 0 {
- return false
- }
- compareVersion := strings.Split(compareVer, ".")
- guestVersion := strings.Split(qemuVer, ".")
- var i = 0
- for ; i < len(guestVersion); i++ {
- if i >= len(compareVersion) {
- return true
- }
- v, _ := strconv.ParseInt(guestVersion[i], 10, 0)
- compareV, _ := strconv.ParseInt(compareVersion[i], 10, 0)
- if v < compareV {
- return false
- } else if v > compareV {
- return true
- }
- }
- if i < len(compareVersion)-1 {
- return false
- }
- return true
- }
- func (self *SGuest) validateMigrate(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- migrateInput *api.GuestMigrateInput,
- liveMigrateInput *api.GuestLiveMigrateInput,
- ) error {
- isLiveMigrate := false
- if liveMigrateInput != nil {
- isLiveMigrate = true
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- if isLiveMigrate {
- // do live migrate check
- if !driver.IsSupportLiveMigrate() {
- return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
- }
- if err := driver.CheckLiveMigrate(ctx, self, userCred, *liveMigrateInput); err != nil {
- return err
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_SUSPEND}) {
- if len(liveMigrateInput.PreferHostId) > 0 {
- iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, liveMigrateInput.PreferHostId)
- if iHost == nil {
- return httperrors.NewBadRequestError("Host %s not found", liveMigrateInput.PreferHostId)
- }
- host := iHost.(*SHost)
- liveMigrateInput.PreferHostId = host.Id
- }
- return nil
- }
- return httperrors.NewBadRequestError("Cannot live migrate in status %s", self.Status)
- } else {
- // do migrate check
- if !driver.IsSupportMigrate() {
- return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
- }
- if !migrateInput.IsRescueMode && self.Status != api.VM_READY {
- return httperrors.NewServerStatusError("Cannot normal migrate guest in status %s, try rescue mode or server-live-migrate?", self.Status)
- }
- if err := driver.CheckMigrate(ctx, self, userCred, *migrateInput); err != nil {
- return err
- }
- if len(migrateInput.PreferHostId) > 0 {
- iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, migrateInput.PreferHostId)
- if iHost == nil {
- return httperrors.NewBadRequestError("Host %s not found", migrateInput.PreferHostId)
- }
- host := iHost.(*SHost)
- migrateInput.PreferHostId = host.Id
- }
- return nil
- }
- }
- func (self *SGuest) validateConvertToKvm(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- migrateInput *api.GuestMigrateInput,
- ) error {
- if len(migrateInput.PreferHostId) > 0 {
- iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, migrateInput.PreferHostId)
- if iHost == nil {
- return httperrors.NewBadRequestError("Host %s not found", migrateInput.PreferHostId)
- }
- host := iHost.(*SHost)
- migrateInput.PreferHostId = host.Id
- }
- if self.Status != api.VM_READY {
- return httperrors.NewServerStatusError("can't convert guest in status %s", self.Status)
- }
- return nil
- }
- // 迁移调度
- func (self *SGuest) PerformMigrateForecast(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerMigrateForecastInput) (jsonutils.JSONObject, error) {
- var (
- mInput *api.GuestMigrateInput = nil
- lmInput *api.GuestLiveMigrateInput = nil
- )
- if input.ConvertToKvm {
- mInput = &api.GuestMigrateInput{PreferHostId: input.PreferHostId}
- if err := self.validateConvertToKvm(ctx, userCred, mInput); err != nil {
- return nil, err
- }
- input.PreferHostId = mInput.PreferHostId
- } else {
- if input.LiveMigrate {
- lmInput = &api.GuestLiveMigrateInput{
- PreferHostId: input.PreferHostId,
- SkipCpuCheck: &input.SkipCpuCheck,
- }
- if err := self.validateMigrate(ctx, userCred, nil, lmInput); err != nil {
- return nil, err
- }
- input.PreferHostId = lmInput.PreferHostId
- } else {
- mInput = &api.GuestMigrateInput{
- PreferHostId: input.PreferHostId,
- IsRescueMode: input.IsRescueMode,
- }
- if err := self.validateMigrate(ctx, userCred, mInput, nil); err != nil {
- return nil, err
- }
- input.PreferHostId = mInput.PreferHostId
- }
- }
- schedParams := self.GetSchedMigrateParams(userCred, input)
- if input.ConvertToKvm {
- schedParams.Hypervisor = api.HYPERVISOR_KVM
- }
- s := auth.GetAdminSession(ctx, options.Options.Region)
- _, res, err := scheduler.SchedManager.DoScheduleForecast(s, schedParams, 1)
- if err != nil {
- return nil, errors.Wrap(err, "Do schedule migrate forecast")
- }
- return res, nil
- }
- func (self *SGuest) GetSchedMigrateParams(
- userCred mcclient.TokenCredential,
- input *api.ServerMigrateForecastInput,
- ) *schedapi.ScheduleInput {
- schedDesc := self.ToSchedDesc()
- if input.PreferHostId != "" {
- schedDesc.ServerConfig.PreferHost = input.PreferHostId
- }
- schedDesc.ResetCpuNumaPin = input.ResetCpuNumaPin
- if input.LiveMigrate {
- schedDesc.LiveMigrate = input.LiveMigrate
- if self.GetMetadata(context.Background(), "__cpu_mode", userCred) != api.CPU_MODE_QEMU {
- host, _ := self.GetHost()
- schedDesc.CpuDesc = host.CpuDesc
- schedDesc.CpuMicrocode = host.CpuMicrocode
- schedDesc.CpuMode = api.CPU_MODE_HOST
- } else {
- schedDesc.CpuMode = api.CPU_MODE_QEMU
- }
- schedDesc.SkipCpuCheck = &input.SkipCpuCheck
- host, _ := self.GetHost()
- if host != nil {
- schedDesc.TargetHostKernel, _ = host.SysInfo.GetString("kernel_version")
- schedDesc.SkipKernelCheck = &input.SkipKernelCheck
- schedDesc.HostMemPageSizeKB = host.PageSizeKB
- }
- if self.IsSchedulerNumaAllocate() {
- cpuNumaPin := make([]schedapi.SCpuNumaPin, 0)
- self.CpuNumaPin.Unmarshal(&cpuNumaPin)
- schedDesc.CpuNumaPin = cpuNumaPin
- }
- }
- schedDesc.ReuseNetwork = true
- return schedDesc
- }
- // 冷迁移虚拟机
- func (self *SGuest) PerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestMigrateInput) (jsonutils.JSONObject, error) {
- if err := self.validateMigrate(ctx, userCred, input, nil); err != nil {
- return nil, err
- }
- return nil, self.StartMigrateTask(ctx, userCred, input.IsRescueMode, input.AutoStart, self.Status, input.PreferHostId, "")
- }
- func (self *SGuest) StartMigrateTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- isRescueMode, autoStart bool, guestStatus, preferHostId, parentTaskId string,
- ) error {
- vmStatus := api.VM_START_MIGRATE
- data := jsonutils.NewDict()
- if isRescueMode {
- data.Set("is_rescue_mode", jsonutils.JSONTrue)
- }
- if len(preferHostId) > 0 {
- data.Set("prefer_host_id", jsonutils.NewString(preferHostId))
- }
- if autoStart {
- data.Set("auto_start", jsonutils.JSONTrue)
- }
- if self.HostId == preferHostId {
- vmStatus = api.VM_STARTING
- data.Set("reset_cpu_numa_pin", jsonutils.JSONTrue)
- }
- data.Set("guest_status", jsonutils.NewString(guestStatus))
- dedicateMigrateTask := "GuestMigrateTask"
- if len(self.ExternalId) > 0 {
- dedicateMigrateTask = "ManagedGuestMigrateTask" //托管私有云
- }
- self.SetStatus(ctx, userCred, vmStatus, "")
- if task, err := taskman.TaskManager.NewTask(ctx, dedicateMigrateTask, self, userCred, data, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- // 在线迁移虚拟机
- func (self *SGuest) PerformLiveMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestLiveMigrateInput) (jsonutils.JSONObject, error) {
- if err := self.validateMigrate(ctx, userCred, nil, input); err != nil {
- return nil, err
- }
- if input.EnableTLS == nil {
- input.EnableTLS = &options.Options.EnableTlsMigration
- }
- return nil, self.StartGuestLiveMigrateTask(ctx, userCred,
- self.Status, input.PreferHostId, input.SkipCpuCheck,
- input.SkipKernelCheck, input.EnableTLS, input.QuicklyFinish, input.MaxBandwidthMb, input.KeepDestGuestOnFailed, "",
- )
- }
- func (self *SGuest) StartGuestLiveMigrateTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- guestStatus, preferHostId string,
- skipCpuCheck, skipKernelCheck, enableTLS, quicklyFinish *bool,
- maxBandwidthMb *int64, keepDestGuestOnFailed *bool, parentTaskId string,
- ) error {
- self.SetStatus(ctx, userCred, api.VM_START_MIGRATE, "")
- data := jsonutils.NewDict()
- if len(preferHostId) > 0 {
- data.Set("prefer_host_id", jsonutils.NewString(preferHostId))
- }
- if skipCpuCheck != nil {
- data.Set("skip_cpu_check", jsonutils.NewBool(*skipCpuCheck))
- }
- if skipKernelCheck != nil {
- data.Set("skip_kernel_check", jsonutils.NewBool(*skipKernelCheck))
- }
- if enableTLS != nil {
- data.Set("enable_tls", jsonutils.NewBool(*enableTLS))
- }
- if quicklyFinish != nil {
- data.Set("quickly_finish", jsonutils.NewBool(*quicklyFinish))
- }
- if maxBandwidthMb != nil {
- data.Set("max_bandwidth_mb", jsonutils.NewInt(*maxBandwidthMb))
- }
- if keepDestGuestOnFailed != nil {
- data.Set("keep_dest_guest_on_failed", jsonutils.NewBool(*keepDestGuestOnFailed))
- }
- data.Set("guest_status", jsonutils.NewString(guestStatus))
- dedicateMigrateTask := "GuestLiveMigrateTask"
- if len(self.ExternalId) > 0 {
- dedicateMigrateTask = "ManagedGuestLiveMigrateTask" //托管私有云
- }
- if task, err := taskman.TaskManager.NewTask(ctx, dedicateMigrateTask, self, userCred, data, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- // +onecloud:swagger-gen-ignore
- func (self *SGuest) PerformSetLiveMigrateParams(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetLiveMigrateParamsInput,
- ) (jsonutils.JSONObject, error) {
- if self.Status != api.VM_LIVE_MIGRATING {
- return nil, httperrors.NewServerStatusError("cannot set migrate params in status %s", self.Status)
- }
- if input.MaxBandwidthMB == nil && input.DowntimeLimitMS == nil {
- return nil, httperrors.NewInputParameterError("empty input")
- }
- arguments := map[string]interface{}{}
- if input.MaxBandwidthMB != nil {
- arguments["max-bandwidth"] = *input.MaxBandwidthMB * 1024 * 1024
- }
- if input.DowntimeLimitMS != nil {
- arguments["downtime-limit"] = *input.DowntimeLimitMS
- }
- cmd := map[string]interface{}{
- "execute": "migrate-set-parameters",
- "arguments": arguments,
- }
- log.Infof("set live migrate params input: %s", jsonutils.Marshal(cmd).String())
- monitorInput := &api.ServerMonitorInput{
- COMMAND: jsonutils.Marshal(cmd).String(),
- QMP: true,
- }
- return self.SendMonitorCommand(ctx, userCred, monitorInput)
- }
- // 取消在线迁移
- func (self *SGuest) PerformCancelLiveMigrate(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if self.Status != api.VM_LIVE_MIGRATING {
- return nil, httperrors.NewServerStatusError("cannot set migrate params in status %s", self.Status)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- return nil, driver.RequestCancelLiveMigrate(ctx, self, userCred)
- }
- // 克隆虚拟机
- func (self *SGuest) PerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.IsEncrypted() {
- return nil, httperrors.NewForbiddenError("cannot clone encrypted server")
- }
- if len(self.BackupHostId) > 0 {
- return nil, httperrors.NewBadRequestError("Can't clone guest with backup guest")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.IsSupportGuestClone() {
- return nil, httperrors.NewBadRequestError("Guest hypervisor %s does not support clone", self.Hypervisor)
- }
- if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- return nil, httperrors.NewInvalidStatusError("Cannot clone VM in status %s", self.Status)
- }
- cloneInput, err := cmdline.FetchServerCreateInputByJSON(data)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Unmarshal input error %s", err)
- }
- if len(cloneInput.Name) == 0 {
- return nil, httperrors.NewMissingParameterError("name")
- }
- err = db.NewNameValidator(ctx, GuestManager, userCred, cloneInput.Name, nil)
- if err != nil {
- return nil, err
- }
- createInput := self.ToCreateInput(ctx, userCred)
- createInput.Name = cloneInput.Name
- createInput.AutoStart = cloneInput.AutoStart
- createInput.EipBw = cloneInput.EipBw
- createInput.Eip = cloneInput.Eip
- createInput.EipChargeType = cloneInput.EipChargeType
- if err := GuestManager.validateEip(ctx, userCred, createInput, createInput.PreferRegion, createInput.PreferManager); err != nil {
- return nil, err
- }
- dataDict := createInput.JSON(createInput)
- // ownerId := db.SOwnerId{DomainId: createInput.Domain, ProjectId: createInput.Project}
- model, err := db.DoCreate(GuestManager, ctx, userCred, query, dataDict, userCred)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- func() {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- model.PostCreate(ctx, userCred, userCred, query, dataDict)
- }()
- db.OpsLog.LogEvent(model, db.ACT_CREATE, model.GetShortDesc(ctx), userCred)
- logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CREATE, "", userCred, true)
- pendingUsage, pendingRegionUsage := getGuestResourceRequirements(ctx, userCred, *createInput, userCred, 1, false)
- task, err := taskman.TaskManager.NewTask(ctx,
- "GuestCloneTask",
- model.(db.IStandaloneModel),
- userCred,
- dataDict,
- "", "",
- &pendingUsage, &pendingRegionUsage)
- if err != nil {
- log.Errorln(err)
- return nil, err
- }
- task.ScheduleRun(nil)
- return nil, nil
- }
- // 重置密码
- func (self *SGuest) PerformSetPassword(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetPasswordInput) (jsonutils.JSONObject, error) {
- if self.Hypervisor == api.HYPERVISOR_KVM && self.Status == api.VM_RUNNING {
- inputQga := &api.ServerQgaSetPasswordInput{
- Username: input.Username,
- Password: input.Password,
- }
- if inputQga.Username == "" {
- if self.OsType == osprofile.OS_TYPE_WINDOWS {
- inputQga.Username = api.VM_DEFAULT_WINDOWS_LOGIN_USER
- } else {
- inputQga.Username = api.VM_DEFAULT_LINUX_LOGIN_USER
- }
- }
- if inputQga.Password == "" && input.ResetPassword {
- inputQga.Password = seclib2.RandomPassword2(12)
- }
- return self.PerformQgaSetPassword(ctx, userCred, query, inputQga)
- } else {
- inputDeploy := api.ServerDeployInput{}
- inputDeploy.AutoStart = input.AutoStart
- inputDeploy.Password = input.Password
- inputDeploy.ResetPassword = input.ResetPassword
- if input.Username != "" {
- inputDeploy.LoginAccount = input.Username
- }
- return self.PerformDeploy(ctx, userCred, query, inputDeploy)
- }
- }
- func (self *SGuest) GetDetailsCreateParams(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- input := self.ToCreateInput(ctx, userCred)
- return input.JSON(input), nil
- }
- func (self *SGuest) saveOldPassword(ctx context.Context, userCred mcclient.TokenCredential) {
- loginKey := self.GetMetadata(ctx, api.VM_METADATA_LOGIN_KEY, userCred)
- if len(loginKey) > 0 {
- password, err := utils.DescryptAESBase64(self.Id, loginKey)
- if err == nil && len(password) <= 30 {
- for _, r := range password {
- if !unicode.IsPrint(r) {
- return
- }
- }
- self.SetMetadata(ctx, api.VM_METADATA_LAST_LOGIN_KEY, loginKey, userCred)
- }
- }
- }
- func (self *SGuest) GetOldPassword(ctx context.Context, userCred mcclient.TokenCredential) string {
- loginSecret := self.GetMetadata(ctx, api.VM_METADATA_LAST_LOGIN_KEY, userCred)
- password, _ := utils.DescryptAESBase64(self.Id, loginSecret)
- return password
- }
- // 重置密码或秘钥信息&探测虚拟机镜像
- func (self *SGuest) PerformDeploy(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerDeployInput,
- ) (jsonutils.JSONObject, error) {
- self.saveOldPassword(ctx, userCred)
- if input.DeleteKeypair || len(input.KeypairId) > 0 {
- if len(input.KeypairId) > 0 {
- _, err := validators.ValidateModel(ctx, userCred, KeypairManager, &input.KeypairId)
- if err != nil {
- return nil, err
- }
- }
- if self.KeypairId != input.KeypairId {
- okey := self.getKeypair()
- if okey != nil {
- input.DeletePublicKey = okey.PublicKey
- }
- diff, err := db.Update(self, func() error {
- self.KeypairId = input.KeypairId
- return nil
- })
- if err != nil {
- return nil, httperrors.NewInternalServerError("update keypairId %v", err)
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
- input.ResetPassword = true
- }
- }
- if len(input.Password) > 0 {
- err := seclib2.ValidatePassword(input.Password)
- if err != nil {
- return nil, err
- }
- input.ResetPassword = true
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- // 变更密码/密钥时需要Restart才能生效。更新普通字段不需要Restart, Azure需要在运行状态下操作
- doRestart := false
- if input.ResetPassword {
- doRestart = driver.IsNeedRestartForResetLoginInfo()
- if len(input.LoginAccount) > 0 {
- if len(input.LoginAccount) > 32 {
- return nil, httperrors.NewInputParameterError("login_account is longer than 32 chars")
- }
- if err := GuestManager.ValidateNameLoginAccount(input.LoginAccount); err != nil {
- return nil, err
- }
- }
- }
- deployStatus, err := driver.GetDeployStatus()
- if err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- if utils.IsInStringArray(self.Status, deployStatus) {
- if (doRestart && self.Status == api.VM_RUNNING) || (self.Status != api.VM_RUNNING && (input.AutoStart || input.Restart)) {
- input.Restart = true
- } else {
- // 避免前端直接传restart参数, 越过校验
- input.Restart = false
- }
- err := self.StartGuestDeployTask(ctx, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), "deploy", "")
- if err != nil {
- return nil, err
- }
- return nil, nil
- }
- return nil, httperrors.NewServerStatusError("Cannot deploy in status %s", self.Status)
- }
- func (self *SGuest) ValidateAttachDisk(ctx context.Context, disk *SDisk) error {
- storage, _ := disk.GetStorage()
- host, _ := self.GetHost()
- if provider := storage.GetCloudprovider(); provider != nil {
- if provider.Id != host.ManagerId {
- return httperrors.NewInputParameterError("Disk %s and guest not belong to the same account", disk.Name)
- }
- if storage.ZoneId != host.ZoneId {
- return httperrors.NewInputParameterError("Disk %s and guest not belong to the same zone", disk.Name)
- }
- }
- if disk.IsLocal() {
- attached, err := disk.isAttached()
- if err != nil {
- return httperrors.NewInternalServerError("isAttached check failed %s", err)
- }
- if attached {
- return httperrors.NewInputParameterError("Disk %s has been attached", disk.Name)
- }
- }
- if len(disk.GetPathAtHost(host)) == 0 {
- return httperrors.NewInputParameterError("Disk %s not belong the guest's host", disk.Name)
- }
- if disk.Status != api.DISK_READY {
- return httperrors.NewInputParameterError("Disk in %s not able to attach", disk.Status)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- guestStatus, err := driver.GetAttachDiskStatus()
- if err != nil {
- return err
- }
- if !utils.IsInStringArray(self.Status, guestStatus) {
- return httperrors.NewInputParameterError("Guest %s not support attach disk in status %s", self.Name, self.Status)
- }
- ok, err := self.IsInSameClass(ctx, &disk.SStandaloneAnonResourceBase)
- if err != nil {
- return err
- }
- if self.EncryptKeyId != disk.EncryptKeyId {
- return errors.Wrapf(httperrors.ErrConflict, "conflict encryption key between server and disk")
- }
- if !ok {
- return httperrors.NewForbiddenError("the class metadata of guest and disk is different")
- }
- return nil
- }
- // 绑定磁盘
- func (self *SGuest) PerformAttachdisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerAttachDiskInput) (jsonutils.JSONObject, error) {
- if len(input.DiskId) == 0 {
- return nil, httperrors.NewMissingParameterError("disk_id")
- }
- if input.BootIndex != nil {
- if isDup, err := self.isBootIndexDuplicated(*input.BootIndex); err != nil {
- return nil, err
- } else if isDup {
- return nil, httperrors.NewInputParameterError("boot index %d is duplicated", *input.BootIndex)
- }
- }
- diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
- if err != nil {
- return nil, err
- }
- if err := self.ValidateAttachDisk(ctx, diskObj.(*SDisk)); err != nil {
- return nil, err
- }
- taskData := jsonutils.NewDict()
- taskData.Add(jsonutils.NewString(input.DiskId), "disk_id")
- if input.BootIndex != nil && *input.BootIndex >= 0 {
- taskData.Add(jsonutils.NewInt(int64(*input.BootIndex)), "boot_index")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- self.SetStatus(ctx, userCred, api.VM_ATTACH_DISK, "")
- return nil, driver.StartGuestAttachDiskTask(ctx, userCred, self, taskData, "")
- }
- func (self *SGuest) StartRestartNetworkTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, ip string, inBlockStream bool) error {
- data := jsonutils.NewDict()
- data.Set("ip", jsonutils.NewString(ip))
- data.Set("in_block_stream", jsonutils.NewBool(inBlockStream))
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestRestartNetworkTask", self, userCred, data, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) StartQgaRestartNetworkTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId, device, ipMask, gateway, ip6Mask, gateway6 string) error {
- data := jsonutils.NewDict()
- data.Set("device", jsonutils.NewString(device))
- if len(ipMask) > 0 {
- data.Set("ip_mask", jsonutils.NewString(ipMask))
- data.Set("gateway", jsonutils.NewString(gateway))
- }
- if len(ip6Mask) > 0 {
- data.Set("ip6_mask", jsonutils.NewString(ip6Mask))
- data.Set("gateway6", jsonutils.NewString(gateway6))
- }
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestQgaRestartNetworkTask", self, userCred, data, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) startSyncTask(ctx context.Context, userCred mcclient.TokenCredential, firewallOnly bool, parentTaskId string, data *jsonutils.JSONDict) error {
- if firewallOnly {
- data.Add(jsonutils.JSONTrue, "fw_only")
- } else if err := self.SetStatus(ctx, userCred, api.VM_SYNC_CONFIG, ""); err != nil {
- log.Errorln(err)
- return err
- }
- return self.doSyncTask(ctx, data, userCred, parentTaskId)
- }
- func (self *SGuest) StartSyncTask(ctx context.Context, userCred mcclient.TokenCredential, firewallOnly bool, parentTaskId string) error {
- return self.startSyncTask(ctx, userCred, firewallOnly, parentTaskId, jsonutils.NewDict())
- }
- func (self *SGuest) StartSyncTaskWithoutSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, fwOnly bool, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Set("without_sync_status", jsonutils.JSONTrue)
- data.Set("fw_only", jsonutils.NewBool(fwOnly))
- return self.doSyncTask(ctx, data, userCred, parentTaskId)
- }
- func (self *SGuest) doSyncTask(ctx context.Context, data *jsonutils.JSONDict, userCred mcclient.TokenCredential, parentTaskId string) error {
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestSyncConfTask", self, userCred, data, parentTaskId, "", nil); err != nil {
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- // 挂起虚拟机
- func (self *SGuest) PerformSuspend(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Status == api.VM_RUNNING {
- err := self.StartSuspendTask(ctx, userCred, "")
- return nil, err
- }
- return nil, httperrors.NewInvalidStatusError("Cannot suspend VM in status %s", self.Status)
- }
- func (self *SGuest) StartSuspendTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- err := self.SetStatus(ctx, userCred, api.VM_START_SUSPEND, "do suspend")
- if err != nil {
- return err
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartSuspendTask(ctx, userCred, self, nil, parentTaskId)
- }
- // 恢复虚拟机挂起状态
- func (self *SGuest) PerformResume(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResumeInput) (jsonutils.JSONObject, error) {
- if self.Status == api.VM_SUSPEND {
- err := self.StartResumeTask(ctx, userCred, "")
- return nil, err
- }
- return nil, httperrors.NewInvalidStatusError("Cannot resume VM in status %s", self.Status)
- }
- func (self *SGuest) StartResumeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- err := self.SetStatus(ctx, userCred, api.VM_RESUMING, "do resume")
- if err != nil {
- return err
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartResumeTask(ctx, userCred, self, nil, parentTaskId)
- }
- func (self *SGuest) PerformRestoreVirtualIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- config := self.GetMetadataJson(ctx, api.VM_METADATA_VIRTUAL_ISOLATED_DEVICE_CONFIG, userCred)
- if config == nil {
- return nil, nil
- }
- devConfigs := make([]api.IsolatedDeviceConfig, 0)
- err := config.Unmarshal(&devConfigs)
- if err != nil {
- return nil, errors.Wrap(err, "unmarshal virtual dev configs")
- }
- devs, err := self.GetIsolatedDevices()
- if err != nil {
- return nil, errors.Wrap(err, "GetIsolatedDevices")
- }
- devCount := map[string]int{}
- for i := range devConfigs {
- key := devConfigs[i].DevType + "-" + devConfigs[i].Model
- if cnt, ok := devCount[key]; ok {
- devCount[key] = cnt + 1
- } else {
- devCount[key] = 1
- }
- }
- for i := range devs {
- key := devConfigs[i].DevType + "-" + devConfigs[i].Model
- if cnt, ok := devCount[key]; ok {
- devCount[key] = cnt - 1
- }
- }
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "get host")
- }
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- usedDeviceMap := map[string]*SIsolatedDevice{}
- for key, cnt := range devCount {
- if cnt <= 0 {
- continue
- }
- segs := strings.SplitN(key, "-", 2)
- devConfig := &api.IsolatedDeviceConfig{
- Model: segs[1],
- DevType: segs[0],
- }
- err := IsolatedDeviceManager.attachHostDeviceToGuestByModel(ctx, self, host, devConfig, userCred, usedDeviceMap, nil)
- if err != nil {
- return nil, errors.Wrap(err, "attachHostDeviceToGuestByModel")
- }
- }
- return nil, nil
- }
- // 开机
- func (self *SGuest) PerformStart(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.GuestPerformStartInput,
- ) (jsonutils.JSONObject, error) {
- validStartStatuses := []string{api.VM_READY, api.VM_START_FAILED, api.VM_SAVE_DISK_FAILED, api.VM_SUSPEND, api.VM_KICKSTART_PENDING, api.VM_KICKSTART_FAILED}
- if utils.IsInStringArray(self.Status, validStartStatuses) {
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- if !self.guestDisksStorageTypeIsShared() {
- host, _ := self.GetHost()
- guestStats, err := host.GetNotReadyGuestsStat()
- if err != nil {
- return nil, err
- }
- if float32(guestStats.GuestVcpuCount+self.VcpuCount) > host.GetVirtualCPUCount() {
- log.Debugf("GuestPerformStart: guestStats: %s host: %f request: %d", jsonutils.Marshal(guestStats), host.GetVirtualCPUCount(), self.VcpuCount)
- return nil, httperrors.NewInsufficientResourceError("host virtual cpu not enough")
- }
- if float32(guestStats.GuestVmemSize+self.VmemSize) > host.GetVirtualMemorySize() {
- log.Debugf("GuestPerformStart: guestStats: %s host: %f request: %d", jsonutils.Marshal(guestStats), host.GetVirtualMemorySize(), self.VmemSize)
- return nil, httperrors.NewInsufficientResourceError("host virtual memory not enough")
- }
- }
- if self.isAllDisksReady() {
- kwargs := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- err = driver.PerformStart(ctx, userCred, self, kwargs, "")
- return nil, err
- } else {
- return nil, httperrors.NewInvalidStatusError("Some disk not ready")
- }
- } else {
- return nil, httperrors.NewInvalidStatusError("Cannot do start server in status %s", self.Status)
- }
- }
- func (self *SGuest) StartGuestDeployTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- kwargs *jsonutils.JSONDict, action string, parentTaskId string,
- ) error {
- self.SetStatus(ctx, userCred, api.VM_START_DEPLOY, "")
- if kwargs == nil {
- kwargs = jsonutils.NewDict()
- }
- kwargs.Add(jsonutils.NewString(action), "deploy_action")
- taskName := "GuestDeployTask"
- if self.BackupHostId != "" {
- taskName = "HAGuestDeployTask"
- }
- task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, kwargs, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) EventNotify(ctx context.Context, userCred mcclient.TokenCredential, action noapi.SAction) {
- detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
- if action != notifyclient.ActionCreate && action != notifyclient.ActionRebuildRoot && action != notifyclient.ActionResetPassword {
- return
- }
- meta, err := self.GetAllMetadata(ctx, userCred)
- if err != nil {
- return
- }
- details.Add(jsonutils.NewString(self.getNotifyIps()), "ips")
- osName := meta[api.VM_METADATA_OS_NAME]
- if osName == "Windows" {
- details.Add(jsonutils.JSONTrue, "windows")
- }
- loginAccount := meta[api.VM_METADATA_LOGIN_ACCOUNT]
- if len(loginAccount) > 0 {
- details.Add(jsonutils.NewString(loginAccount), "login_account")
- }
- keypair := self.getKeypairName()
- if len(keypair) > 0 {
- details.Add(jsonutils.NewString(keypair), "keypair")
- } else {
- loginKey := meta[api.VM_METADATA_LOGIN_KEY]
- if len(loginKey) > 0 {
- passwd, err := utils.DescryptAESBase64(self.Id, loginKey)
- if err == nil {
- details.Add(jsonutils.NewString(passwd), "password")
- }
- }
- }
- }
- var resourceType string
- if self.Hypervisor == api.HYPERVISOR_BAREMETAL {
- resourceType = noapi.TOPIC_RESOURCE_BAREMETAL
- }
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: self,
- ResourceType: resourceType,
- Action: action,
- ObjDetailsDecorator: detailsDecro,
- AdvanceDays: 0,
- })
- }
- func (self *SGuest) NotifyServerEvent(
- ctx context.Context, userCred mcclient.TokenCredential, event string, priority notify.TNotifyPriority,
- loginInfo bool, kwargs *jsonutils.JSONDict, notifyAdmin bool,
- ) {
- meta, err := self.GetAllMetadata(ctx, userCred)
- if err != nil {
- return
- }
- if kwargs == nil {
- kwargs = jsonutils.NewDict()
- }
- kwargs.Add(jsonutils.NewString(self.Name), "name")
- kwargs.Add(jsonutils.NewString(self.Hypervisor), "hypervisor")
- host, _ := self.GetHost()
- if host != nil {
- brand := host.GetBrand()
- if brand == api.CLOUD_PROVIDER_ONECLOUD {
- brand = api.ComputeI18nTable.Lookup(ctx, brand)
- }
- kwargs.Add(jsonutils.NewString(brand), "brand")
- }
- if loginInfo {
- kwargs.Add(jsonutils.NewString(self.getNotifyIps()), "ips")
- osName := meta[api.VM_METADATA_OS_NAME]
- if osName == "Windows" {
- kwargs.Add(jsonutils.JSONTrue, "windows")
- }
- loginAccount := meta[api.VM_METADATA_LOGIN_ACCOUNT]
- if len(loginAccount) > 0 {
- kwargs.Add(jsonutils.NewString(loginAccount), "account")
- }
- keypair := self.getKeypairName()
- if len(keypair) > 0 {
- kwargs.Add(jsonutils.NewString(keypair), "keypair")
- } else {
- loginKey := meta[api.VM_METADATA_LOGIN_KEY]
- if len(loginKey) > 0 {
- passwd, err := utils.DescryptAESBase64(self.Id, loginKey)
- if err == nil {
- kwargs.Add(jsonutils.NewString(passwd), "password")
- }
- }
- }
- }
- notifyclient.NotifyWithCtx(ctx, []string{userCred.GetUserId()}, false, priority, event, kwargs)
- if notifyAdmin {
- notifyclient.SystemNotifyWithCtx(ctx, priority, event, kwargs)
- }
- }
- func (self *SGuest) NotifyAdminServerEvent(ctx context.Context, event string, priority notify.TNotifyPriority) {
- kwargs := jsonutils.NewDict()
- kwargs.Add(jsonutils.NewString(self.Name), "name")
- kwargs.Add(jsonutils.NewString(self.Hypervisor), "hypervisor")
- tc, _ := self.GetTenantCache(ctx)
- if tc != nil {
- kwargs.Add(jsonutils.NewString(tc.Name), "tenant")
- } else {
- kwargs.Add(jsonutils.NewString(self.ProjectId), "tenant")
- }
- notifyclient.SystemNotifyWithCtx(ctx, priority, event, kwargs)
- }
- func (self *SGuest) StartGuestStopTask(ctx context.Context, userCred mcclient.TokenCredential, timeoutSecs int, isForce, stopCharging bool, parentTaskId string) error {
- if len(parentTaskId) == 0 {
- self.SetStatus(ctx, userCred, api.VM_START_STOP, "")
- }
- params := jsonutils.NewDict()
- if isForce {
- params.Add(jsonutils.NewBool(isForce), "is_force")
- } else {
- params.Add(jsonutils.NewInt(int64(timeoutSecs)), "timeout")
- }
- params.Add(jsonutils.NewBool(stopCharging), "stop_charging")
- if len(parentTaskId) > 0 {
- params.Add(jsonutils.JSONTrue, "subtask")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- shutdownMode := api.VM_SHUTDOWN_MODE_KEEP_CHARGING
- if stopCharging && driver.IsSupportShutdownMode() {
- shutdownMode = api.VM_SHUTDOWN_MODE_STOP_CHARGING
- }
- _, err = db.Update(self, func() error {
- self.ShutdownMode = shutdownMode
- return nil
- })
- if err != nil {
- return errors.Wrapf(err, "db.Update")
- }
- return driver.StartGuestStopTask(self, ctx, userCred, params, parentTaskId)
- }
- func (self *SGuest) insertIso(imageId string, cdromOrdinal int64) bool {
- cdrom := self.getCdrom(true, cdromOrdinal)
- return cdrom.insertIso(imageId)
- }
- func (self *SGuest) InsertIsoSucc(cdromOrdinal int64, imageId string, path string, size int64, name string, bootIndex *int8) (*SGuestcdrom, bool) {
- cdrom := self.getCdrom(false, cdromOrdinal)
- return cdrom, cdrom.insertIsoSucc(imageId, path, size, name, bootIndex)
- }
- func (self *SGuest) GetDetailsIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- var cdromOrdinal int64 = 0
- if query.Contains("ordinal") {
- cdromOrdinal, _ = query.Int("ordinal")
- }
- cdrom := self.getCdrom(false, cdromOrdinal)
- desc := jsonutils.NewDict()
- if len(cdrom.ImageId) > 0 {
- desc.Set("image_id", jsonutils.NewString(cdrom.ImageId))
- desc.Set("status", jsonutils.NewString("inserting"))
- }
- if len(cdrom.Path) > 0 {
- desc.Set("name", jsonutils.NewString(cdrom.Name))
- desc.Set("size", jsonutils.NewInt(int64(cdrom.Size)))
- desc.Set("status", jsonutils.NewString("ready"))
- }
- return desc, nil
- }
- // 获取Kickstart信息
- func (self *SGuest) GetDetailsKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- result := jsonutils.NewDict()
- kickstartConfig, err := self.GetKickstartConfig(ctx, userCred)
- if err != nil {
- return nil, httperrors.NewInternalServerError("Failed to get kickstart config: %v", err)
- }
- status := self.GetKickstartStatus(ctx, userCred)
- attempt := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, userCred)
- if attempt == "" {
- attempt = "0"
- }
- kickstartType := self.GetKickstartType(ctx, userCred)
- if kickstartConfig != nil {
- configDict := jsonutils.Marshal(kickstartConfig)
- result.Set("config", configDict)
- } else {
- result.Set("config", jsonutils.NewDict())
- }
- result.Set("status", jsonutils.NewString(status))
- result.Set("attempt", jsonutils.NewString(attempt))
- result.Set("type", jsonutils.NewString(kickstartType))
- return result, nil
- }
- // 挂载ISO镜像
- func (self *SGuest) PerformInsertiso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- cdromOrdinal, _ := data.Int("cdrom_ordinal")
- if cdromOrdinal < 0 {
- return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
- }
- cdrom := self.getCdrom(false, cdromOrdinal)
- if cdrom != nil && len(cdrom.ImageId) > 0 {
- return nil, httperrors.NewBadRequestError("CD-ROM not empty, please eject first")
- }
- imageId, _ := data.GetString("image_id")
- image, err := parseIsoInfo(ctx, userCred, imageId)
- if err != nil {
- log.Errorln(err)
- return nil, err
- }
- var bootIndex *int8
- if data.Contains("boot_index") {
- bd, _ := data.Int("boot_index")
- bd8 := int8(bd)
- bootIndex = &bd8
- if isDup, err := self.isBootIndexDuplicated(bd8); err != nil {
- return nil, err
- } else if isDup {
- return nil, httperrors.NewInputParameterError("boot index %d is duplicated", bd8)
- }
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- err = self.StartInsertIsoTask(ctx, cdromOrdinal, image.Id, false, bootIndex, self.HostId, userCred, "")
- return nil, err
- } else {
- return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
- }
- }
- // 卸载ISO镜像
- func (self *SGuest) PerformEjectiso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- cdromOrdinal, _ := data.Int("cdrom_ordinal")
- if cdromOrdinal < 0 {
- return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
- }
- cdrom := self.getCdrom(false, cdromOrdinal)
- if cdrom == nil || len(cdrom.ImageId) == 0 {
- return nil, httperrors.NewBadRequestError("No ISO to eject")
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- err := self.StartEjectisoTask(ctx, cdromOrdinal, userCred, "")
- return nil, err
- } else {
- return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
- }
- }
- func (self *SGuest) StartEjectisoTask(ctx context.Context, cdromOrdinal int64, userCred mcclient.TokenCredential, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectISOTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) StartInsertIsoTask(ctx context.Context, cdromOrdinal int64, imageId string, boot bool, bootIndex *int8, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
- self.insertIso(imageId, cdromOrdinal)
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewString(imageId), "image_id")
- data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
- data.Add(jsonutils.NewString(hostId), "host_id")
- if boot {
- data.Add(jsonutils.JSONTrue, "boot")
- }
- if bootIndex != nil {
- data.Add(jsonutils.NewInt(int64(*bootIndex)), "boot_index")
- }
- taskName := "GuestInsertIsoTask"
- if self.BackupHostId != "" {
- taskName = "HaGuestInsertIsoTask"
- }
- task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) insertVfd(imageId string, floppyOrdinal int64) bool {
- floppy := self.getFloppy(true, floppyOrdinal)
- return floppy.insertVfd(imageId)
- }
- func (self *SGuest) InsertVfdSucc(floppyOrdinal int64, imageId string, path string, size int64, name string) bool {
- floppy := self.getFloppy(true, floppyOrdinal)
- return floppy.insertVfdSucc(imageId, path, size, name)
- }
- func (self *SGuest) GetDetailsVfd(floppyOrdinal int64, userCred mcclient.TokenCredential) jsonutils.JSONObject {
- floppy := self.getFloppy(false, floppyOrdinal)
- desc := jsonutils.NewDict()
- if len(floppy.ImageId) > 0 {
- desc.Set("image_id", jsonutils.NewString(floppy.ImageId))
- desc.Set("status", jsonutils.NewString("inserting"))
- }
- if len(floppy.Path) > 0 {
- desc.Set("name", jsonutils.NewString(floppy.Name))
- desc.Set("size", jsonutils.NewInt(int64(floppy.Size)))
- desc.Set("status", jsonutils.NewString("ready"))
- }
- return desc
- }
- // 挂载软盘
- func (self *SGuest) PerformInsertvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerInsertVfdInput) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if data.FloppyOrdinal < 0 {
- return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
- }
- floppy := self.getFloppy(false, data.FloppyOrdinal)
- if floppy != nil && len(floppy.ImageId) > 0 {
- return nil, httperrors.NewBadRequestError("Floppy not empty, please eject first")
- }
- image, err := parseIsoInfo(ctx, userCred, data.ImageId)
- if err != nil {
- log.Errorln(err)
- return nil, err
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- err = self.StartInsertVfdTask(ctx, data.FloppyOrdinal, image.Id, false, self.HostId, userCred, "")
- return nil, err
- } else {
- return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
- }
- }
- // 卸载软盘
- func (self *SGuest) PerformEjectvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerEjectVfdInput) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if data.FloppyOrdinal < 0 {
- return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
- }
- floppy := self.getFloppy(false, data.FloppyOrdinal)
- if floppy == nil || len(floppy.ImageId) == 0 {
- return nil, httperrors.NewBadRequestError("No VFD to eject")
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- err := self.StartEjectvfdTask(ctx, userCred, "")
- return nil, err
- } else {
- return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
- }
- }
- func (self *SGuest) StartEjectvfdTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectVFDTask", self, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) StartInsertVfdTask(ctx context.Context, floppyOrdinal int64, imageId string, boot bool, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
- self.insertVfd(imageId, floppyOrdinal)
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewString(imageId), "image_id")
- data.Add(jsonutils.NewString(hostId), "host_id")
- if boot {
- data.Add(jsonutils.JSONTrue, "boot")
- }
- taskName := "GuestInsertVfdTask"
- if self.BackupHostId != "" {
- taskName = "HaGuestInsertVfdTask"
- }
- task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) RebalanceVirtualIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential) error {
- devs, err := self.GetIsolatedDevices()
- if err != nil {
- return errors.Wrap(err, "guest get isolated devices")
- }
- if len(devs) == 0 {
- return nil
- }
- host, err := self.GetHost()
- if err != nil {
- return errors.Wrap(err, "guest get host")
- }
- var numaNodeBalance = true
- detachDevs := make([]SIsolatedDevice, 0)
- originDevConfigs := make([]api.IsolatedDeviceConfig, 0)
- for i := range devs {
- if utils.IsInStringArray(devs[i].DevType, api.VITRUAL_DEVICE_TYPES) {
- originDevConfigs = append(originDevConfigs, api.IsolatedDeviceConfig{
- Model: devs[i].Model,
- DevType: devs[i].DevType,
- })
- detachDevs = append(detachDevs, devs[i])
- isBalance, err := host.VirtualDeviceNumaBalance(devs[i].DevType, devs[i].NumaNode)
- if err != nil {
- return errors.Wrap(err, "VirtualDeviceNumaBalance")
- }
- if numaNodeBalance && !isBalance {
- numaNodeBalance = false
- }
- }
- }
- log.Infof("Guest %s on host %s start virtual devices numa node balance %v", self.Id, host.Id, numaNodeBalance)
- if !numaNodeBalance {
- err := self.SetMetadata(ctx, api.VM_METADATA_VIRTUAL_ISOLATED_DEVICE_CONFIG, originDevConfigs, userCred)
- if err != nil {
- return errors.Wrap(err, "set metadata virtual isolated device config")
- }
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- for i := 0; i < len(detachDevs); i++ {
- err := self.detachIsolateDevice(ctx, userCred, &detachDevs[i])
- if err != nil {
- return errors.Wrapf(err, "detach device %s", detachDevs[i].GetId())
- }
- }
- usedDeviceMap := map[string]*SIsolatedDevice{}
- for i := range detachDevs {
- devConfig := &api.IsolatedDeviceConfig{
- Model: detachDevs[i].Model,
- DevType: detachDevs[i].DevType,
- }
- err := IsolatedDeviceManager.attachHostDeviceToGuestByModel(ctx, self, host, devConfig, userCred, usedDeviceMap, nil)
- if err != nil {
- return errors.Wrap(err, "attachHostDeviceToGuestByModel")
- }
- }
- }
- return nil
- }
- func (self *SGuest) StartGueststartTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- data *jsonutils.JSONDict, parentTaskId string,
- ) error {
- schedStart := self.Hypervisor == api.HYPERVISOR_KVM && self.guestDisksStorageTypeIsShared()
- startFromCreate := false
- if !gotypes.IsNil(data) {
- startFromCreate = jsonutils.QueryBoolean(data, "start_from_create", false)
- }
- if options.Options.IgnoreNonrunningGuests {
- host := HostManager.FetchHostById(self.HostId)
- if !startFromCreate && host != nil && host.EnableNumaAllocate {
- schedStart = true
- }
- }
- if !startFromCreate && self.IsSchedulerNumaAllocate() {
- // clean cpu numa pin
- err := self.SetCpuNumaPin(ctx, userCred, nil, nil)
- if err != nil {
- return errors.Wrap(err, "clean cpu numa pin")
- }
- }
- if options.Options.VirtualDeviceNumaBalance {
- err := self.RebalanceVirtualIsolatedDevices(ctx, userCred)
- if err != nil {
- return errors.Wrap(err, "rebalance virtual isolated devices")
- }
- }
- if schedStart {
- return self.GuestSchedStartTask(ctx, userCred, data, parentTaskId)
- } else {
- return self.GuestNonSchedStartTask(ctx, userCred, data, parentTaskId)
- }
- }
- func (self *SGuest) GuestSchedStartTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- data *jsonutils.JSONDict, parentTaskId string,
- ) error {
- self.SetStatus(ctx, userCred, api.VM_SCHEDULE, "")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestSchedStartTask", self, userCred, data,
- parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) GuestNonSchedStartTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- data *jsonutils.JSONDict, parentTaskId string,
- ) error {
- self.SetStatus(ctx, userCred, api.VM_START_START, "")
- taskName := "GuestStartTask"
- if self.BackupHostId != "" {
- taskName = "HAGuestStartTask"
- }
- if self.IsSchedulerNumaAllocate() {
- srcSchedCpuNumaPin := make([]schedapi.SCpuNumaPin, 0)
- err := self.CpuNumaPin.Unmarshal(&srcSchedCpuNumaPin)
- if err != nil {
- return errors.Wrap(err, "unmarshal cpu_numa_pin")
- }
- // set cpu numa pin
- err = self.SetCpuNumaPin(ctx, userCred, srcSchedCpuNumaPin, nil)
- if err != nil {
- return nil
- }
- }
- task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) StartGuestCreateTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput, pendingUsage quotas.IQuota, parentTaskId string) error {
- if input.FakeCreate {
- self.fixFakeServerInfo(ctx, userCred)
- return nil
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartGuestCreateTask(self, ctx, userCred, input.JSON(input), pendingUsage, parentTaskId)
- }
- func (self *SGuest) fixFakeServerInfo(ctx context.Context, userCred mcclient.TokenCredential) {
- status := []string{api.VM_READY, api.VM_RUNNING}
- rand.Seed(time.Now().Unix())
- db.Update(self, func() error {
- self.Status = status[rand.Intn(len(status))]
- self.PowerStates = api.VM_POWER_STATES_ON
- if self.Status == api.VM_READY {
- self.PowerStates = api.VM_POWER_STATES_OFF
- }
- return nil
- })
- disks, _ := self.GetDisks()
- for i := range disks {
- db.Update(&disks[i], func() error {
- disks[i].Status = api.DISK_READY
- return nil
- })
- }
- networks, _ := self.GetNetworks("")
- for i := range networks {
- if len(networks[i].IpAddr) > 0 {
- continue
- }
- network, _ := networks[i].GetNetwork()
- if network != nil {
- db.Update(&networks[i], func() error {
- networks[i].IpAddr, _ = network.GetFreeIP(ctx, userCred, nil, nil, "", api.IPAllocationRandom, false, api.AddressTypeIPv4)
- return nil
- })
- }
- }
- if eip, _ := self.GetEipOrPublicIp(); eip != nil && len(eip.IpAddr) == 0 {
- db.Update(eip, func() error {
- if len(eip.NetworkId) > 0 {
- if network, _ := eip.GetNetwork(); network != nil {
- eip.IpAddr, _ = network.GetFreeIP(ctx, userCred, nil, nil, "", api.IPAllocationRandom, false, api.AddressTypeIPv4)
- return nil
- }
- }
- return nil
- })
- }
- }
- func (self *SGuest) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- if len(parentTaskId) == 0 {
- self.SetStatus(ctx, userCred, apis.STATUS_START_SYNC_STATUS, "")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartGuestSyncstatusTask(self, ctx, userCred, parentTaskId)
- }
- func (self *SGuest) StartAutoDeleteGuestTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- db.OpsLog.LogEvent(self, db.ACT_DELETE, "auto-delete after stop", userCred)
- opts := api.ServerDeleteInput{}
- return self.StartDeleteGuestTask(ctx, userCred, parentTaskId, opts)
- }
- func (self *SGuest) StartDeleteGuestTask(
- ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string,
- opts api.ServerDeleteInput,
- ) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(self.Status), "guest_status")
- params.Update(jsonutils.Marshal(opts))
- self.SetStatus(ctx, userCred, api.VM_START_DELETE, "")
- if self.HostId != "" {
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- return driver.StartDeleteGuestTask(ctx, userCred, self, params, parentTaskId)
- } else {
- task, err := taskman.TaskManager.NewTask(ctx, "GuestDeleteWithoutHostTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask GuestDeleteWithoutHostTask")
- }
- task.ScheduleRun(nil)
- return nil
- }
- }
- // 清除虚拟机记录(仅数据库操作)
- func (self *SGuest) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- err := self.validateDeleteCondition(ctx, true)
- if err != nil {
- return nil, err
- }
- host, _ := self.GetHost()
- if host != nil && host.GetEnabled() {
- return nil, httperrors.NewInvalidStatusError("Cannot purge server on enabled host")
- }
- opts := api.ServerDeleteInput{
- Purge: true,
- }
- err = self.StartDeleteGuestTask(ctx, userCred, "", opts)
- return nil, err
- }
- func (self *SGuest) setKeypairId(userCred mcclient.TokenCredential, keypairId string) error {
- diff, err := db.Update(self, func() error {
- self.KeypairId = keypairId
- return nil
- })
- if err != nil {
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
- }
- return err
- }
- // 重装系统(更换系统镜像)
- func (self *SGuest) PerformRebuildRoot(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input *api.ServerRebuildRootInput,
- ) (*api.SGuest, error) {
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- input, err = driver.ValidateRebuildRoot(ctx, userCred, self, input)
- if err != nil {
- return nil, err
- }
- if len(input.UserData) > 0 {
- // validate UserData
- if err := userdata.ValidateUserdata(input.UserData, self.OsType); err != nil {
- return nil, httperrors.NewInputParameterError("Invalid userdata: %v", err)
- }
- self.setUserData(ctx, userCred, input.UserData)
- }
- if len(input.ImageId) > 0 {
- img, err := CachedimageManager.getImageInfo(ctx, userCred, input.ImageId, false)
- if err != nil {
- return nil, httperrors.NewNotFoundError("failed to find %s", input.ImageId)
- }
- err = driver.ValidateImage(ctx, img)
- if err != nil {
- return nil, err
- }
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrapf(err, "GetHost")
- }
- region, err := host.GetRegion()
- if err != nil {
- return nil, errors.Wrapf(err, "GetRegion")
- }
- // compare os arch
- if len(img.Properties["os_arch"]) > 0 && len(self.OsArch) > 0 && !apis.IsSameArch(self.OsArch, img.Properties["os_arch"]) {
- return nil, httperrors.NewConflictError("root disk image(%s) and guest(%s) OsArch mismatch", img.Properties["os_arch"], self.OsArch)
- } else if len(self.InstanceType) > 0 {
- sku, _ := ServerSkuManager.FetchSkuByNameAndProvider(self.InstanceType, region.Provider, true)
- if sku != nil && len(sku.CpuArch) > 0 && len(img.Properties["os_arch"]) > 0 && !apis.IsSameArch(img.Properties["os_arch"], sku.CpuArch) {
- return nil, httperrors.NewConflictError("root disk image(%s) and sku(%s) architecture mismatch", img.Properties["os_arch"], sku.CpuArch)
- }
- }
- diskCat := self.CategorizeDisks()
- if gotypes.IsNil(diskCat.Root) {
- return nil, httperrors.NewInputParameterError("no root disk is found")
- }
- if img.MinDiskMB == 0 || img.Status != imageapi.IMAGE_STATUS_ACTIVE {
- return nil, httperrors.NewInputParameterError("invlid image")
- }
- if img.MinDiskMB > diskCat.Root.DiskSize {
- return nil, httperrors.NewInputParameterError("image size exceeds root disk size")
- }
- osType, _ := img.Properties["os_type"]
- osName := self.GetMetadata(ctx, "os_name", userCred)
- if len(osName) == 0 && len(osType) == 0 && strings.ToLower(osType) != strings.ToLower(osName) {
- return nil, httperrors.NewBadRequestError("Cannot switch OS between %s-%s", osName, osType)
- }
- input.ImageId = img.Id
- }
- templateId := self.GetTemplateId()
- if templateId != input.ImageId && len(templateId) > 0 && len(input.ImageId) > 0 && !driver.IsRebuildRootSupportChangeUEFI() {
- q := CachedimageManager.Query().In("id", []string{input.ImageId, templateId})
- images := []SCachedimage{}
- err := db.FetchModelObjects(CachedimageManager, q, &images)
- if err != nil {
- return nil, errors.Wrap(err, "FetchModelObjects")
- }
- if len(images) == 2 && images[0].UEFI != images[1].UEFI {
- return nil, httperrors.NewUnsupportOperationError("Can not rebuild root with with diff uefi image")
- }
- }
- rebuildStatus, err := driver.GetRebuildRootStatus()
- if err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- if !driver.IsRebuildRootSupportChangeImage() && len(input.ImageId) > 0 {
- if len(templateId) == 0 {
- return nil, httperrors.NewBadRequestError("No template for root disk, cannot rebuild root")
- }
- if input.ImageId != templateId {
- return nil, httperrors.NewInputParameterError("%s not support rebuild root with a different image", driver.GetHypervisor())
- }
- }
- if !utils.IsInStringArray(self.Status, rebuildStatus) {
- return nil, httperrors.NewInvalidStatusError("Cannot reset root in status %s", self.Status)
- }
- if self.Status == api.VM_READY && self.ShutdownMode == api.VM_SHUTDOWN_MODE_STOP_CHARGING {
- return nil, httperrors.NewInvalidStatusError("Cannot reset root with %s", self.ShutdownMode)
- }
- autoStart := false
- if input.AutoStart != nil {
- autoStart = *input.AutoStart
- }
- var needStop = false
- if self.Status == api.VM_RUNNING {
- needStop = true
- }
- resetPasswd := input.ResetPassword
- passwd := input.Password
- if len(passwd) > 0 {
- err = seclib2.ValidatePassword(passwd)
- if err != nil {
- return nil, errors.Wrap(err, "ValidatePassword")
- }
- }
- if len(input.KeypairId) > 0 {
- _, err := validators.ValidateModel(ctx, userCred, KeypairManager, &input.KeypairId)
- if err != nil {
- return nil, err
- }
- if self.KeypairId != input.KeypairId {
- err = self.setKeypairId(userCred, input.KeypairId)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- resetPasswd = true
- } else {
- err = self.setKeypairId(userCred, "")
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- allDisks := false
- if input.AllDisks != nil {
- allDisks = *input.AllDisks
- }
- input.ResetPassword = resetPasswd
- if input.ResetPassword {
- if len(input.LoginAccount) > 0 {
- if len(input.LoginAccount) > 32 {
- return nil, httperrors.NewInputParameterError("login_account is longer than 32 chars")
- }
- if err := GuestManager.ValidateNameLoginAccount(input.LoginAccount); err != nil {
- return nil, err
- }
- }
- }
- return nil, self.StartRebuildRootTask(ctx, userCred, input.ImageId, needStop, autoStart, allDisks, &input.ServerDeployInputBase)
- }
- func (self *SGuest) GetTemplateId() string {
- gdc := self.CategorizeDisks()
- if gdc.Root != nil {
- return gdc.Root.GetTemplateId()
- }
- return ""
- }
- func (self *SGuest) StartRebuildRootTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, needStop, autoStart bool, allDisk bool, deployInput *api.ServerDeployInputBase) error {
- data := jsonutils.NewDict()
- if len(imageId) == 0 {
- imageId = self.GetTemplateId()
- if len(imageId) == 0 {
- return httperrors.NewBadRequestError("No template for root disk, cannot rebuild root")
- }
- }
- data.Set("image_id", jsonutils.NewString(imageId))
- if needStop {
- data.Set("need_stop", jsonutils.JSONTrue)
- }
- if autoStart {
- data.Set("auto_start", jsonutils.JSONTrue)
- }
- /*if resetPasswd {
- data.Set("reset_password", jsonutils.JSONTrue)
- } else {
- data.Set("reset_password", jsonutils.JSONFalse)
- }
- if len(passwd) > 0 {
- data.Set("password", jsonutils.NewString(passwd))
- }*/
- if allDisk {
- data.Set("all_disks", jsonutils.JSONTrue)
- } else {
- data.Set("all_disks", jsonutils.JSONFalse)
- }
- data.Set("deploy_params", jsonutils.Marshal(deployInput))
- self.SetStatus(ctx, userCred, api.VM_REBUILD_ROOT, "request start rebuild root")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestRebuildRootTask", self, userCred, data, "", "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) DetachDisk(ctx context.Context, disk *SDisk, userCred mcclient.TokenCredential) {
- guestdisk := self.GetGuestDisk(disk.Id)
- if guestdisk != nil {
- guestdisk.Detach(ctx, userCred)
- }
- }
- // 创建虚拟机磁盘
- func (self *SGuest) PerformCreatedisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- diskSize := 0
- disksConf := make([]*api.DiskConfig, 0)
- diskSizes := make(map[string]int, 0)
- diskDefArray, err := cmdline.FetchDiskConfigsByJSON(data)
- if err != nil {
- return nil, err
- }
- if len(diskDefArray) == 0 {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No Disk Info Provided", userCred, false)
- return nil, httperrors.NewBadRequestError("No Disk Info Provided")
- }
- for diskIdx := 0; diskIdx < len(diskDefArray); diskIdx += 1 {
- diskInfo, err := parseDiskInfo(ctx, userCred, diskDefArray[diskIdx])
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
- return nil, httperrors.NewBadRequestError("%v", err)
- }
- if len(diskInfo.Backend) == 0 {
- diskInfo.Backend = self.getDefaultStorageType()
- }
- disksConf = append(disksConf, diskInfo)
- if _, ok := diskSizes[diskInfo.Backend]; !ok {
- diskSizes[diskInfo.Backend] = diskInfo.SizeMb
- } else {
- diskSizes[diskInfo.Backend] += diskInfo.SizeMb
- }
- diskSize += diskInfo.SizeMb
- }
- host, _ := self.GetHost()
- if host == nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No valid host", userCred, false)
- return nil, httperrors.NewBadRequestError("No valid host")
- }
- for backend, size := range diskSizes {
- storage := host.GetLeastUsedStorage(backend)
- if storage == nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No valid storage on current host", userCred, false)
- return nil, httperrors.NewBadRequestError("No valid storage on current host")
- }
- if storage.GetCapacity() > 0 && storage.GetCapacity() < int64(size) {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "Not eough storage space on current host", userCred, false)
- return nil, httperrors.NewBadRequestError("Not eough storage space on current host")
- }
- }
- pendingUsage := &SQuota{
- Storage: diskSize,
- }
- keys, err := self.GetQuotaKeys()
- if err != nil {
- return nil, err
- }
- pendingUsage.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
- return nil, httperrors.NewOutOfQuotaError("%v", err)
- }
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- err = self.CreateDisksOnHost(ctx, userCred, host, disksConf, pendingUsage, false, options.Options.UseServerTagsForDisk, nil, nil, false)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
- return nil, httperrors.NewBadRequestError("%v", err)
- }
- err = self.StartGuestCreateDiskTask(ctx, userCred, disksConf, "")
- return nil, err
- }
- func (self *SGuest) StartGuestCreateDiskTask(ctx context.Context, userCred mcclient.TokenCredential, disks []*api.DiskConfig, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Add(jsonutils.Marshal(disks), "disks")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateDiskTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrapf(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- // 卸载磁盘
- func (self *SGuest) PerformDetachdisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDetachDiskInput) (jsonutils.JSONObject, error) {
- if len(input.DiskId) == 0 {
- return nil, httperrors.NewMissingParameterError("disk_id")
- }
- diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
- if err != nil {
- return nil, err
- }
- disk := diskObj.(*SDisk)
- attached, err := self.isAttach2Disk(disk)
- if err != nil {
- return nil, httperrors.NewInternalServerError("check isAttach2Disk fail %s", err)
- }
- if !attached {
- return nil, nil
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- detachDiskStatus, err := driver.GetDetachDiskStatus()
- if err != nil {
- return nil, err
- }
- if input.KeepDisk && !driver.CanKeepDetachDisk() {
- return nil, httperrors.NewInputParameterError("Cannot keep detached disk")
- }
- err = driver.ValidateDetachDisk(ctx, userCred, self, disk)
- if err != nil {
- return nil, err
- }
- if utils.IsInStringArray(self.Status, detachDiskStatus) {
- self.SetStatus(ctx, userCred, api.VM_DETACH_DISK, "")
- err = self.StartGuestDetachdiskTask(ctx, userCred, disk, input.KeepDisk, "", false, false)
- return nil, err
- }
- return nil, httperrors.NewInvalidStatusError("Server in %s not able to detach disk", self.Status)
- }
- func (self *SGuest) StartGuestDetachdiskTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- disk *SDisk, keepDisk bool, parentTaskId string, purge bool, syncDescOnly bool,
- ) error {
- taskData := jsonutils.NewDict()
- taskData.Add(jsonutils.NewString(disk.Id), "disk_id")
- taskData.Add(jsonutils.NewBool(keepDisk), "keep_disk")
- taskData.Add(jsonutils.NewBool(purge), "purge")
- // finally reuse fw_only option
- taskData.Add(jsonutils.NewBool(syncDescOnly), "sync_desc_only")
- if utils.IsInStringArray(disk.Status, []string{api.DISK_INIT, api.DISK_ALLOC_FAILED}) {
- //删除非正常状态下的disk
- taskData.Add(jsonutils.JSONFalse, "keep_disk")
- db.Update(disk, func() error {
- disk.AutoDelete = true
- return nil
- })
- }
- driver, err := self.GetDriver()
- if err != nil {
- return errors.Wrapf(err, "GetDriver")
- }
- disk.SetStatus(ctx, userCred, api.DISK_DETACHING, "")
- return driver.StartGuestDetachdiskTask(ctx, userCred, self, taskData, parentTaskId)
- }
- func (self *SGuest) GetReleasedIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential) ([]api.ServerReleasedIsolatedDevice, error) {
- devs := make([]api.ServerReleasedIsolatedDevice, 0)
- if ret := self.GetMetadata(ctx, api.VM_METADATA_RELEASED_DEVICES, userCred); ret == "" {
- return devs, nil
- }
- obj := self.GetMetadataJson(ctx, api.VM_METADATA_RELEASED_DEVICES, userCred)
- if obj == nil {
- return nil, errors.Error("get medata json")
- }
- if err := obj.Unmarshal(&devs); err != nil {
- return nil, errors.Wrapf(err, "unmarshal json string: %s", obj.String())
- }
- return devs, nil
- }
- func (self *SGuest) SetReleasedIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs []SIsolatedDevice) error {
- records := make([]api.ServerReleasedIsolatedDevice, 0)
- for _, dev := range devs {
- record := api.ServerReleasedIsolatedDevice{
- DevType: dev.DevType,
- Model: dev.Model,
- }
- records = append(records, record)
- }
- if err := self.SetMetadata(ctx, api.VM_METADATA_RELEASED_DEVICES, records, userCred); err != nil {
- return errors.Wrap(err, "set metadata")
- }
- return nil
- }
- func (self *SGuest) DetachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs []SIsolatedDevice) error {
- host, _ := self.GetHost()
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- for i := 0; i < len(devs); i++ {
- // check first
- dev := devs[i]
- if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
- if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
- msg := fmt.Sprintf("Can't separately detach dev type %s", dev.DevType)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return httperrors.NewBadRequestError("%s", msg)
- } else {
- if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
- msg := fmt.Sprintf("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return httperrors.NewBadRequestError("%s", msg)
- }
- }
- }
- }
- for i := 0; i < len(devs); i++ {
- err := self.detachIsolateDevice(ctx, userCred, &devs[i])
- if err != nil {
- return errors.Wrapf(err, "detach device %s", devs[i].GetId())
- }
- }
- return nil
- }
- // 卸载透传设备
- func (self *SGuest) PerformDetachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
- (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
- msg := fmt.Sprintf("Can't detach isolated device when guest is %s", self.GetStatus())
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return nil, httperrors.NewInvalidStatusError("%s", msg)
- }
- var detachAllDevice = jsonutils.QueryBoolean(data, "detach_all", false)
- devs := make([]SIsolatedDevice, 0)
- if !detachAllDevice {
- device, err := data.GetString("device")
- if err != nil {
- msg := "Missing isolated device"
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return nil, httperrors.NewBadRequestError("%s", msg)
- }
- iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
- if err != nil {
- msgFmt := "Isolated device %s not found"
- msg := fmt.Sprintf(msgFmt, device)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return nil, httperrors.NewBadRequestError(msgFmt, device)
- }
- devs = append(devs, *iDev.(*SIsolatedDevice))
- } else {
- devs, _ = self.GetIsolatedDevices()
- }
- if err := self.DetachIsolatedDevices(ctx, userCred, devs); err != nil {
- return nil, err
- }
- return nil, self.StartIsolatedDevicesSyncTask(ctx, userCred, jsonutils.QueryBoolean(data, "auto_start", false), "")
- }
- func (self *SGuest) startDetachIsolateDeviceWithoutNic(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
- iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
- if err != nil {
- msgFmt := "Isolated device %s not found"
- msg := fmt.Sprintf(msgFmt, device)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return httperrors.NewBadRequestError(msgFmt, device)
- }
- dev := iDev.(*SIsolatedDevice)
- return self.DetachIsolatedDevices(ctx, userCred, []SIsolatedDevice{*dev})
- }
- func (self *SGuest) detachIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice) error {
- if dev.GuestId != self.Id {
- msg := "Isolated device is not attached to this guest"
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
- return httperrors.NewBadRequestError("%s", msg)
- }
- drv, _ := self.GetDriver()
- if err := drv.BeforeDetachIsolatedDevice(ctx, userCred, self, dev); err != nil {
- return errors.Wrapf(err, "BeforeDetachIsolatedDevice %s of guest %s", jsonutils.Marshal(dev), self.GetId())
- }
- _, err := db.Update(dev, func() error {
- dev.GuestId = ""
- dev.NetworkIndex = -1
- dev.DiskIndex = -1
- return nil
- })
- if err != nil {
- return err
- }
- db.OpsLog.LogEvent(self, db.ACT_GUEST_DETACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
- return nil
- }
- // 挂载透传设备
- func (self *SGuest) PerformAttachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
- (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
- msg := fmt.Sprintf("Can't attach isolated device when guest is %v", self.GetStatus())
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
- return nil, httperrors.NewInvalidStatusError("%s", msg)
- }
- var err error
- autoStart := jsonutils.QueryBoolean(data, "auto_start", false)
- if data.Contains("device") {
- device, _ := data.GetString("device")
- err = self.StartAttachIsolatedDeviceGpuOrUsb(ctx, userCred, device, autoStart)
- } else if data.Contains("model") {
- vmodel, _ := data.GetString("model")
- var count int64 = 1
- if data.Contains("count") {
- count, _ = data.Int("count")
- }
- if count < 1 {
- return nil, httperrors.NewBadRequestError("guest attach gpu count must > 0")
- }
- err = self.StartAttachIsolatedDevices(ctx, userCred, vmodel, int(count), autoStart)
- } else {
- return nil, httperrors.NewMissingParameterError("device||model")
- }
- if err != nil {
- return nil, err
- }
- return nil, nil
- }
- func (self *SGuest) StartAttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModel string, count int, autoStart bool) error {
- if err := self.startAttachIsolatedDevices(ctx, userCred, devModel, count); err != nil {
- return err
- }
- // perform post attach task
- return self.StartIsolatedDevicesSyncTask(ctx, userCred, autoStart, "")
- }
- func (self *SGuest) AttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModelCount map[string]int) error {
- host, _ := self.GetHost()
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- unusedDevs := []SIsolatedDevice{}
- for devModel, count := range devModelCount {
- devs, err := IsolatedDeviceManager.GetUnusedDevsOnHost(host.Id, devModel, count)
- if err != nil {
- return httperrors.NewInternalServerError("fetch gpu failed %s", err)
- }
- if len(devs) == 0 || len(devs) != count {
- return httperrors.NewBadRequestError("require %d %s isolated device of host %s is not enough", count, devModel, host.GetName())
- }
- dev := devs[0]
- if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
- if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
- return httperrors.NewBadRequestError("Can't separately attach dev type %s", dev.DevType)
- } else {
- if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
- return httperrors.NewBadRequestError("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
- }
- }
- }
- if dev.DevType == api.LEGACY_VGPU_TYPE {
- attachedGpus, err := self.GetIsolatedDevices()
- if err != nil {
- return errors.Wrap(err, "get isolated devices")
- }
- for i := range attachedGpus {
- if attachedGpus[i].DevType == api.LEGACY_VGPU_TYPE {
- return httperrors.NewBadRequestError("Nvidia vgpu count exceed > 1")
- } else if utils.IsInStringArray(attachedGpus[i].DevType, api.VALID_GPU_TYPES) {
- return httperrors.NewBadRequestError("Nvidia vgpu can't passthrough with other gpus")
- }
- }
- } else if dev.DevType == api.CONTAINER_DEV_NVIDIA_MPS {
- allDevs, err := IsolatedDeviceManager.GetUnusedDevsOnHost(host.Id, devModel, -1)
- if err != nil {
- return httperrors.NewInternalServerError("fetch gpu failed %s", err)
- }
- attachedGpus, err := self.GetIsolatedDevices()
- if err != nil {
- return httperrors.NewInternalServerError("get attached isolated devices %s", err)
- }
- attachedAddrs := map[string]struct{}{}
- for i := range attachedGpus {
- addr := strings.Split(attachedGpus[i].Addr, "-")[0]
- attachedAddrs[addr] = struct{}{}
- }
- validDevs := []SIsolatedDevice{}
- for i := range allDevs {
- devAddr := strings.Split(allDevs[i].Addr, "-")[0]
- if _, ok := attachedAddrs[devAddr]; ok {
- continue
- }
- validDevs = append(validDevs, allDevs[i])
- }
- if len(validDevs) < count {
- return httperrors.NewInsufficientResourceError("require %d %s isolated device of host %s is not enough", count, devModel, host.GetName())
- }
- devs = validDevs[:count]
- }
- unusedDevs = append(unusedDevs, devs...)
- }
- defer func() { go host.ClearSchedDescCache() }()
- for i := 0; i < len(unusedDevs); i++ {
- if err := self.attachIsolatedDevice(ctx, userCred, &unusedDevs[i], nil, nil); err != nil {
- return errors.Wrapf(err, "attach device %s", unusedDevs[i].GetId())
- }
- }
- return nil
- }
- func (self *SGuest) startAttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModel string, count int) error {
- return self.AttachIsolatedDevices(ctx, userCred, map[string]int{devModel: count})
- }
- func (self *SGuest) StartAttachIsolatedDeviceGpuOrUsb(ctx context.Context, userCred mcclient.TokenCredential, device string, autoStart bool) error {
- if err := self.startAttachIsolatedDevGeneral(ctx, userCred, device); err != nil {
- return err
- }
- // perform post attach task
- return self.StartIsolatedDevicesSyncTask(ctx, userCred, autoStart, "")
- }
- func (self *SGuest) startAttachIsolatedDevGeneral(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
- iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
- if err != nil {
- msgFmt := "Isolated device %s not found"
- msg := fmt.Sprintf(msgFmt, device)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
- return httperrors.NewBadRequestError(msgFmt, device)
- }
- dev := iDev.(*SIsolatedDevice)
- if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
- if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
- return httperrors.NewBadRequestError("Can't separately attach dev type %s", dev.DevType)
- } else {
- if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
- return httperrors.NewBadRequestError("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
- }
- }
- }
- if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) {
- return httperrors.NewInvalidStatusError("Can't attach GPU when status is %q", self.GetStatus())
- }
- if dev.DevType == api.LEGACY_VGPU_TYPE {
- devs, err := self.GetIsolatedDevices()
- if err != nil {
- return errors.Wrap(err, "get isolated devices")
- }
- for i := range devs {
- if devs[i].DevType == api.LEGACY_VGPU_TYPE {
- return httperrors.NewBadRequestError("Nvidia vgpu count exceed > 1")
- } else if utils.IsInStringArray(devs[i].DevType, api.VALID_GPU_TYPES) {
- return httperrors.NewBadRequestError("Nvidia vgpu can't passthrough with other gpus")
- }
- }
- }
- host, _ := self.GetHost()
- lockman.LockObject(ctx, host)
- defer lockman.ReleaseObject(ctx, host)
- err = self.attachIsolatedDevice(ctx, userCred, dev, nil, nil)
- var msg string
- if err != nil {
- msg = err.Error()
- } else {
- go host.ClearSchedDescCache()
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, err == nil)
- return err
- }
- func (self *SGuest) attachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice, networkIndex *int, diskIndex *int8) error {
- if len(dev.GuestId) > 0 {
- return fmt.Errorf("Isolated device already attached to another guest: %s", dev.GuestId)
- }
- if dev.HostId != self.HostId {
- return fmt.Errorf("Isolated device and guest are not located in the same host")
- }
- drv, _ := self.GetDriver()
- if err := drv.BeforeAttachIsolatedDevice(ctx, userCred, self, dev); err != nil {
- return errors.Wrapf(err, "BeforeAttachIsolatedDevice %s of guest %s", jsonutils.Marshal(dev), self.GetId())
- }
- if _, err := db.Update(dev, func() error {
- dev.GuestId = self.Id
- if networkIndex != nil {
- dev.NetworkIndex = *networkIndex
- } else {
- dev.NetworkIndex = -1
- }
- if diskIndex != nil {
- dev.DiskIndex = *diskIndex
- } else {
- dev.DiskIndex = -1
- }
- return nil
- }); err != nil {
- return errors.Wrap(err, "db.Update")
- }
- db.OpsLog.LogEvent(self, db.ACT_GUEST_ATTACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
- return nil
- }
- // 设置透传设备
- func (self *SGuest) PerformSetIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
- (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
- return nil, httperrors.NewInvalidStatusError("Can't set isolated device when guest is %s", self.GetStatus())
- }
- var addDevs []string
- {
- addDevices, err := data.Get("add_devices")
- if err == nil {
- arrAddDev, ok := addDevices.(*jsonutils.JSONArray)
- if ok {
- addDevs = arrAddDev.GetStringArray()
- } else {
- return nil, httperrors.NewInputParameterError("attach devices is not string array")
- }
- }
- }
- var delDevs []string
- {
- delDevices, err := data.Get("del_devices")
- if err == nil {
- arrDelDev, ok := delDevices.(*jsonutils.JSONArray)
- if ok {
- delDevs = arrDelDev.GetStringArray()
- } else {
- return nil, httperrors.NewInputParameterError("detach devices is not string array")
- }
- }
- }
- // detach first
- for i := 0; i < len(delDevs); i++ {
- err := self.startDetachIsolateDeviceWithoutNic(ctx, userCred, delDevs[i])
- if err != nil {
- return nil, err
- }
- }
- for i := 0; i < len(addDevs); i++ {
- err := self.startAttachIsolatedDevGeneral(ctx, userCred, addDevs[i])
- if err != nil {
- return nil, err
- }
- }
- return nil, self.StartIsolatedDevicesSyncTask(ctx, userCred, jsonutils.QueryBoolean(data, "auto_start", false), "")
- }
- func (self *SGuest) StartIsolatedDevicesSyncTask(ctx context.Context, userCred mcclient.TokenCredential, autoStart bool, parentId string) error {
- if self.GetStatus() == api.VM_RUNNING {
- autoStart = false
- }
- data := jsonutils.Marshal(map[string]interface{}{
- "auto_start": autoStart,
- }).(*jsonutils.JSONDict)
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestIsolatedDeviceSyncTask", self, userCred, data, parentId, "", nil); err != nil {
- return err
- } else {
- return task.ScheduleRun(nil)
- }
- }
- func (self *SGuest) findGuestnetworkByInfo(info api.ServerNetworkInfo) (*SGuestnetwork, error) {
- if len(info.IpAddr) > 0 {
- gn, err := self.GetGuestnetworkByIp(info.IpAddr)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("ip %s not found", info.IpAddr)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- return gn, nil
- } else if len(info.Ip6Addr) > 0 {
- gn, err := self.GetGuestnetworkByIp6(info.Ip6Addr)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("ipv6 %s not found", info.Ip6Addr)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- return gn, nil
- } else if len(info.Mac) > 0 {
- gn, err := self.GetGuestnetworkByMac(info.Mac)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("mac %s not found", info.Mac)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- return gn, nil
- } else {
- gns, err := self.GetNetworks("")
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if info.Index >= 0 && info.Index < len(gns) {
- return &gns[info.Index], nil
- } else if info.Index >= 0 {
- return nil, httperrors.NewNotFoundError("nic at index %d not found", info.Index)
- }
- return nil, httperrors.NewInputParameterError("no either ip_addr, ip6_addr, mac or index specified")
- }
- }
- func (self *SGuest) getReuseAddr(gn *SGuestnetwork) string {
- if self.GetHypervisor() != api.HYPERVISOR_BAREMETAL {
- return ""
- }
- host, _ := self.GetHost()
- hostNics := host.GetNics()
- for _, hn := range hostNics {
- if hn.GetMac().String() == gn.MacAddr {
- return hn.IpAddr
- }
- }
- return ""
- }
- // Change IPaddress of a guestnetwork
- // first detach the network, then attach a network with identity mac address but different IP configurations
- // TODO change IP address of a teaming NIC may fail!!
- func (self *SGuest) PerformChangeIpaddr(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerChangeIpaddrInput,
- ) (jsonutils.JSONObject, error) {
- if self.Status != api.VM_READY && self.Status != api.VM_RUNNING && self.Status != api.VM_BLOCK_STREAM {
- return nil, httperrors.NewInvalidStatusError("Cannot change network ip_addr in status %s", self.Status)
- }
- reserve := (input.Reserve != nil && *input.Reserve)
- gn, err := self.findGuestnetworkByInfo(input.ServerNetworkInfo)
- if err != nil {
- return nil, errors.Wrap(err, "findGuestnetworkByInfo")
- }
- var conf *api.NetworkConfig
- if input.NetConf != nil {
- conf = input.NetConf
- } else if len(input.NetDesc) > 0 {
- netDescJson, err := jsonutils.ParseString(input.NetDesc)
- if err != nil {
- netDescJson = jsonutils.NewString(input.NetDesc)
- }
- netConf, err := cmdline.ParseNetworkConfigByJSON(netDescJson, -1)
- if err != nil {
- return nil, httperrors.NewInputParameterError("fail to parse net_desc %s: %s", input.NetDesc, err)
- }
- conf = netConf
- } else {
- return nil, httperrors.NewMissingParameterError("net_desc/net_conf")
- }
- if conf.BwLimit == 0 {
- conf.BwLimit = gn.BwLimit
- }
- if conf.Index == 0 {
- conf.Index = int(gn.Index)
- }
- conf, err = parseNetworkInfo(ctx, userCred, conf)
- if err != nil {
- return nil, httperrors.NewInputParameterError("parseNetworkInfo fail: %s", err)
- }
- if conf.StrictIPv6 && len(conf.Address) > 0 {
- // strict ipv6 network, can't add ipv4 address
- return nil, httperrors.NewBadRequestError("guest network has no ipv4 address")
- }
- reuseV4 := ""
- if conf.Address == gn.IpAddr {
- // 允许IPv4地址不变,只改IPv6地址
- reuseV4 = conf.Address
- }
- reuseV6 := ""
- if gn.Ip6Addr != "" && conf.Address6 != "" {
- inputIP := net.ParseIP(conf.Address6)
- gnIP := net.ParseIP(gn.Ip6Addr)
- if string(inputIP) == string(gnIP) {
- conf.Address6 = gn.Ip6Addr
- reuseV6 = gn.Ip6Addr
- }
- }
- err = isValidNetworkInfo(ctx, userCred, conf, reuseV4, reuseV6)
- if err != nil {
- return nil, httperrors.NewInputParameterError("isValidNetworkInfo fail: %s", err)
- }
- if len(conf.Network) == 0 {
- return nil, httperrors.NewInputParameterError("no specific network")
- }
- netObj, err := NetworkManager.FetchById(conf.Network)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2("network", conf.Network)
- } else {
- return nil, errors.Wrapf(err, "NetworkManager.FetchById %s", conf.Network)
- }
- }
- targetNetwork := netObj.(*SNetwork)
- // host, _ := self.GetHost()
- ngn, err := func() (*SGuestnetwork, error) {
- lockman.LockObject(ctx, targetNetwork)
- defer lockman.ReleaseObject(ctx, targetNetwork)
- if len(conf.Mac) > 0 {
- if conf.Mac != gn.MacAddr {
- if self.Status != api.VM_READY {
- // change mac
- return nil, httperrors.NewInvalidStatusError("cannot change mac when guest is running")
- }
- // check mac duplication
- cnt, err := GuestnetworkManager.Query().Equals("mac_addr", conf.Mac).CountWithError()
- if err != nil {
- return nil, httperrors.NewInternalServerError("check mac uniqueness fail %s", err)
- }
- if cnt > 0 {
- return nil, httperrors.NewConflictError("mac addr %s has been occupied", conf.Mac)
- }
- }
- } else {
- conf.Mac = gn.MacAddr
- }
- if conf.Mac == gn.MacAddr && conf.Address == gn.IpAddr {
- if len(gn.Ip6Addr) == 0 && len(conf.Address6) == 0 && !conf.RequireIPv6 {
- return nil, nil
- } else if len(gn.Ip6Addr) > 0 && conf.Address6 == gn.Ip6Addr {
- return nil, nil
- }
- // reserve = true
- }
- if conf.StrictIPv6 {
- conf.Address = ""
- } else if len(conf.Address) == 0 || conf.Address != gn.IpAddr {
- // need to allocate new address
- addr4, err := targetNetwork.GetFreeIP(ctx, userCred, nil, nil, conf.Address, api.IPAllocationDirection(targetNetwork.AllocPolicy), reserve, api.AddressTypeIPv4)
- if err != nil {
- return nil, errors.Wrap(err, "GetFreeIPv4")
- }
- if len(conf.Address) > 0 && conf.RequireDesignatedIP && conf.Address != addr4 {
- return nil, httperrors.NewConflictError("addr %s has been occupied", conf.Address)
- }
- conf.Address = addr4
- }
- if (conf.Address6 == "" && conf.RequireIPv6) || (len(conf.Address6) > 0 && conf.Address6 != gn.Ip6Addr) {
- // need to allocate new IPv6 address
- addr6, err := targetNetwork.GetFreeIP(ctx, userCred, nil, nil, conf.Address6, api.IPAllocationNone, reserve, api.AddressTypeIPv6)
- if err != nil {
- return nil, errors.Wrap(err, "GetFreeIPv6")
- }
- if len(conf.Address6) > 0 && conf.RequireDesignatedIP && conf.Address6 != addr6 {
- return nil, httperrors.NewConflictError("addr %s has been occupied", conf.Address6)
- }
- conf.Address6 = addr6
- }
- ngn := *gn
- _, err := db.Update(&ngn, func() error {
- ngn.NetworkId = conf.Network
- ngn.MacAddr = conf.Mac
- ngn.IpAddr = conf.Address
- ngn.Ip6Addr = conf.Address6
- return nil
- })
- if err != nil {
- return nil, errors.Wrap(err, "Update")
- }
- return &ngn, nil
- }()
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CHANGE_NIC, err, userCred, false)
- return nil, err
- }
- if ngn == nil {
- return nil, nil
- }
- //Get the detailed description of the NIC
- networkJsonDesc := ngn.getJsonDesc()
- newIpAddr := networkJsonDesc.Ip
- newMacAddr := networkJsonDesc.Mac
- newMaskLen := networkJsonDesc.Masklen
- newGateway := networkJsonDesc.Gateway
- ipMask := ""
- if networkJsonDesc.Ip != "" {
- ipMask = fmt.Sprintf("%s/%d", newIpAddr, newMaskLen)
- }
- ip6Mask := ""
- if networkJsonDesc.Ip6 != "" {
- ip6Mask = fmt.Sprintf("%s/%d", networkJsonDesc.Ip6, networkJsonDesc.Masklen6)
- }
- notes := gn.GetShortDesc(ctx)
- if gn != nil {
- if gn.IpAddr != ngn.IpAddr {
- notes.Add(jsonutils.NewString(gn.IpAddr), "prev_ip")
- }
- if gn.Ip6Addr != ngn.Ip6Addr {
- notes.Add(jsonutils.NewString(gn.Ip6Addr), "prev_ip6")
- }
- }
- db.OpsLog.LogEvent(self, db.ACT_CHANGE_IPADDR, notes, userCred)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CHANGE_NIC, notes, userCred, true)
- restartNetwork := (input.RestartNetwork != nil && *input.RestartNetwork)
- taskData := jsonutils.NewDict()
- if self.Hypervisor == api.HYPERVISOR_KVM && restartNetwork && (self.Status == api.VM_RUNNING || self.Status == api.VM_BLOCK_STREAM) {
- taskData.Set("restart_network", jsonutils.JSONTrue)
- taskData.Set("prev_ip", jsonutils.NewString(gn.IpAddr))
- taskData.Set("prev_mac", jsonutils.NewString(newMacAddr))
- net, err := ngn.GetNetwork()
- if err != nil {
- return nil, errors.Wrapf(err, "GetNetwork")
- }
- taskData.Set("is_vpc_network", jsonutils.NewBool(net.isOneCloudVpcNetwork()))
- if len(ipMask) > 0 {
- taskData.Set("ip_mask", jsonutils.NewString(ipMask))
- taskData.Set("gateway", jsonutils.NewString(newGateway))
- }
- if len(ip6Mask) > 0 {
- taskData.Set("ip6_mask", jsonutils.NewString(ip6Mask))
- taskData.Set("gateway6", jsonutils.NewString(networkJsonDesc.Gateway6))
- }
- if self.Status == api.VM_BLOCK_STREAM {
- taskData.Set("in_block_stream", jsonutils.JSONTrue)
- }
- self.SetStatus(ctx, userCred, api.VM_RESTART_NETWORK, "restart network")
- }
- return nil, self.startSyncTask(ctx, userCred, false, "", taskData)
- }
- func (self *SGuest) GetIfNameByMac(ctx context.Context, userCred mcclient.TokenCredential, mac string) (string, error) {
- //Find the network card according to the mac address, if it is empty, it means no network card is found
- ifnameData, err := self.PerformQgaGetNetwork(ctx, userCred, nil, nil)
- if err != nil {
- return "", err
- }
- //Get the name of the network card
- var parsedData []api.IfnameDetail
- if err := ifnameData.Unmarshal(&parsedData); err != nil {
- return "", err
- }
- var ifnameDevice string
- //Finding a network card by its mac address
- for _, detail := range parsedData {
- if detail.HardwareAddress == mac {
- ifnameDevice = detail.Name
- }
- }
- return ifnameDevice, nil
- }
- // 卸载网卡
- func (self *SGuest) PerformDetachnetwork(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerDetachnetworkInput,
- ) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) && !input.IsForce() {
- return nil, httperrors.NewInvalidStatusError("Cannot detach network in status %s", self.Status)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- err = driver.ValidateDetachNetwork(ctx, userCred, self)
- if err != nil {
- if !input.IsForce() {
- return nil, errors.Wrap(err, "ValidateDetachNetwork")
- } else {
- log.Errorf("ValidateDetachNetwork fail %s, ignore by force", err)
- }
- }
- var gns []SGuestnetwork
- if len(input.NetId) > 0 {
- netObj, err := validators.ValidateModel(ctx, userCred, NetworkManager, &input.NetId)
- if err != nil {
- return nil, err
- }
- gns, err = self.GetNetworks(netObj.GetId())
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- } else if len(input.IpAddr) > 0 || len(input.Ip6Addr) > 0 {
- var gn *SGuestnetwork
- var err error
- if len(input.IpAddr) > 0 {
- gn, err = self.GetGuestnetworkByIp(input.IpAddr)
- } else if len(input.Ip6Addr) > 0 {
- gn, err = self.GetGuestnetworkByIp6(input.Ip6Addr)
- }
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("ip %s not found", input.IpAddr)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- gns = []SGuestnetwork{*gn}
- } else if len(input.Mac) > 0 {
- gn, err := self.GetGuestnetworkByMac(input.Mac)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("mac %s not found", input.Mac)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- gns = []SGuestnetwork{*gn}
- } else {
- return nil, httperrors.NewMissingParameterError("net_id")
- }
- removeNics := make(map[string]*SGuestnetwork)
- for i := range gns {
- removeNics[gns[i].MacAddr] = &gns[i]
- }
- slaveNics, err := self.GetSlaveNetworks()
- if err != nil {
- return nil, errors.Wrap(err, "GetSlaveNetworks")
- }
- for i := range slaveNics {
- teamMac := slaveNics[i].TeamWith
- if len(teamMac) > 0 {
- if _, ok := removeNics[teamMac]; ok {
- // to remove slave's master NIC, should also remove slave itself
- if _, ok2 := removeNics[slaveNics[i].MacAddr]; !ok2 {
- // otherwise, report the error
- return nil, errors.Wrap(errors.ErrInvalidStatus, "")
- }
- }
- }
- }
- err = self.detachNetworks(ctx, userCred, gns, input.Reserve)
- if err != nil {
- return nil, errors.Wrap(err, "detachNetworks")
- }
- {
- // 修正缺省网关
- err := self.fixDefaultGateway(ctx, userCred)
- if err != nil {
- log.Errorf("fixDefaultGateway fail %s", err)
- }
- }
- if !input.IsForce() && input.DisableSyncConfig != nil && !*input.DisableSyncConfig {
- if self.Status == api.VM_READY {
- err := self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
- if err != nil {
- return nil, errors.Wrap(err, "StartGuestDeployTask")
- }
- } else {
- err := self.StartSyncTask(ctx, userCred, false, "")
- if err != nil {
- return nil, errors.Wrap(err, "StartSyncTask")
- }
- }
- }
- return nil, nil
- }
- func (guest *SGuest) fixDefaultGatewayByNics(ctx context.Context, userCred mcclient.TokenCredential, nics []SGuestnetwork) (bool, error) {
- defaultGwCnt := 0
- for i := range nics {
- if nics[i].Virtual || len(nics[i].TeamWith) > 0 {
- continue
- }
- if nics[i].IsDefault {
- defaultGwCnt++
- }
- }
- if defaultGwCnt == 1 {
- return false, nil
- }
- nicList := netutils2.SNicInfoList{}
- for i := range nics {
- if nics[i].Virtual || len(nics[i].TeamWith) > 0 {
- continue
- }
- net, _ := nics[i].GetNetwork()
- if net != nil {
- nicList = nicList.Add(nics[i].MacAddr, nics[i].IpAddr, net.GuestGateway, nics[i].Ip6Addr, net.GuestGateway6, nics[i].IsDefault)
- }
- }
- gwMac, _ := nicList.FindDefaultNicMac()
- if gwMac != "" {
- err := guest.setDefaultGateway(ctx, userCred, gwMac)
- if err != nil {
- log.Errorf("setDefaultGateway fail %s", err)
- return true, errors.Wrap(err, "setDefaultGateway")
- }
- }
- return true, nil
- }
- func (guest *SGuest) fixDefaultGateway(ctx context.Context, userCred mcclient.TokenCredential) error {
- nics, _ := guest.GetNetworks("")
- _, err := guest.fixDefaultGatewayByNics(ctx, userCred, nics)
- return err
- }
- // 挂载网卡
- func (self *SGuest) PerformAttachnetwork(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input *api.AttachNetworkInput,
- ) (*api.SGuest, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewBadRequestError("Cannot attach network in status %s", self.Status)
- }
- // count := len(input.Nets)
- if len(input.Nets) == 0 {
- if len(input.NetDesc) > 0 {
- for _, netDesc := range input.NetDesc {
- netConf, err := cmdline.ParseNetworkConfigByJSON(jsonutils.NewString(netDesc), -1)
- if err != nil {
- return nil, httperrors.NewInputParameterError("fail to parse net_desc %s: %s", netDesc, err)
- }
- input.Nets = append(input.Nets, netConf)
- }
- }
- if len(input.Nets) == 0 {
- return nil, httperrors.NewMissingParameterError("nets/net_desc")
- }
- }
- var inicCnt, enicCnt, isolatedDevCount, defaultGwCnt int
- for i := range input.Nets {
- err := isValidNetworkInfo(ctx, userCred, input.Nets[i], "", "")
- if err != nil {
- return nil, err
- }
- if IsExitNetworkInfo(ctx, userCred, input.Nets[i]) {
- enicCnt += 1
- // ebw = input.BwLimit
- } else {
- inicCnt += 1
- // ibw = input.BwLimit
- }
- if input.Nets[i].BwLimit == 0 {
- input.Nets[i].BwLimit = options.Options.DefaultBandwidth
- }
- if input.Nets[i].SriovDevice != nil {
- if self.BackupHostId != "" {
- return nil, httperrors.NewBadRequestError("Cannot create backup with isolated device")
- }
- devConfig, err := IsolatedDeviceManager.parseDeviceInfo(userCred, input.Nets[i].SriovDevice)
- if err != nil {
- return nil, httperrors.NewInputParameterError("parse isolated device description error %s", err)
- }
- err = IsolatedDeviceManager.isValidNicDeviceInfo(devConfig)
- if err != nil {
- return nil, err
- }
- input.Nets[i].SriovDevice = devConfig
- input.Nets[i].Driver = api.NETWORK_DRIVER_VFIO
- isolatedDevCount += 1
- }
- if len(input.Nets[i].Secgroups) > 0 {
- secgroupIds, err := isValidSecgroups(ctx, userCred, input.Nets[i].Secgroups)
- if err != nil {
- return nil, err
- }
- input.Nets[i].Secgroups = secgroupIds
- }
- if input.Nets[i].IsDefault {
- defaultGwCnt++
- }
- }
- if defaultGwCnt > 1 {
- return nil, errors.Wrapf(httperrors.ErrInputParameter, "more than 1 nic(%d) assigned as default gateway", defaultGwCnt)
- }
- pendingUsage := &SRegionQuota{
- Port: inicCnt,
- Eport: enicCnt,
- //Bw: ibw,
- //Ebw: ebw,
- }
- pendingUsageHost := &SQuota{IsolatedDevice: isolatedDevCount}
- keys, err := self.GetRegionalQuotaKeys()
- if err != nil {
- return nil, err
- }
- quotakeys, err := self.GetQuotaKeys()
- if err != nil {
- return nil, err
- }
- pendingUsageHost.SetKeys(quotakeys)
- pendingUsage.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
- if err != nil {
- return nil, httperrors.NewOutOfQuotaError("%v", err)
- }
- defer quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsageHost)
- if err != nil {
- return nil, httperrors.NewOutOfQuotaError("%v", err)
- }
- defer quotas.CancelPendingUsage(ctx, userCred, pendingUsageHost, pendingUsageHost, false)
- host, _ := self.GetHost()
- defer host.ClearSchedDescCache()
- var defaultGwGn *SGuestnetwork
- for i := range input.Nets {
- gns, err := self.attach2NetworkDesc(ctx, userCred, host, input.Nets[i], pendingUsage, nil)
- logclient.AddSimpleActionLog(self, logclient.ACT_ATTACH_NETWORK, input.Nets[i], userCred, err == nil)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
- return nil, httperrors.NewBadRequestError("%v", err)
- }
- for gnIdx := range gns {
- if gns[gnIdx].IsDefault {
- defaultGwGn = &gns[gnIdx]
- }
- }
- if input.Nets[i].SriovDevice != nil {
- err = self.allocSriovNicDevice(ctx, userCred, host, &gns[0], input.Nets[i], pendingUsageHost)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, pendingUsageHost, pendingUsageHost, false)
- return nil, errors.Wrap(err, "self.allocSriovNicDevice")
- }
- }
- if len(input.Nets[i].Secgroups) > 0 {
- err = self.SaveNetworkSecgroups(ctx, userCred, input.Nets[i].Secgroups, gns[0].Index)
- if err != nil {
- return nil, errors.Wrap(err, "SaveNetworkSecgroups")
- }
- }
- }
- // adjust default gateway
- if defaultGwGn != nil {
- err := self.setDefaultGateway(ctx, userCred, defaultGwGn.MacAddr)
- if err != nil {
- log.Errorf("setDefaultGateway fail %s", err)
- }
- } else {
- err := self.fixDefaultGateway(ctx, userCred)
- if err != nil {
- log.Errorf("fixDefaultGateway fail %s", err)
- }
- }
- if input.DisableSyncConfig != nil && !*input.DisableSyncConfig {
- if self.Status == api.VM_READY {
- err := self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
- if err != nil {
- return nil, errors.Wrap(err, "StartGuestDeployTask")
- }
- } else {
- err := self.StartSyncTask(ctx, userCred, false, "")
- if err != nil {
- return nil, errors.Wrap(err, "StartSyncTask")
- }
- }
- }
- return nil, nil
- }
- // 调整带宽
- func (guest *SGuest) PerformChangeBandwidth(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerChangeBandwidthInput,
- ) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewBadRequestError("Cannot change bandwidth in status %s", guest.Status)
- }
- bandwidth := input.Bandwidth
- if bandwidth < 0 {
- return nil, httperrors.NewBadRequestError("Bandwidth must be non-negative")
- }
- guestnic, err := guest.findGuestnetworkByInfo(input.ServerNetworkInfo)
- if err != nil {
- return nil, errors.Wrap(err, "findGuestnetworkByInfo")
- }
- if guestnic.BwLimit != int(bandwidth) {
- oldBw := guestnic.BwLimit
- _, err := db.Update(guestnic, func() error {
- guestnic.BwLimit = int(bandwidth)
- return nil
- })
- if err != nil {
- return nil, err
- }
- eventDesc := guestnic.GetShortDesc(ctx)
- eventDesc.Add(jsonutils.NewInt(int64(oldBw)), "old_bw_limit_mbps")
- db.OpsLog.LogEvent(guest, db.ACT_CHANGE_BANDWIDTH, eventDesc, userCred)
- logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_CHANGE_BANDWIDTH, eventDesc, userCred, true)
- if guest.Status == api.VM_READY || (input.NoSync != nil && *input.NoSync) {
- // if no sync, just update db
- return nil, nil
- }
- // otherwise, sync configure to host
- return nil, guest.StartSyncTask(ctx, userCred, false, "")
- }
- return nil, nil
- }
- // 修改源地址检查
- func (self *SGuest) PerformModifySrcCheck(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewBadRequestError("Cannot change setting in status %s", self.Status)
- }
- argValue := func(name string, val *bool) error {
- if !data.Contains(name) {
- return nil
- }
- b, err := data.Bool(name)
- if err != nil {
- return errors.Wrapf(err, "fetch arg %s", name)
- }
- *val = b
- return nil
- }
- var (
- srcIpCheck = self.SrcIpCheck.Bool()
- srcMacCheck = self.SrcMacCheck.Bool()
- )
- if err := argValue("src_ip_check", &srcIpCheck); err != nil {
- return nil, err
- }
- if err := argValue("src_mac_check", &srcMacCheck); err != nil {
- return nil, err
- }
- // default: both check on
- // switch: mac check off, also implies ip check off
- // router: mac check on, ip check off
- if !srcMacCheck && srcIpCheck {
- srcIpCheck = false
- }
- if srcIpCheck != self.SrcIpCheck.Bool() || srcMacCheck != self.SrcMacCheck.Bool() {
- diff, err := db.Update(self, func() error {
- self.SrcIpCheck = tristate.NewFromBool(srcIpCheck)
- self.SrcMacCheck = tristate.NewFromBool(srcMacCheck)
- return nil
- })
- if err != nil {
- return nil, err
- }
- db.OpsLog.LogEvent(self, db.ACT_GUEST_SRC_CHECK, diff, userCred)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_SRC_CHECK, diff, userCred, true)
- return nil, self.StartSyncTask(ctx, userCred, false, "")
- }
- return nil, nil
- }
- // 调整配置
- func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerChangeConfigInput) (jsonutils.JSONObject, error) {
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.AllowReconfigGuest() {
- return nil, httperrors.NewInvalidStatusError("Not allow to change config")
- }
- if len(self.BackupHostId) > 0 {
- return nil, httperrors.NewBadRequestError("Guest have backup not allow to change config")
- }
- _, err = self.GetHost()
- if err != nil {
- return nil, httperrors.NewInvalidStatusError("no valid host")
- }
- confs, err := driver.ValidateGuestChangeConfigInput(ctx, self, input)
- if err != nil {
- return nil, errors.Wrap(err, "ValidateGuestChangeConfigInput")
- }
- if confs.CpuChanged() || confs.MemChanged() || confs.InstanceTypeChanged() {
- changeStatus, err := driver.GetChangeInstanceTypeStatus()
- if err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- if !utils.IsInStringArray(self.Status, changeStatus) {
- return nil, httperrors.NewInvalidStatusError("Cannot change config in %s for %s, requires %s", self.Status, self.GetHypervisor(), changeStatus)
- }
- }
- if self.PowerStates == api.VM_POWER_STATES_ON && (confs.CpuChanged() || confs.MemChanged()) {
- confs, err = driver.ValidateGuestHotChangeConfigInput(ctx, self, confs)
- if err != nil {
- return nil, httperrors.NewInvalidStatusError("cannot change CPU/Memory spec in power status %s: %s", self.PowerStates, err)
- }
- }
- if len(confs.Create) > 0 {
- attachStatus, err := driver.GetAttachDiskStatus()
- if err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- if !utils.IsInStringArray(self.Status, attachStatus) {
- return nil, httperrors.NewInvalidStatusError("Cannot attach disk in %s for %s, requires %s", self.Status, self.GetHypervisor(), attachStatus)
- }
- }
- log.Debugf("PerformChangeConfig %s", jsonutils.Marshal(confs).String())
- pendingUsage := &SQuota{}
- if added := confs.AddedCpu(); added > 0 {
- pendingUsage.Cpu = added
- }
- if added := confs.AddedExtraCpu(); added > 0 {
- pendingUsage.Cpu += added
- }
- if added := confs.AddedMem(); added > 0 {
- pendingUsage.Memory = added
- }
- if added := confs.AddedDisk(); added > 0 {
- pendingUsage.Storage = added
- }
- keys, err := self.GetQuotaKeys()
- if err != nil {
- return nil, err
- }
- pendingUsage.SetKeys(keys)
- log.Debugf("ChangeConfig pendingUsage %s", jsonutils.Marshal(pendingUsage))
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
- if err != nil {
- return nil, errors.Wrap(err, "CheckSetPendingQuota")
- }
- // logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CHANGE_CONFIG, confs, userCred, true)
- self.StartChangeConfigTask(ctx, userCred, confs, "", pendingUsage)
- return nil, nil
- }
- func (self *SGuest) ChangeConfToSchedDesc(addCpu, addExtraCpu, addMem int, schedInputDisks []*api.DiskConfig) *schedapi.ScheduleInput {
- region, _ := self.GetRegion()
- devs, _ := self.GetIsolatedDevices()
- desc := &schedapi.ScheduleInput{
- ServerConfig: schedapi.ServerConfig{
- ServerConfigs: &api.ServerConfigs{
- Hypervisor: self.Hypervisor,
- PreferRegion: region.Id,
- PreferHost: self.HostId,
- Disks: schedInputDisks,
- },
- Memory: addMem,
- Ncpu: addCpu,
- Project: self.ProjectId,
- Domain: self.DomainId,
- },
- OsArch: self.OsArch,
- ChangeConfig: true,
- HasIsolatedDevice: len(devs) > 0,
- ExtraCpuCount: addExtraCpu,
- }
- return desc
- }
- func (self *SGuest) StartChangeConfigTask(ctx context.Context, userCred mcclient.TokenCredential,
- confs *api.ServerChangeConfigSettings, parentTaskId string, pendingUsage quotas.IQuota) error {
- self.SetStatus(ctx, userCred, api.VM_CHANGE_FLAVOR, "")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeConfigTask", self, userCred, jsonutils.Marshal(confs).(*jsonutils.JSONDict), parentTaskId, "", pendingUsage)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- eip, _ := self.GetEipOrPublicIp()
- if eip != nil {
- eip.DoPendingDelete(ctx, userCred)
- }
- disks, _ := self.GetDisks()
- for i := range disks {
- if !disks[i].IsDetachable() {
- disks[i].DoPendingDelete(ctx, userCred)
- } else {
- log.Warningf("detachable disk on pending delete guests!!! should be removed earlier")
- self.DetachDisk(ctx, &disks[i], userCred)
- }
- }
- SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
- return self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
- }
- // 从回收站恢复虚拟机
- func (self *SGuest) PerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.PendingDeleted && !self.Deleted {
- err := self.DoCancelPendingDelete(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "DoCancelPendingDelete")
- }
- self.RecoverUsages(ctx, userCred)
- }
- return nil, nil
- }
- func (self *SGuest) DoCancelPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- eip, _ := self.GetEipOrPublicIp()
- if eip != nil {
- eip.DoCancelPendingDelete(ctx, userCred)
- }
- disks, err := self.GetDisks()
- if err != nil {
- return err
- }
- for _, disk := range disks {
- disk.DoCancelPendingDelete(ctx, userCred)
- }
- if self.BillingType == billing_api.BILLING_TYPE_POSTPAID && !self.ExpiredAt.IsZero() {
- err := SaveReleaseAt(ctx, self, userCred, time.Time{})
- if err != nil {
- return err
- }
- }
- err = self.SVirtualResourceBase.DoCancelPendingDelete(ctx, userCred)
- if err != nil {
- return err
- }
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: self,
- Action: notifyclient.ActionCreate,
- })
- return nil
- }
- func (self *SGuest) StartUndeployGuestTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, targetHostId string) error {
- data := jsonutils.NewDict()
- if len(targetHostId) > 0 {
- data.Add(jsonutils.NewString(targetHostId), "target_host_id")
- }
- task, err := taskman.TaskManager.NewTask(ctx, "GuestUndeployTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- // 重置虚拟机状态
- func (self *SGuest) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- isHard := jsonutils.QueryBoolean(data, "is_hard", false)
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED, api.VM_KICKSTART_INSTALLING, api.VM_KICKSTART_FAILED, api.VM_KICKSTART_COMPLETED}) {
- drv.StartGuestResetTask(self, ctx, userCred, isHard, "")
- return nil, nil
- }
- return nil, httperrors.NewInvalidStatusError("Cannot reset VM in status %s", self.Status)
- }
- // 同步虚拟机状态
- func (self *SGuest) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- var openTask = true
- count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
- if err != nil {
- return nil, err
- }
- if count > 0 {
- return nil, httperrors.NewBadRequestError("Guest has %d task active, can't sync status", count)
- }
- return nil, self.StartSyncstatus(ctx, userCred, "")
- }
- func (self *SGuest) isNotRunningStatus(status string) bool {
- if status == api.VM_READY || status == api.VM_SUSPEND {
- return true
- }
- return false
- }
- func (self *SGuest) SetStatus(ctx context.Context, userCred mcclient.TokenCredential, status, reason string) error {
- if status == api.VM_RUNNING {
- if err := self.SetPowerStates(api.VM_POWER_STATES_ON); err != nil {
- return errors.Wrap(err, "input status is running")
- }
- } else if status == api.VM_READY {
- if err := self.SetPowerStates(api.VM_POWER_STATES_OFF); err != nil {
- return errors.Wrap(err, "input status is ready")
- }
- }
- err := self.SVirtualResourceBase.SetStatus(ctx, userCred, status, reason)
- if err != nil {
- return errors.Wrap(err, "setStatus")
- }
- return nil
- }
- func (self *SGuest) SetPowerStates(powerStates string) error {
- if self.PowerStates == powerStates {
- return nil
- }
- _, err := db.Update(self, func() error {
- self.PowerStates = powerStates
- return nil
- })
- return errors.Wrapf(err, "Update power states to %s", powerStates)
- }
- func (self *SGuest) SetBackupGuestStatus(userCred mcclient.TokenCredential, status string, reason string) error {
- if self.BackupGuestStatus == status {
- return nil
- }
- oldStatus := self.BackupGuestStatus
- _, err := db.Update(self, func() error {
- self.BackupGuestStatus = status
- return nil
- })
- if err != nil {
- return errors.Wrap(err, "Update backup guest status")
- }
- if userCred != nil {
- notes := fmt.Sprintf("%s=>%s", oldStatus, status)
- if len(reason) > 0 {
- notes = fmt.Sprintf("%s: %s", notes, reason)
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE_BACKUP_GUEST_STATUS, notes, userCred)
- logclient.AddSimpleActionLog(self, logclient.ACT_UPDATE_BACKUP_GUEST_STATUS, notes, userCred, true)
- }
- return nil
- }
- func (g *SGuest) SetStatusFromHost(ctx context.Context, userCred mcclient.TokenCredential, resp api.HostUploadGuestStatusInput, hasParentTask bool, originStatus string) error {
- statusStr := resp.Status
- switch statusStr {
- case cloudprovider.CloudVMStatusRunning:
- statusStr = api.VM_RUNNING
- case cloudprovider.CloudVMStatusSuspend:
- statusStr = api.VM_SUSPEND
- case cloudprovider.CloudVMStatusStopped, api.VM_READY:
- statusStr = api.VM_READY
- case api.VM_BLOCK_STREAM, api.VM_BLOCK_STREAM_FAIL:
- break
- default:
- if g.GetHypervisor() != api.HYPERVISOR_POD {
- statusStr = api.VM_UNKNOWN
- }
- }
- // Do not override kickstart_installing with running
- if statusStr == api.VM_RUNNING && g.Status == api.VM_KICKSTART_INSTALLING {
- statusStr = g.Status
- log.Infof("guest %s is installing, skip set running status", g.Name)
- }
- if !hasParentTask {
- // migrating status hack
- // not change migrating when:
- // guest.Status is migrating and task not has parent task
- if originStatus == api.VM_MIGRATING && statusStr == api.VM_RUNNING && len(g.ExternalId) == 0 {
- statusStr = originStatus
- }
- }
- input := api.ServerPerformStatusInput{
- PerformStatusInput: resp.PerformStatusInput,
- }
- input.Status = statusStr
- if _, err := g.PerformStatus(ctx, userCred, nil, input); err != nil {
- return errors.Wrapf(err, "perform status of %s", jsonutils.Marshal(resp))
- }
- return nil
- }
- func (m *SGuestManager) PerformUploadStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.HostUploadGuestsStatusInput) (*api.GuestUploadStatusesResponse, error) {
- out := &api.GuestUploadStatusesResponse{
- Guests: make(map[string]*api.GuestUploadStatusResponse),
- }
- for id, status := range input.Guests {
- gst := m.FetchGuestById(id)
- if gst == nil {
- out.Guests[id] = &api.GuestUploadStatusResponse{
- Error: "not found guest",
- }
- continue
- }
- resp, err := gst.PerformUploadStatus(ctx, userCred, query, *status)
- out.Guests[id] = resp
- if err != nil {
- resp.Error = err.Error()
- continue
- }
- }
- return out, nil
- }
- func (self *SGuest) PerformUploadStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUploadGuestStatusInput) (*api.GuestUploadStatusResponse, error) {
- err := self.SetStatusFromHost(ctx, userCred, input, false, "")
- if err != nil {
- return nil, errors.Wrap(err, "set status from host")
- }
- resp := &api.GuestUploadStatusResponse{
- OK: true,
- Containers: make(map[string]*api.GuestUploadContainerStatusResponse),
- }
- for cId, cStatus := range input.Containers {
- ctr, err := GetContainerManager().FetchById(cId)
- if err != nil {
- resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
- Error: err.Error(),
- }
- continue
- }
- if _, err := ctr.(*SContainer).PerformStatus(ctx, userCred, query, *cStatus); err != nil {
- resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
- Error: err.Error(),
- }
- } else {
- resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
- OK: true,
- }
- }
- }
- return resp, nil
- }
- // 同步状态
- func (self *SGuest) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerPerformStatusInput) (jsonutils.JSONObject, error) {
- if input.HostId != "" && self.BackupHostId != "" && input.HostId == self.BackupHostId {
- // perform status called from slave guest
- return nil, self.SetBackupGuestStatus(userCred, input.Status, input.Reason)
- }
- if input.HostId != "" && input.HostId != self.HostId {
- // perform status called from volatile host, eg: migrate dest host
- return nil, nil
- }
- if input.PowerStates != "" {
- if err := self.SetPowerStates(input.PowerStates); err != nil {
- return nil, errors.Wrap(err, "set power states")
- }
- }
- // Do not override kickstart_installing with running
- if input.Status == api.VM_RUNNING && self.Status == api.VM_KICKSTART_INSTALLING {
- log.Debugf("guest %s is in kickstart_installing state, skip set running status", self.Name)
- return nil, nil
- }
- preStatus := self.Status
- _, err := self.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input.PerformStatusInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBase.PerformStatus")
- }
- if self.HasBackupGuest() {
- if input.Status == api.VM_READY {
- if err := self.ResetGuestQuorumChildIndex(ctx, userCred); err != nil {
- return nil, errors.Wrap(err, "reset guest quorum child index")
- }
- }
- }
- if input.Status == api.VM_RUNNING && input.BlockJobsCount == 0 {
- if ispId := self.GetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, userCred); len(ispId) > 0 {
- var disksMerged = true
- disks, _ := self.GetDisks()
- for _, disk := range disks {
- if disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
- disksMerged = false
- }
- }
- if disksMerged {
- ispM, err := InstanceSnapshotManager.FetchById(ispId)
- if err == nil {
- isp := ispM.(*SInstanceSnapshot)
- isp.DecRefCount(ctx, userCred)
- }
- self.SetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, "", userCred)
- }
- }
- }
- if preStatus != self.Status && !self.isNotRunningStatus(preStatus) && self.isNotRunningStatus(self.Status) {
- db.OpsLog.LogEvent(self, db.ACT_STOP, "", userCred)
- if self.Status == api.VM_READY && !self.DisableDelete.Bool() && self.ShutdownBehavior == api.SHUTDOWN_TERMINATE {
- err = self.StartAutoDeleteGuestTask(ctx, userCred, "")
- return nil, err
- }
- }
- for cId, cStatus := range input.Containers {
- ctr, err := GetContainerManager().FetchById(cId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetContainerManager(%s)", cId)
- }
- if _, err := ctr.(*SContainer).PerformStatus(ctx, userCred, query, *cStatus); err != nil {
- return nil, errors.Wrapf(err, "PerformStatus(%s) of container", cId)
- }
- }
- return nil, nil
- }
- // 关机
- func (self *SGuest) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- input api.ServerStopInput) (jsonutils.JSONObject, error) {
- drv, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrap(err, "GetDriver")
- }
- if !input.IsForce {
- err := drv.CanStop(self)
- if err != nil {
- return nil, errors.Wrap(err, "Cannot stop server")
- }
- }
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- return nil, self.StartGuestStopTask(ctx, userCred, input.TimeoutSecs, input.IsForce, input.StopCharging, "")
- }
- // 冻结虚拟机
- func (self *SGuest) PerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) (jsonutils.JSONObject, error) {
- if self.Freezed {
- return nil, httperrors.NewBadRequestError("virtual resource already freezed")
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED}) {
- return nil, self.StartGuestStopAndFreezeTask(ctx, userCred)
- } else {
- return self.SVirtualResourceBase.PerformFreeze(ctx, userCred, query, input)
- }
- }
- func (self *SGuest) StartGuestStopAndFreezeTask(ctx context.Context, userCred mcclient.TokenCredential) error {
- self.SetStatus(ctx, userCred, api.VM_START_STOP, "")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestStopAndFreezeTask", self, userCred, nil, "", "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- // 重启
- func (self *SGuest) PerformRestart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- isForce := jsonutils.QueryBoolean(data, "is_force", false)
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED, api.VM_KICKSTART_INSTALLING, api.VM_KICKSTART_FAILED, api.VM_KICKSTART_COMPLETED}) || (isForce && self.Status == api.VM_STOPPING) {
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- return nil, driver.StartGuestRestartTask(self, ctx, userCred, isForce, "")
- }
- return nil, httperrors.NewInvalidStatusError("Cannot do restart server in status %s", self.Status)
- }
- // 发送远程命令
- func (self *SGuest) PerformSendkeys(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM {
- return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
- }
- if self.Status != api.VM_RUNNING {
- return nil, httperrors.NewInvalidStatusError("Cannot send keys in status %s", self.Status)
- }
- keys, err := data.GetString("keys")
- if err != nil {
- log.Errorln(err)
- return nil, httperrors.NewMissingParameterError("keys")
- }
- err = self.VerifySendKeys(keys)
- if err != nil {
- return nil, httperrors.NewBadRequestError("%v", err)
- }
- cmd := fmt.Sprintf("sendkey %s", keys)
- duration, err := data.Int("duration")
- if err == nil {
- cmd = fmt.Sprintf("%s %d", cmd, duration)
- }
- _, err = self.SendMonitorCommand(ctx, userCred, &api.ServerMonitorInput{COMMAND: cmd})
- return nil, err
- }
- func (self *SGuest) VerifySendKeys(keyStr string) error {
- keys := strings.Split(keyStr, "-")
- for _, key := range keys {
- if !self.IsLegalKey(key) {
- return fmt.Errorf("Unknown key '%s'", key)
- }
- }
- return nil
- }
- func (self *SGuest) IsLegalKey(key string) bool {
- singleKeys := "1234567890abcdefghijklmnopqrstuvwxyz"
- legalKeys := []string{"ctrl", "ctrl_r", "alt", "alt_r", "shift", "shift_r",
- "delete", "esc", "insert", "print", "spc",
- "f1", "f2", "f3", "f4", "f5", "f6",
- "f7", "f8", "f9", "f10", "f11", "f12",
- "home", "pgup", "pgdn", "end",
- "up", "down", "left", "right",
- "tab", "minus", "equal", "backspace", "backslash",
- "bracket_left", "bracket_right", "backslash",
- "semicolon", "apostrophe", "grave_accent", "ret",
- "comma", "dot", "slash",
- "caps_lock", "num_lock", "scroll_lock"}
- if len(key) > 1 && !utils.IsInStringArray(key, legalKeys) {
- return false
- } else if len(key) == 1 && !strings.Contains(singleKeys, key) {
- return false
- }
- return true
- }
- func (self *SGuest) SendMonitorCommand(ctx context.Context, userCred mcclient.TokenCredential, cmd *api.ServerMonitorInput) (jsonutils.JSONObject, error) {
- host, _ := self.GetHost()
- url := fmt.Sprintf("%s/servers/%s/monitor", host.ManagerUri, self.Id)
- header := http.Header{}
- header.Add("X-Auth-Token", userCred.GetTokenString())
- body := jsonutils.NewDict()
- body.Add(jsonutils.NewString(cmd.COMMAND), "cmd")
- body.Add(jsonutils.NewBool(cmd.QMP), "qmp")
- _, res, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
- if err != nil {
- return nil, err
- }
- ret := res.(*jsonutils.JSONDict)
- return ret, nil
- }
- // 绑定EIP
- func (self *SGuest) PerformAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerAssociateEipInput) (jsonutils.JSONObject, error) {
- err := self.IsEipAssociable()
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- eipStr := input.EipId
- if len(eipStr) == 0 {
- return nil, httperrors.NewMissingParameterError("eip_id")
- }
- eipObj, err := ElasticipManager.FetchByIdOrName(ctx, userCred, eipStr)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError("eip %s not found", eipStr)
- } else {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- eip := eipObj.(*SElasticip)
- eipRegion, err := eip.GetRegion()
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "eip.GetRegion"))
- }
- instRegion, _ := self.getRegion()
- if eip.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- return nil, httperrors.NewUnsupportOperationError("fixed eip cannot be associated")
- }
- if eip.IsAssociated() {
- return nil, httperrors.NewConflictError("eip has been associated")
- }
- if eipRegion.Id != instRegion.Id {
- return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different region")
- }
- if len(eip.NetworkId) > 0 {
- gns, err := self.GetNetworks("")
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
- }
- for _, gn := range gns {
- if gn.NetworkId == eip.NetworkId {
- return nil, httperrors.NewInputParameterError("cannot associate eip with same network")
- }
- }
- }
- eipZone, _ := eip.GetZone()
- if eipZone != nil {
- insZone, _ := self.getZone()
- if eipZone.Id != insZone.Id {
- return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different zone")
- }
- }
- host, _ := self.GetHost()
- if host == nil {
- return nil, httperrors.NewInputParameterError("server host is not found???")
- }
- if host.ManagerId != eip.ManagerId {
- return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different provider")
- }
- self.SetStatus(ctx, userCred, api.INSTANCE_ASSOCIATE_EIP, "associate eip")
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(self.ExternalId), "instance_external_id")
- params.Add(jsonutils.NewString(self.Id), "instance_id")
- params.Add(jsonutils.NewString(api.EIP_ASSOCIATE_TYPE_SERVER), "instance_type")
- if len(input.IpAddr) > 0 {
- params.Add(jsonutils.NewString(input.IpAddr), "ip_addr")
- }
- err = eip.StartEipAssociateTask(ctx, userCred, params, "")
- return nil, err
- }
- // 解绑EIP
- func (self *SGuest) PerformDissociateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDissociateEipInput) (jsonutils.JSONObject, error) {
- eip, err := self.GetElasticIp()
- if err != nil {
- log.Errorf("Fail to get Eip %s", err)
- return nil, httperrors.NewGeneralError(err)
- }
- if eip == nil {
- return nil, httperrors.NewInvalidStatusError("No eip to dissociate")
- }
- err = db.IsObjectRbacAllowed(ctx, eip, userCred, policy.PolicyActionGet)
- if err != nil {
- return nil, errors.Wrap(err, "eip is not accessible")
- }
- self.SetStatus(ctx, userCred, api.INSTANCE_DISSOCIATE_EIP, "associate eip")
- autoDelete := (input.AudoDelete != nil && *input.AudoDelete)
- err = eip.StartEipDissociateTask(ctx, userCred, autoDelete, "")
- if err != nil {
- log.Errorf("fail to start dissociate task %s", err)
- return nil, httperrors.NewGeneralError(err)
- }
- return nil, nil
- }
- // 创建EIP
- func (self *SGuest) PerformCreateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerCreateEipInput) (jsonutils.JSONObject, error) {
- var (
- host, _ = self.GetHost()
- region, _ = host.GetRegion()
- regionDriver = region.GetDriver()
- bw = input.Bandwidth
- chargeType = input.ChargeType
- bgpType = input.BgpType
- autoDellocate = (input.AutoDellocate != nil && *input.AutoDellocate)
- )
- err := self.IsEipAssociable()
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(chargeType) == 0 {
- chargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
- }
- if chargeType == billing_api.NET_CHARGE_TYPE_BY_BANDWIDTH {
- if bw == 0 {
- return nil, httperrors.NewMissingParameterError("bandwidth")
- }
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- err = driver.ValidateCreateEip(ctx, userCred, input)
- if err != nil {
- return nil, err
- }
- eipPendingUsage := &SRegionQuota{Eip: 1}
- keys, err := self.GetRegionalQuotaKeys()
- if err != nil {
- return nil, err
- }
- eipPendingUsage.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, eipPendingUsage)
- if err != nil {
- return nil, httperrors.NewOutOfQuotaError("Out of eip quota: %s", err)
- }
- eip, err := ElasticipManager.NewEipForVMOnHost(ctx, userCred, &NewEipForVMOnHostArgs{
- Bandwidth: int(bw),
- BgpType: bgpType,
- ChargeType: chargeType,
- AutoDellocate: autoDellocate,
- Guest: self,
- Host: host,
- PendingUsage: eipPendingUsage,
- })
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, eipPendingUsage, eipPendingUsage, false)
- return nil, httperrors.NewGeneralError(err)
- }
- opts := api.ElasticipAssociateInput{
- InstanceId: self.Id,
- InstanceExternalId: self.ExternalId,
- InstanceType: api.EIP_ASSOCIATE_TYPE_SERVER,
- }
- err = eip.AllocateAndAssociateInstance(ctx, userCred, self, opts, "")
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return nil, nil
- }
- func (self *SGuest) setUserData(ctx context.Context, userCred mcclient.TokenCredential, data string) error {
- if err := userdata.ValidateUserdata(data, self.OsType); err != nil {
- return err
- }
- encodeData, err := userdata.Encode(data)
- if err != nil {
- return errors.Wrap(err, "encode guest userdata")
- }
- err = self.SetMetadata(ctx, "user_data", encodeData, userCred)
- if err != nil {
- return err
- }
- return nil
- }
- func (self *SGuest) GetUserData(ctx context.Context, userCred mcclient.TokenCredential) string {
- userData := self.GetMetadata(ctx, "user_data", userCred)
- if len(userData) == 0 {
- return userData
- }
- decodeData, _ := userdata.Decode(userData)
- return decodeData
- }
- // 更新自定义脚本数据
- func (self *SGuest) PerformUserData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerUserDataInput) (jsonutils.JSONObject, error) {
- if len(input.UserData) == 0 {
- return nil, httperrors.NewMissingParameterError("user_data")
- }
- // validate UserData
- if err := userdata.ValidateUserdata(input.UserData, self.OsType); err != nil {
- return nil, httperrors.NewInputParameterError("Invalid userdata: %v", err)
- }
- err := self.setUserData(ctx, userCred, input.UserData)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(self.HostId) > 0 {
- return nil, self.StartSyncTask(ctx, userCred, false, "")
- }
- return nil, nil
- }
- // 设置Qemu参数
- func (self *SGuest) PerformSetQemuParams(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- isaSerial, err := data.GetString("disable_isa_serial")
- if err == nil {
- err = self.SetMetadata(ctx, "disable_isa_serial", isaSerial, userCred)
- if err != nil {
- return nil, err
- }
- }
- pvpanic, err := data.GetString("disable_pvpanic")
- if err == nil {
- err = self.SetMetadata(ctx, "disable_pvpanic", pvpanic, userCred)
- if err != nil {
- return nil, err
- }
- }
- usbKbd, err := data.GetString("disable_usb_kbd")
- if err == nil {
- err = self.SetMetadata(ctx, "disable_usb_kbd", usbKbd, userCred)
- if err != nil {
- return nil, err
- }
- }
- usbContType, err := data.GetString("usb_controller_type")
- if err == nil {
- err = self.SetMetadata(ctx, "usb_controller_type", usbContType, userCred)
- if err != nil {
- return nil, err
- }
- }
- return nil, nil
- }
- func (self *SGuest) SwitchToBackup(userCred mcclient.TokenCredential) error {
- diff, err := db.Update(self, func() error {
- self.HostId, self.BackupHostId = self.BackupHostId, self.HostId
- return nil
- })
- if err != nil {
- return err
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
- return nil
- }
- // 主备机切换
- func (self *SGuest) PerformSwitchToBackup(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if self.Status == api.VM_BLOCK_STREAM {
- return nil, httperrors.NewBadRequestError("Cannot swith to backup when guest in status %s", self.Status)
- }
- if len(self.BackupHostId) == 0 {
- return nil, httperrors.NewBadRequestError("Guest no backup host")
- }
- backupHost := HostManager.FetchHostById(self.BackupHostId)
- if backupHost.HostStatus != api.HOST_ONLINE {
- return nil, httperrors.NewBadRequestError("Can't switch to backup host on host status %s", backupHost.HostStatus)
- }
- if !self.IsGuestBackupMirrorJobReady(ctx, userCred) {
- return nil, httperrors.NewBadRequestError("Guest can't switch to backup, mirror job not ready")
- }
- if !utils.IsInStringArray(self.BackupGuestStatus, []string{api.VM_RUNNING, api.VM_READY, api.VM_UNKNOWN}) {
- return nil, httperrors.NewInvalidStatusError("Guest can't switch to backup with backup status %s", self.BackupGuestStatus)
- }
- oldStatus := self.Status
- taskData := jsonutils.NewDict()
- taskData.Set("old_status", jsonutils.NewString(oldStatus))
- taskData.Set("auto_start", jsonutils.NewBool(jsonutils.QueryBoolean(data, "auto_start", false)))
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestSwitchToBackupTask", self, userCred, taskData, "", "", nil); err != nil {
- log.Errorln(err)
- return nil, err
- } else {
- self.SetStatus(ctx, userCred, api.VM_SWITCH_TO_BACKUP, "Switch to backup")
- task.ScheduleRun(nil)
- }
- return nil, nil
- }
- func (manager *SGuestManager) getGuests(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) ([]SGuest, error) {
- _guests := []string{}
- data.Unmarshal(&_guests, "guests")
- if len(_guests) == 0 {
- return nil, httperrors.NewMissingParameterError("guests")
- }
- guests := []SGuest{}
- q1 := manager.Query().In("id", _guests)
- q2 := manager.Query().In("name", _guests)
- q2 = manager.FilterByOwner(ctx, q2, manager, userCred, userCred, manager.NamespaceScope())
- q2 = manager.FilterBySystemAttributes(q2, userCred, data, manager.ResourceScope())
- q := sqlchemy.Union(q1, q2).Query().Distinct()
- err := db.FetchModelObjects(manager, q, &guests)
- if err != nil {
- return nil, err
- }
- guestStr := []string{}
- for _, guest := range guests {
- guestStr = append(guestStr, guest.Id)
- guestStr = append(guestStr, guest.Name)
- }
- for _, guest := range _guests {
- if !utils.IsInStringArray(guest, guestStr) {
- return nil, httperrors.NewResourceNotFoundError("failed to found guest %s", guest)
- }
- }
- return guests, nil
- }
- func (manager *SGuestManager) getUserMetadata(data jsonutils.JSONObject) (map[string]string, error) {
- if !data.Contains("metadata") {
- return nil, httperrors.NewMissingParameterError("metadata")
- }
- metadata, err := data.GetMap("metadata")
- if err != nil {
- return nil, httperrors.NewInputParameterError("input data not key value dict")
- }
- dictStore := make(map[string]string)
- for k, v := range metadata {
- dictStore[db.USER_TAG_PREFIX+k], _ = v.GetString()
- }
- return dictStore, nil
- }
- func (manager *SGuestManager) PerformBatchUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- guests, err := manager.getGuests(ctx, userCred, data)
- if err != nil {
- return nil, err
- }
- metadata, err := manager.getUserMetadata(data)
- if err != nil {
- return nil, err
- }
- for _, guest := range guests {
- err := guest.SetUserMetadataValues(ctx, metadata, userCred)
- if err != nil {
- msg := fmt.Errorf("set guest %s(%s) user-metadata error: %v", guest.Name, guest.Id, err)
- return nil, httperrors.NewGeneralError(msg)
- }
- }
- return jsonutils.Marshal(guests), nil
- }
- func (manager *SGuestManager) PerformBatchSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- guests, err := manager.getGuests(ctx, userCred, data)
- if err != nil {
- return nil, err
- }
- metadata, err := manager.getUserMetadata(data)
- if err != nil {
- return nil, err
- }
- for _, guest := range guests {
- err := guest.SetUserMetadataAll(ctx, metadata, userCred)
- if err != nil {
- msg := fmt.Errorf("set guest %s(%s) user-metadata error: %v", guest.Name, guest.Id, err)
- return nil, httperrors.NewGeneralError(msg)
- }
- }
- return jsonutils.Marshal(guests), nil
- }
- func (self *SGuest) PerformBlockStreamFailed(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if len(self.BackupHostId) > 0 {
- if err := self.SetGuestBackupMirrorJobFailed(ctx, userCred); err != nil {
- return nil, errors.Wrap(err, "set guest backup mirror job failed")
- }
- }
- if self.Status == api.VM_BLOCK_STREAM || self.Status == api.VM_RUNNING {
- reason, _ := data.GetString("reason")
- logclient.AddSimpleActionLog(self, logclient.ACT_VM_BLOCK_STREAM, reason, userCred, false)
- return nil, self.SetStatus(ctx, userCred, api.VM_BLOCK_STREAM_FAIL, reason)
- }
- return nil, nil
- }
- func (self *SGuest) PerformSlaveBlockStreamReady(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if len(self.BackupHostId) > 0 {
- if err := self.TrySetGuestBackupMirrorJobReady(ctx, userCred); err != nil {
- return nil, errors.Wrap(err, "set guest backup mirror job status ready")
- }
- self.SetBackupGuestStatus(userCred, api.VM_RUNNING, "perform slave block stream ready")
- }
- return nil, nil
- }
- func (self *SGuest) PerformBlockMirrorReady(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Status == api.VM_BLOCK_STREAM || self.Status == api.VM_RUNNING {
- diskId, err := data.GetString("disk_id")
- if err != nil {
- return nil, httperrors.NewMissingParameterError("disk_id")
- }
- log.Infof("disk_id %s", diskId)
- disk := DiskManager.FetchDiskById(diskId)
- if disk == nil {
- return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
- }
- if taskId := disk.GetMetadata(ctx, api.DISK_CLONE_TASK_ID, userCred); len(taskId) > 0 {
- log.Infof("task_id %s", taskId)
- if err := self.startSwitchToClonedDisk(ctx, userCred, taskId); err != nil {
- return nil, errors.Wrap(err, "startSwitchToClonedDisk")
- }
- }
- }
- return nil, nil
- }
- func (self *SGuest) StartMirrorJob(ctx context.Context, userCred mcclient.TokenCredential, nbdServerPort int64, parentTaskId string) error {
- taskData := jsonutils.NewDict()
- taskData.Set("nbd_server_port", jsonutils.NewInt(nbdServerPort))
- if task, err := taskman.TaskManager.NewTask(
- ctx, "GuestReSyncToBackup", self, userCred, taskData, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (manager *SGuestManager) PerformDirtyServerStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- guestId, err := data.GetString("guest_id")
- if err != nil {
- return nil, httperrors.NewMissingParameterError("guest_id")
- }
- guest := manager.FetchGuestById(guestId)
- if guest == nil {
- return nil, httperrors.NewNotFoundError("Guest %s not found", guestId)
- }
- hostId, _ := data.GetString("host_id")
- if len(hostId) == 0 {
- return nil, httperrors.NewMissingParameterError("host_id")
- }
- if guest.HostId == hostId {
- // master guest
- err := guest.StartGueststartTask(ctx, userCred, nil, "")
- return nil, err
- } else if guest.BackupHostId == hostId {
- // slave guest
- err := guest.GuestStartAndSyncToBackup(ctx, userCred, "", guest.Status)
- return nil, err
- }
- return nil, nil
- }
- func (manager *SGuestManager) PerformDirtyServerVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- guestId, err := data.GetString("guest_id")
- if err != nil {
- return nil, httperrors.NewMissingParameterError("guest_id")
- }
- iguest, err := manager.FetchById(guestId)
- if err == sql.ErrNoRows {
- ret := jsonutils.NewDict()
- ret.Set("guest_unknown_need_clean", jsonutils.JSONTrue)
- return ret, nil
- } else {
- if err != nil {
- return nil, httperrors.NewInternalServerError("Fetch guest error %s", err)
- } else {
- guest := iguest.(*SGuest)
- hostId, _ := data.GetString("host_id")
- if len(hostId) == 0 {
- return nil, httperrors.NewMissingParameterError("host_id")
- }
- if guest.HostId != hostId && guest.BackupHostId != hostId {
- return nil, guest.StartGuestDeleteOnHostTask(ctx, userCred, hostId, false, "")
- }
- }
- }
- return nil, nil
- }
- func (self *SGuest) StartGuestDeleteOnHostTask(ctx context.Context, userCred mcclient.TokenCredential, hostId string, purge bool, parentTaskId string) error {
- taskData := jsonutils.NewDict()
- taskData.Set("host_id", jsonutils.NewString(hostId))
- taskData.Set("purge", jsonutils.NewBool(purge))
- if task, err := taskman.TaskManager.NewTask(
- ctx, "GuestDeleteOnHostTask", self, userCred, taskData, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (guest *SGuest) GuestStartAndSyncToBackup(
- ctx context.Context, userCred mcclient.TokenCredential, parentTaskId, guestStatus string,
- ) error {
- data := jsonutils.NewDict()
- data.Set("guest_status", jsonutils.NewString(guestStatus))
- task, err := taskman.TaskManager.NewTask(
- ctx, "GuestStartAndSyncToBackupTask", guest, userCred, data, parentTaskId, "", nil)
- if err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) guestDisksStorageTypeIsLocal() bool {
- disks, _ := self.GetDisks()
- for _, disk := range disks {
- storage, _ := disk.GetStorage()
- if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
- return false
- }
- }
- return true
- }
- func (self *SGuest) guestDisksStorageTypeIsShared() bool {
- disks, _ := self.GetDisks()
- for _, disk := range disks {
- storage, _ := disk.GetStorage()
- if storage.StorageType == api.STORAGE_LOCAL || storage.StorageType == api.STORAGE_LVM {
- return false
- }
- }
- return true
- }
- // 创建备机
- func (self *SGuest) PerformCreateBackup(
- ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if len(self.BackupHostId) > 0 {
- return nil, httperrors.NewBadRequestError("Already have backup server")
- }
- if self.Status != api.VM_READY {
- return nil, httperrors.NewBadRequestError("Can't create backup in guest status %s", self.Status)
- }
- if !self.guestDisksStorageTypeIsLocal() {
- return nil, httperrors.NewBadRequestError("Cannot create backup with shared storage")
- }
- if self.Hypervisor != api.HYPERVISOR_KVM {
- return nil, httperrors.NewBadRequestError("Backup only support hypervisor kvm")
- }
- devs, _ := self.GetIsolatedDevices()
- if len(devs) > 0 {
- return nil, httperrors.NewBadRequestError("Cannot create backup with isolated devices")
- }
- hasSnapshot, err := self.GuestDisksHasSnapshot()
- if err != nil {
- return nil, httperrors.NewInternalServerError("GuestDisksHasSnapshot fail %s", err)
- }
- if hasSnapshot {
- return nil, httperrors.NewBadRequestError("Cannot create backup with snapshot")
- }
- return self.StartGuestCreateBackupTask(ctx, userCred, "", data)
- }
- func (self *SGuest) StartGuestCreateBackupTask(
- ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- req := self.getGuestBackupResourceRequirements(ctx, userCred)
- keys, err := self.GetQuotaKeys()
- if err != nil {
- return nil, err
- }
- req.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, &req)
- if err != nil {
- return nil, httperrors.NewOutOfQuotaError("%v", err)
- }
- params := data.(*jsonutils.JSONDict)
- params.Set("guest_status", jsonutils.NewString(self.Status))
- self.SetStatus(ctx, userCred, api.VM_BACKUP_CREATING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateBackupTask", self, userCred, params, parentTaskId, "", &req)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, &req, &req, false)
- log.Errorln(err)
- return nil, err
- } else {
- task.ScheduleRun(nil)
- }
- return nil, nil
- }
- // 删除备机
- func (self *SGuest) PerformDeleteBackup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if len(self.BackupHostId) == 0 {
- return nil, httperrors.NewBadRequestError("Guest without backup")
- }
- backupHost := HostManager.FetchHostById(self.BackupHostId)
- if backupHost == nil {
- return nil, httperrors.NewNotFoundError("Guest backup host not found")
- }
- if backupHost.Status == api.HOST_OFFLINE && !jsonutils.QueryBoolean(data, "purge", false) {
- return nil, httperrors.NewBadRequestError("Backup host is offline")
- }
- taskData := jsonutils.NewDict()
- taskData.Set("purge", jsonutils.NewBool(jsonutils.QueryBoolean(data, "purge", false)))
- taskData.Set("create", jsonutils.NewBool(jsonutils.QueryBoolean(data, "create", false)))
- self.SetStatus(ctx, userCred, api.VM_DELETING_BACKUP, "delete backup server")
- if task, err := taskman.TaskManager.NewTask(
- ctx, "GuestDeleteBackupTask", self, userCred, taskData, "", "", nil); err != nil {
- log.Errorln(err)
- return nil, err
- } else {
- task.ScheduleRun(nil)
- }
- return nil, nil
- }
- func (self *SGuest) CreateBackupDisks(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateBackupDisksTask", self, userCred, nil, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) StartCreateBackup(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, kwargs *jsonutils.JSONDict) error {
- if kwargs == nil {
- kwargs = jsonutils.NewDict()
- }
- kwargs.Add(jsonutils.NewString("create"), "deploy_action")
- if task, err := taskman.TaskManager.NewTask(ctx, "GuestDeployBackupTask", self, userCred, kwargs, parentTaskId, "", nil); err != nil {
- log.Errorln(err)
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- // 备机开机
- func (self *SGuest) PerformStartBackup(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if !self.HasBackupGuest() {
- return nil, httperrors.NewBadRequestError("guest has no backup guest")
- }
- if host := HostManager.FetchHostById(self.BackupHostId); host.HostStatus != api.HOST_ONLINE {
- return nil, httperrors.NewBadRequestError("can't start backup guest on host status %s", host.HostStatus)
- }
- if self.Status != api.VM_RUNNING || self.BackupGuestStatus == api.VM_RUNNING {
- return nil, httperrors.NewBadRequestError("can't start backup guest on backup guest status %s", self.BackupGuestStatus)
- }
- return nil, self.GuestStartAndSyncToBackup(ctx, userCred, "", self.Status)
- }
- func (self *SGuest) PerformSetExtraOption(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetExtraOptionInput) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, errors.Wrap(err, "input.Validate")
- }
- extraOptions := self.GetExtraOptions(ctx, userCred)
- optVal := make([]string, 0)
- extraOptions.Unmarshal(&optVal, input.Key)
- if !utils.IsInStringArray(input.Value, optVal) {
- optVal = append(optVal, input.Value)
- }
- extraOptions.Set(input.Key, jsonutils.Marshal(optVal))
- return nil, self.SetExtraOptions(ctx, userCred, extraOptions)
- }
- func (self *SGuest) GetExtraOptions(ctx context.Context, userCred mcclient.TokenCredential) *jsonutils.JSONDict {
- options := self.GetMetadataJson(ctx, "extra_options", userCred)
- o, ok := options.(*jsonutils.JSONDict)
- if ok {
- return o
- }
- return jsonutils.NewDict()
- }
- func (self *SGuest) SetExtraOptions(ctx context.Context, userCred mcclient.TokenCredential, extraOptions *jsonutils.JSONDict) error {
- return self.SetMetadata(ctx, "extra_options", extraOptions, userCred)
- }
- func (self *SGuest) PerformDelExtraOption(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDelExtraOptionInput) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, errors.Wrap(err, "input.Validate")
- }
- extraOptions := self.GetExtraOptions(ctx, userCred)
- var newOpt []string
- if len(input.Value) > 0 {
- optVal := make([]string, 0)
- extraOptions.Unmarshal(&optVal, input.Key)
- for _, v := range optVal {
- if v != input.Value {
- newOpt = append(newOpt, v)
- }
- }
- }
- if len(newOpt) > 0 {
- extraOptions.Set(input.Key, jsonutils.Marshal(newOpt))
- } else if extraOptions.Contains(input.Key) {
- extraOptions.Remove(input.Key)
- }
- return nil, self.SetExtraOptions(ctx, userCred, extraOptions)
- }
- // 取消定时删除
- func (self *SGuest) PerformCancelExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
- return nil, httperrors.NewBadRequestError("guest billing type %s not support cancel expire", self.BillingType)
- }
- err := SaveReleaseAt(ctx, self, userCred, time.Time{})
- if err != nil {
- return nil, err
- }
- disks, err := self.GetDisks()
- if err != nil {
- return nil, err
- }
- for i := 0; i < len(disks); i += 1 {
- if disks[i].BillingType == billing_api.BILLING_TYPE_POSTPAID {
- err := SaveReleaseAt(ctx, &disks[i], userCred, time.Time{})
- if err != nil {
- return nil, err
- }
- }
- }
- return nil, nil
- }
- // 设置定时删除
- func (self *SGuest) PerformPostpaidExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PostpaidExpireInput) (jsonutils.JSONObject, error) {
- if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
- return nil, httperrors.NewBadRequestError("guest billing type is %s", self.BillingType)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.IsSupportPostpaidExpire() {
- return nil, httperrors.NewBadRequestError("guest %s unsupport postpaid expire", self.Hypervisor)
- }
- releaseAt, err := input.GetReleaseAt()
- if err != nil {
- return nil, err
- }
- err = SaveReleaseAt(ctx, self, userCred, releaseAt)
- if err != nil {
- return nil, err
- }
- return nil, nil
- }
- // 续费
- func (self *SGuest) PerformRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- durationStr, _ := data.GetString("duration")
- if len(durationStr) == 0 {
- return nil, httperrors.NewInputParameterError("missong duration")
- }
- bc, err := billing.ParseBillingCycle(durationStr)
- if err != nil {
- return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.IsSupportedBillingCycle(bc) {
- return nil, httperrors.NewInputParameterError("unsupported duration %s", durationStr)
- }
- err = self.startGuestRenewTask(ctx, userCred, durationStr, "")
- if err != nil {
- return nil, err
- }
- return nil, nil
- }
- func (self *SGuest) GetStorages() ([]SStorage, error) {
- q := StorageManager.Query().Distinct()
- disks := DiskManager.Query().SubQuery()
- guestdisks := GuestdiskManager.Query().Equals("guest_id", self.Id).SubQuery()
- q = q.Join(disks, sqlchemy.Equals(disks.Field("storage_id"), q.Field("id")))
- q = q.Join(guestdisks, sqlchemy.Equals(guestdisks.Field("disk_id"), disks.Field("id")))
- ret := []SStorage{}
- err := db.FetchModelObjects(StorageManager, q, &ret)
- if err != nil {
- return nil, err
- }
- return ret, nil
- }
- func (self *SGuest) startGuestRenewTask(ctx context.Context, userCred mcclient.TokenCredential, duration string, parentTaskId string) error {
- self.SetStatus(ctx, userCred, api.VM_RENEWING, "")
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewString(duration), "duration")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestRenewTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("fail to crate GuestRenewTask %s", err)
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) SaveRenewInfo(
- ctx context.Context, userCred mcclient.TokenCredential,
- bc *billing.SBillingCycle, expireAt *time.Time, billingType billing_api.TBillingType,
- ) error {
- err := SaveRenewInfo(ctx, userCred, self, bc, expireAt, billingType)
- if err != nil {
- return err
- }
- disks, err := self.GetDisks()
- if err != nil {
- return err
- }
- for i := 0; i < len(disks); i += 1 {
- if disks[i].AutoDelete {
- err = SaveRenewInfo(ctx, userCred, &disks[i], bc, expireAt, billingType)
- if err != nil {
- return err
- }
- }
- }
- return nil
- }
- func (self *SGuest) PerformSetNetworkNumQueues(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetNetworkNumQueuesInput,
- ) (jsonutils.JSONObject, error) {
- if self.Status != api.VM_READY {
- return nil, httperrors.NewInvalidStatusError("can't set network num_queues on vm %s", self.Status)
- }
- if input.NumQueues < 1 {
- return nil, httperrors.NewInputParameterError("invalid num_queues %d", input.NumQueues)
- }
- gn, err := self.GetGuestnetworkByMac(input.MacAddr)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewNotFoundError("guest network mac %s not found", input.MacAddr)
- }
- }
- _, err = db.Update(gn, func() error {
- gn.NumQueues = input.NumQueues
- return nil
- })
- return nil, err
- }
- func (self *SGuest) PerformStreamDisksComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- disks, err := self.GetDisks()
- if err != nil {
- return nil, err
- }
- for _, disk := range disks {
- if len(disk.SnapshotId) > 0 && disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
- SnapshotManager.AddRefCount(disk.SnapshotId, -1)
- disk.SetMetadata(ctx, "merge_snapshot", jsonutils.JSONFalse, userCred)
- }
- if len(disk.GetMetadata(ctx, api.DISK_META_REMOTE_ACCESS_PATH, nil)) > 0 {
- disk.SetMetadata(ctx, api.DISK_META_REMOTE_ACCESS_PATH, "", userCred)
- }
- }
- return nil, nil
- }
- // 导入虚拟机
- func (man *SGuestManager) PerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- desc := &api.SImportGuestDesc{}
- if err := data.Unmarshal(desc); err != nil {
- return nil, httperrors.NewInputParameterError("Invalid desc: %s", data.String())
- }
- if len(desc.Id) == 0 {
- return nil, httperrors.NewInputParameterError("Server Id is empty")
- }
- if len(desc.Name) == 0 {
- return nil, httperrors.NewInputParameterError("Server Name is empty")
- }
- if obj, _ := man.FetchByIdOrName(ctx, userCred, desc.Id); obj != nil {
- return nil, httperrors.NewInputParameterError("Server %s already exists", desc.Id)
- }
- if err := db.NewNameValidator(ctx, man, userCred, desc.Name, nil); err != nil {
- return nil, err
- }
- if hostObj, _ := HostManager.FetchByIdOrName(ctx, userCred, desc.HostId); hostObj == nil {
- return nil, httperrors.NewNotFoundError("Host %s not found", desc.HostId)
- } else {
- desc.HostId = hostObj.GetId()
- }
- guset, err := man.DoImport(ctx, userCred, desc)
- if err != nil {
- return nil, err
- }
- return jsonutils.Marshal(guset), nil
- }
- func (man *SGuestManager) DoImport(
- ctx context.Context, userCred mcclient.TokenCredential, desc *api.SImportGuestDesc,
- ) (*SGuest, error) {
- // 1. create import guest on host
- gst, err := man.createImportGuest(ctx, userCred, desc)
- if err != nil {
- return nil, err
- }
- // 2. import networks
- if err := gst.importNics(ctx, userCred, desc.Nics); err != nil {
- return gst, err
- }
- // 3. import disks
- if err := gst.importDisks(ctx, userCred, desc.Disks); err != nil {
- return gst, err
- }
- // 4. set metadata
- for k, v := range desc.Metadata {
- gst.SetMetadata(ctx, k, v, userCred)
- }
- return gst, nil
- }
- func (man *SGuestManager) createImportGuest(ctx context.Context, userCred mcclient.TokenCredential, desc *api.SImportGuestDesc) (*SGuest, error) {
- model, err := db.NewModelObject(man)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- gst, ok := model.(*SGuest)
- if !ok {
- return nil, httperrors.NewGeneralError(fmt.Errorf("Can't convert %#v to *SGuest model", model))
- }
- gst.ProjectId = userCred.GetProjectId()
- gst.DomainId = userCred.GetProjectDomainId()
- gst.IsSystem = desc.IsSystem
- gst.Id = desc.Id
- gst.Name = desc.Name
- gst.HostId = desc.HostId
- gst.Status = api.VM_IMPORT
- gst.Hypervisor = desc.Hypervisor
- gst.VmemSize = desc.MemSizeMb
- gst.VcpuCount = desc.Cpu
- gst.BootOrder = desc.BootOrder
- gst.Description = desc.Description
- err = man.TableSpec().Insert(ctx, gst)
- return gst, err
- }
- func ToNetConfig(n *api.SImportNic, net *SNetwork) *api.NetworkConfig {
- return &api.NetworkConfig{
- Network: net.Id,
- Wire: net.WireId,
- Address: n.Ip,
- Mac: n.Mac,
- Driver: n.Driver,
- BwLimit: n.BandWidth,
- }
- }
- func (self *SGuest) importNics(ctx context.Context, userCred mcclient.TokenCredential, nics []api.SImportNic) error {
- if len(nics) == 0 {
- return httperrors.NewInputParameterError("Empty import nics")
- }
- for _, nic := range nics {
- q := GuestnetworkManager.Query()
- count := q.Filter(sqlchemy.OR(
- sqlchemy.Equals(q.Field("mac_addr"), nic.Mac),
- sqlchemy.Equals(q.Field("ip_addr"), nic.Ip)),
- ).Count()
- if count > 0 {
- return httperrors.NewInputParameterError("ip %s or mac %s has been registered", nic.Ip, nic.Mac)
- }
- net, err := NetworkManager.GetOnPremiseNetworkOfIP(nic.Ip, "", tristate.None)
- if err != nil {
- return httperrors.NewNotFoundError("Not found network by ip %s", nic.Ip)
- }
- host, _ := self.GetHost()
- _, err = self.attach2NetworkDesc(ctx, userCred, host, ToNetConfig(&nic, net), nil, nil)
- if err != nil {
- return err
- }
- }
- return nil
- }
- func ToDiskConfig(d *api.SImportDisk) *api.DiskConfig {
- ret := &api.DiskConfig{
- SizeMb: d.SizeMb,
- ImageId: d.TemplateId,
- Format: d.Format,
- Driver: d.Driver,
- DiskId: d.DiskId,
- Cache: d.CacheMode,
- Backend: d.Backend,
- }
- if len(d.Mountpoint) > 0 {
- ret.Mountpoint = d.Mountpoint
- }
- if len(d.Fs) > 0 {
- ret.Fs = d.Fs
- }
- return ret
- }
- func (self *SGuest) importDisks(ctx context.Context, userCred mcclient.TokenCredential, disks []api.SImportDisk) error {
- if len(disks) == 0 {
- return httperrors.NewInputParameterError("Empty import disks")
- }
- host, _ := self.GetHost()
- for _, disk := range disks {
- disk, err := self.createDiskOnHost(ctx, userCred, host, ToDiskConfig(&disk), nil, true, true, nil, nil, true)
- if err != nil {
- return err
- }
- disk.SetStatus(ctx, userCred, api.DISK_READY, "")
- }
- return nil
- }
- // 从Libvirt导入虚拟机
- func (manager *SGuestManager) PerformImportFromLibvirt(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- host := &api.SLibvirtHostConfig{}
- if err := data.Unmarshal(host); err != nil {
- return nil, httperrors.NewInputParameterError("Unmarshal data error %s", err)
- }
- if len(host.XmlFilePath) == 0 {
- return nil, httperrors.NewInputParameterError("Some host config missing xml_file_path")
- }
- if len(host.HostIp) == 0 {
- return nil, httperrors.NewInputParameterError("Some host config missing host ip")
- }
- sHost, err := HostManager.GetHostByIp("", api.HOST_TYPE_HYPERVISOR, host.HostIp)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Invalid host ip %s", host.HostIp)
- }
- for _, server := range host.Servers {
- for mac, ip := range server.MacIp {
- _, err = net.ParseMAC(mac)
- if err != nil {
- return nil, httperrors.NewBadRequestError("Invalid server mac address %s", mac)
- }
- nIp := net.ParseIP(ip)
- if nIp == nil {
- return nil, httperrors.NewBadRequestError("Invalid server ip address %s", ip)
- }
- q := GuestnetworkManager.Query()
- count := q.Filter(sqlchemy.OR(
- sqlchemy.Equals(q.Field("mac_addr"), mac),
- sqlchemy.Equals(q.Field("ip_addr"), ip)),
- ).Count()
- if count > 0 {
- return nil, httperrors.NewInputParameterError("ip %s or mac %s has been registered", mac, ip)
- }
- }
- }
- taskData := jsonutils.NewDict()
- taskData.Set("xml_file_path", jsonutils.NewString(host.XmlFilePath))
- taskData.Set("servers", jsonutils.Marshal(host.Servers))
- taskData.Set("monitor_path", jsonutils.NewString(host.MonitorPath))
- task, err := taskman.TaskManager.NewTask(ctx, "HostImportLibvirtServersTask", sHost, userCred,
- taskData, "", "", nil)
- if err != nil {
- return nil, httperrors.NewInternalServerError("NewTask error: %s", err)
- }
- task.ScheduleRun(nil)
- return nil, nil
- }
- func (self *SGuest) GetDetailsVirtInstall(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM {
- return nil, httperrors.NewBadRequestError("Hypervisor %s can't generate libvirt xml", self.Hypervisor)
- }
- var (
- vdiProtocol string
- vdiListenPort int64
- )
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- host, _ := self.GetHost()
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM}) {
- vncInfo, err := driver.GetGuestVncInfo(ctx, userCred, self, host, nil)
- if err != nil {
- log.Errorln(err)
- return nil, err
- }
- vdiProtocol = vncInfo.Protocol
- vdiListenPort = int64(vncInfo.Port)
- }
- extraCmdline, _ := query.GetArray("extra_cmdline")
- libvirtBridge, _ := query.GetString("libvirt_bridge")
- if len(libvirtBridge) == 0 {
- libvirtBridge = "virbr0"
- }
- virtInstallCmd, err := self.GenerateVirtInstallCommandLine(
- vdiProtocol, vdiListenPort, extraCmdline, libvirtBridge)
- if err != nil {
- return nil, httperrors.NewInternalServerError("Generate xml failed: %s", err)
- }
- res := jsonutils.NewDict()
- res.Set("virt-install-command-line", jsonutils.NewString(virtInstallCmd))
- return res, nil
- }
- func (self *SGuest) GenerateVirtInstallCommandLine(
- vdiProtocol string, vdiListenPort int64, extraCmdline []jsonutils.JSONObject, libvirtBridge string,
- ) (string, error) {
- L := func(s string) string { return s + " \\\n" }
- cmd := L("virt-install")
- cmd += L("--cpu host")
- cmd += L("--boot cdrom,hd,network")
- cmd += L(fmt.Sprintf("--name %s", self.Name))
- cmd += L(fmt.Sprintf("--ram %d", self.VmemSize))
- cmd += L(fmt.Sprintf("--vcpus %d", self.VcpuCount))
- host, _ := self.GetHost()
- // disks
- guestDisks, err := self.GetGuestDisks()
- if err != nil {
- return "", errors.Wrapf(err, "GetGuestDisks")
- }
- for _, guestDisk := range guestDisks {
- disk := guestDisk.GetDisk()
- cmd += L(
- fmt.Sprintf("--disk path=%s,bus=%s,cache=%s,io=%s",
- disk.GetPathAtHost(host),
- guestDisk.Driver,
- guestDisk.CacheMode,
- guestDisk.AioMode),
- )
- }
- // networks
- guestNetworks, err := self.GetNetworks("")
- if err != nil {
- return "", err
- }
- for _, guestNetwork := range guestNetworks {
- cmd += L(
- fmt.Sprintf("--network bridge,model=%s,source=%s,mac=%s",
- guestNetwork.Driver, libvirtBridge, guestNetwork.MacAddr),
- )
- }
- // isolated devices
- isolatedDevices, _ := self.GetIsolatedDevices()
- for _, isolatedDev := range isolatedDevices {
- cmd += L(fmt.Sprintf("--hostdev %s", isolatedDev.Addr))
- }
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM}) {
- // spice or vnc
- cmd += L(fmt.Sprintf("--graphics %s,listen=0.0.0.0,port=%d", vdiProtocol, vdiListenPort))
- } else {
- // generate xml, and need virsh define xml
- cmd += L("--print-xml")
- }
- cmd += L("--video vga")
- cmd += L("--wait 0")
- // some customized options, not verify
- for _, cmdline := range extraCmdline {
- cmd += L(cmdline.String())
- }
- // debug print
- cmd += "-d"
- return cmd, nil
- }
- func (self *SGuest) PerformSyncFixNics(ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.GuestSyncFixNicsInput) (jsonutils.JSONObject, error) {
- iVM, err := self.GetIVM(ctx)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- vnics, err := iVM.GetINics()
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- host, _ := self.GetHost()
- if host == nil {
- return nil, httperrors.NewInternalServerError("host not found???")
- }
- iplist := input.Ip
- // validate iplist
- if len(iplist) == 0 {
- return nil, httperrors.NewInputParameterError("empty ip list")
- }
- for _, ip := range iplist {
- if !regutils.MatchIP4Addr(ip) {
- return nil, httperrors.NewInputParameterError("invalid IPv4 address %s", ip)
- }
- // ip is reachable on host
- net, err := host.getNetworkOfIPOnHost(ctx, ip)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Unreachable IP %s: %s", ip, err)
- }
- // check ip is reserved or free
- rip := ReservedipManager.GetReservedIP(net, ip, api.AddressTypeIPv4)
- if rip == nil {
- // check ip is free
- nip, err := net.GetFreeIPWithLock(ctx, userCred, nil, nil, ip, "", false, api.AddressTypeIPv4)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Unavailable IP %s: occupied", ip)
- }
- if nip != ip {
- return nil, httperrors.NewInputParameterError("Unavailable IP %s: occupied", ip)
- }
- }
- }
- errs := make([]error, 0)
- for i := range vnics {
- ip := vnics[i].GetIP()
- if len(ip) == 0 {
- continue
- }
- _, err := host.getNetworkOfIPOnHost(ctx, ip)
- if err != nil {
- errs = append(errs, errors.Wrap(err, ip))
- }
- }
- if len(errs) > 0 {
- return nil, httperrors.NewInvalidStatusError("%v", errors.NewAggregate(errs))
- }
- result := self.SyncVMNics(ctx, userCred, host, vnics, iplist)
- if result.IsError() {
- return nil, httperrors.NewInternalServerError("%s", result.Result())
- }
- return nil, nil
- }
- // 更改项目
- func (guest *SGuest) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
- disks, err := guest.GetDisks()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDisks")
- }
- for i := range disks {
- disk := disks[i]
- _, err := disk.PerformChangeOwner(ctx, userCred, query, input)
- if err != nil {
- return nil, err
- }
- }
- if eip, _ := guest.GetEipOrPublicIp(); eip != nil {
- _, err := eip.PerformChangeOwner(ctx, userCred, query, input)
- if err != nil {
- return nil, err
- }
- }
- // change owner for instance snapshot
- isps, err := guest.GetInstanceSnapshots()
- if err != nil {
- return nil, errors.Wrap(err, "unable to GetInstanceSnapshots")
- }
- for i := range isps {
- _, err := isps[i].PerformChangeOwner(ctx, userCred, query, input)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to change owner for instance snapshot %s", isps[i].GetId())
- }
- }
- ctrs, err := GetContainerManager().GetContainersByPod(guest.GetId())
- if err != nil {
- return nil, errors.Wrapf(err, "get containers by guest_id %s", guest.GetId())
- }
- for _, ctr := range ctrs {
- if _, err := ctr.PerformChangeOwner(ctx, userCred, query, input); err != nil {
- return nil, errors.Wrapf(err, "unable to change owner for container %s", ctr.GetName())
- }
- }
- changOwner, err := guest.SVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input)
- if err != nil {
- return nil, err
- }
- err = guest.StartSyncTask(ctx, userCred, false, "")
- if err != nil {
- return nil, errors.Wrap(err, "PerformChangeOwner StartSyncTask err")
- }
- return changOwner, nil
- }
- // 磁盘扩容
- func (guest *SGuest) PerformResizeDisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResizeDiskInput) (jsonutils.JSONObject, error) {
- if guest.Hypervisor == api.HYPERVISOR_ESXI {
- c, err := guest.GetInstanceSnapshotCount()
- if err != nil {
- return nil, errors.Wrapf(err, "unable to GetInstanceSnapshotCount for guest %s", guest.GetName())
- }
- if c > 0 {
- return nil, httperrors.NewUnsupportOperationError("the disk of a esxi virtual machine with instance snapshots does not support resizing")
- }
- }
- if len(input.DiskId) == 0 {
- return nil, httperrors.NewMissingParameterError("disk_id")
- }
- diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
- if err != nil {
- return nil, err
- }
- guestdisk := guest.GetGuestDisk(input.DiskId)
- if guestdisk == nil {
- return nil, httperrors.NewInvalidStatusError("disk %s not attached to server", input.DiskId)
- }
- disk := diskObj.(*SDisk)
- sizeMb, err := input.SizeMb()
- if err != nil {
- return nil, err
- }
- err = disk.doResize(ctx, userCred, sizeMb, guest)
- if err != nil {
- return nil, err
- }
- return nil, nil
- }
- func (guest *SGuest) StartGuestDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, diskId string, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota) error {
- guest.SetStatus(ctx, userCred, api.VM_START_RESIZE_DISK, "StartGuestDiskResizeTask")
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewInt(sizeMb), "size")
- params.Add(jsonutils.NewString(diskId), "disk_id")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestResizeDiskTask", guest, userCred, params, parentTaskId, "", pendingUsage)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- // 磁盘限速
- func (self *SGuest) PerformIoThrottle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSetDiskIoThrottleInput) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_KVM {
- return nil, httperrors.NewBadRequestError("Hypervisor %s can't do io throttle", self.Hypervisor)
- }
- if self.Status != api.VM_RUNNING && self.Status != api.VM_READY {
- return nil, httperrors.NewServerStatusError("Cannot do io throttle in status %s", self.Status)
- }
- for diskId, bpsMb := range input.Bps {
- if bpsMb < 0 {
- return nil, httperrors.NewInputParameterError("disk %s bps must > 0", diskId)
- }
- disk := DiskManager.FetchDiskById(diskId)
- if disk == nil {
- return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
- }
- }
- for diskId, iops := range input.IOPS {
- if iops < 0 {
- return nil, httperrors.NewInputParameterError("disk %s iops must > 0", diskId)
- }
- disk := DiskManager.FetchDiskById(diskId)
- if disk == nil {
- return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
- }
- }
- if err := self.UpdateIoThrottle(input); err != nil {
- return nil, errors.Wrap(err, "update io throttles")
- }
- return nil, self.StartBlockIoThrottleTask(ctx, userCred, input)
- }
- func (self *SGuest) UpdateIoThrottle(input *api.ServerSetDiskIoThrottleInput) error {
- gds, err := self.GetGuestDisks()
- if err != nil {
- return err
- }
- for i := 0; i < len(gds); i++ {
- _, err := db.Update(&gds[i], func() error {
- if bps, ok := input.Bps[gds[i].DiskId]; ok {
- gds[i].Bps = bps
- }
- if iops, ok := input.IOPS[gds[i].DiskId]; ok {
- gds[i].Iops = iops
- }
- return nil
- })
- if err != nil {
- return nil
- }
- }
- return nil
- }
- func (self *SGuest) StartBlockIoThrottleTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerSetDiskIoThrottleInput) error {
- params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- params.Set("old_status", jsonutils.NewString(self.Status))
- self.SetStatus(ctx, userCred, api.VM_IO_THROTTLE, "start block io throttle task")
- task, err := taskman.TaskManager.NewTask(ctx, "GuestBlockIoThrottleTask", self, userCred, params, "", "", nil)
- if err != nil {
- log.Errorf("%s", err)
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) validateForBatchMigrate(ctx context.Context, rescueMode bool) (*SGuest, error) {
- guest := GuestManager.FetchGuestById(self.Id)
- if guest.Hypervisor != api.HYPERVISOR_KVM {
- return guest, httperrors.NewBadRequestError("guest %s hypervisor %s can't migrate",
- guest.Name, guest.Hypervisor)
- }
- if len(guest.BackupHostId) > 0 {
- return guest, httperrors.NewBadRequestError("guest %s has backup, can't migrate", guest.Name)
- }
- devs, _ := guest.GetIsolatedDevices()
- if len(devs) > 0 {
- return guest, httperrors.NewBadRequestError("guest %s has isolated device, can't migrate", guest.Name)
- }
- if rescueMode {
- if !guest.guestDisksStorageTypeIsShared() {
- return guest, httperrors.NewBadRequestError("can't rescue geust %s with local storage", guest.Name)
- }
- return guest, nil
- }
- if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY, api.VM_UNKNOWN}) {
- return guest, httperrors.NewBadRequestError("guest %s status %s can't migrate", guest.Name, guest.Status)
- }
- if guest.Status == api.VM_RUNNING {
- cdroms, _ := guest.getCdroms()
- for _, cdrom := range cdroms {
- if len(cdrom.ImageId) > 0 {
- return guest, httperrors.NewBadRequestError("cannot migrate with cdrom")
- }
- }
- floppys, _ := guest.getFloppys()
- for _, floppy := range floppys {
- if len(floppy.ImageId) > 0 {
- return guest, httperrors.NewBadRequestError("cannot migrate with floppy")
- }
- }
- } else if guest.Status == api.VM_UNKNOWN {
- if guest.getDefaultStorageType() == api.STORAGE_LOCAL {
- return guest, httperrors.NewBadRequestError(
- "guest %s status %s can't migrate with local storage",
- guest.Name, guest.Status,
- )
- }
- }
- return guest, nil
- }
- // 批量迁移
- func (manager *SGuestManager) PerformBatchMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- params := new(api.GuestBatchMigrateRequest)
- err := data.Unmarshal(params)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Unmarshal input error %s", err)
- }
- if len(params.GuestIds) == 0 {
- return nil, httperrors.NewInputParameterError("missing guest id")
- }
- var preferHostId string
- if len(params.PreferHostId) > 0 {
- iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, params.PreferHostId)
- if iHost == nil {
- return nil, httperrors.NewBadRequestError("Host %s not found", params.PreferHostId)
- }
- host := iHost.(*SHost)
- preferHostId = host.Id
- err := host.IsAssignable(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "IsAssignable")
- }
- }
- if params.EnableTLS == nil {
- params.EnableTLS = &options.Options.EnableTlsMigration
- }
- guests := make([]SGuest, 0)
- q := GuestManager.Query().In("id", params.GuestIds)
- err = db.FetchModelObjects(GuestManager, q, &guests)
- if err != nil {
- return nil, httperrors.NewInternalServerError("%v", err)
- }
- if len(guests) != len(params.GuestIds) {
- return nil, httperrors.NewBadRequestError("Check input guests is exist")
- }
- errs := []error{}
- for i := 0; i < len(guests); i++ {
- func() {
- lockman.LockObject(ctx, &guests[i])
- defer lockman.ReleaseObject(ctx, &guests[i])
- _, err = guests[i].validateForBatchMigrate(ctx, false)
- if err != nil {
- errs = append(errs, errors.Wrapf(err, "Guest %s", guests[i].Name))
- return
- }
- if guests[i].Status == api.VM_RUNNING {
- err = guests[i].StartGuestLiveMigrateTask(ctx, userCred,
- guests[i].Status, preferHostId, ¶ms.SkipCpuCheck, ¶ms.SkipKernelCheck,
- params.EnableTLS, params.QuciklyFinish, params.MaxBandwidthMb, nil, "",
- )
- } else {
- err = guests[i].StartMigrateTask(ctx, userCred, guests[i].Status == api.VM_UNKNOWN,
- false, guests[i].Status, preferHostId, "")
- }
- if err != nil {
- errs = append(errs, errors.Wrapf(err, "Guest %s", guests[i].Name))
- }
- }()
- }
- return nil, errors.NewAggregate(errs)
- }
- func (manager *SGuestManager) StartHostGuestsMigrateTask(
- ctx context.Context, userCred mcclient.TokenCredential,
- guests []*SGuest, kwargs *jsonutils.JSONDict, parentTaskId string,
- ) error {
- taskItems := make([]db.IStandaloneModel, len(guests))
- for i := range guests {
- taskItems[i] = guests[i]
- }
- task, err := taskman.TaskManager.NewParallelTask(ctx, "HostGuestsMigrateTask", taskItems, userCred, kwargs, parentTaskId, "", nil)
- if err != nil {
- log.Errorln(err)
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- var supportInstanceSnapshotHypervisors = []string{
- api.HYPERVISOR_KVM,
- api.HYPERVISOR_ESXI,
- api.HYPERVISOR_CNWARE,
- }
- func (self *SGuest) validateCreateInstanceSnapshot(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerCreateSnapshotParams,
- ) (*SRegionQuota, api.ServerCreateSnapshotParams, error) {
- if !utils.IsInStringArray(self.Hypervisor, supportInstanceSnapshotHypervisors) {
- return nil, input, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
- }
- disks, err := self.GetDisks()
- if err != nil {
- return nil, input, err
- }
- for i := range disks {
- if len(disks[i].SnapshotId) > 0 {
- if disks[i].GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
- return nil, input, httperrors.NewBadRequestError("disk %s backing snapshot not merged", disks[i].Id)
- }
- }
- }
- if len(self.BackupHostId) > 0 {
- return nil, input, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
- }
- if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- return nil, input, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
- }
- ownerId := self.GetOwnerId()
- // dataDict := data.(*jsonutils.JSONDict)
- // nameHint, err := dataDict.GetString("generate_name")
- if len(input.GenerateName) > 0 {
- name, err := db.GenerateName(ctx, InstanceSnapshotManager, ownerId, input.GenerateName)
- if err != nil {
- return nil, input, errors.Wrap(err, "GenerateName")
- }
- input.Name = name
- } else if len(input.Name) == 0 {
- return nil, input, httperrors.NewMissingParameterError("name")
- }
- err = db.NewNameValidator(ctx, InstanceSnapshotManager, ownerId, input.Name, nil)
- if err != nil {
- return nil, input, errors.Wrap(err, "NewNameValidator")
- }
- // construct Quota
- pendingUsage := &SRegionQuota{InstanceSnapshot: 1}
- host, _ := self.GetHost()
- provider := host.GetProviderName()
- if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
- disks, err := self.GetDisks()
- if err != nil {
- return nil, input, errors.Wrapf(err, "GetDisks")
- }
- //for i := 0; i < len(disks); i++ {
- // if storage, _ := disks[i].GetStorage(); utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
- // count, err := SnapshotManager.GetDiskManualSnapshotCount(disks[i].Id)
- // if err != nil {
- // return nil, input, httperrors.NewInternalServerError("%v", err)
- // }
- // if count >= options.Options.DefaultMaxManualSnapshotCount {
- // return nil, input, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
- // }
- // }
- //}
- pendingUsage.Snapshot = len(disks)
- }
- keys, err := self.GetRegionalQuotaKeys()
- if err != nil {
- return nil, input, errors.Wrap(err, "GetRegionalQuotaKeys")
- }
- pendingUsage.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
- if err != nil {
- return nil, input, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
- }
- return pendingUsage, input, nil
- }
- func (self *SGuest) validateCreateInstanceBackup(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerCreateInstanceBackupInput,
- ) (api.ServerCreateInstanceBackupInput, error) {
- if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM}) {
- return input, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
- }
- if len(self.BackupHostId) > 0 {
- return input, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
- }
- if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- return input, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
- }
- ownerId := self.GetOwnerId()
- if len(input.GenerateName) > 0 {
- nameHint := input.GenerateName
- name, err := db.GenerateName(ctx, InstanceBackupManager, ownerId, nameHint)
- if err != nil {
- return input, errors.Wrap(err, "db.GenerateName")
- }
- input.Name = name
- } else if len(input.Name) == 0 {
- return input, httperrors.NewMissingParameterError("name")
- }
- err := db.NewNameValidator(ctx, InstanceBackupManager, ownerId, input.Name, nil)
- if err != nil {
- return input, errors.Wrap(err, "db.NewNameValidator")
- }
- return input, nil
- }
- // 创建主机快照
- // 1. validate guest status, guest hypervisor
- // 2. validate every disk manual snapshot count
- // 3. validate snapshot quota with disk count
- func (self *SGuest) PerformInstanceSnapshot(
- ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerInstanceSnapshot,
- ) (jsonutils.JSONObject, error) {
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- lockman.LockClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
- defer lockman.ReleaseClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
- pendingUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
- if err != nil {
- return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
- }
- input.ServerCreateSnapshotParams = params
- if input.WithMemory {
- if self.Status != api.VM_RUNNING {
- return nil, httperrors.NewUnsupportOperationError("Can't save memory state when guest status is %q", self.Status)
- }
- }
- instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, input.Name, false, input.WithMemory)
- if err != nil {
- quotas.CancelPendingUsage(
- ctx, userCred, pendingUsage, pendingUsage, false)
- return nil, httperrors.NewInternalServerError("create instance snapshot failed: %s", err)
- }
- err = self.InheritTo(ctx, userCred, instanceSnapshot)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to inherit from guest %s to instance snapshot %s", self.GetId(), instanceSnapshot.GetId())
- }
- err = self.InstaceCreateSnapshot(ctx, userCred, instanceSnapshot, pendingUsage)
- if err != nil {
- quotas.CancelPendingUsage(
- ctx, userCred, pendingUsage, pendingUsage, false)
- return nil, httperrors.NewInternalServerError("start create snapshot task failed: %s", err)
- }
- return nil, nil
- }
- func (self *SGuest) PerformInstanceBackup(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerCreateInstanceBackupInput,
- ) (jsonutils.JSONObject, error) {
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- lockman.LockClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
- defer lockman.ReleaseClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
- var err error
- input, err = self.validateCreateInstanceBackup(ctx, userCred, query, input)
- if err != nil {
- return nil, errors.Wrap(err, "validateCreateInstanceBackup")
- }
- name := input.Name
- backupStorageId := input.BackupStorageId
- if backupStorageId == "" {
- return nil, httperrors.NewMissingParameterError("backup_storage_id")
- }
- ibs, err := BackupStorageManager.FetchByIdOrName(ctx, userCred, backupStorageId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(BackupStorageManager.Keyword(), backupStorageId)
- }
- if errors.Cause(err) == sqlchemy.ErrDuplicateEntry {
- return nil, httperrors.NewDuplicateResourceError(BackupStorageManager.Keyword(), backupStorageId)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- bs := ibs.(*SBackupStorage)
- if bs.Status != api.BACKUPSTORAGE_STATUS_ONLINE {
- return nil, httperrors.NewForbiddenError("can't backup guest to backup storage with status %s", bs.Status)
- }
- instanceBackup, err := InstanceBackupManager.CreateInstanceBackup(ctx, userCred, self, name, backupStorageId)
- if err != nil {
- return nil, httperrors.NewInternalServerError("create instance backup failed: %s", err)
- }
- err = self.InheritTo(ctx, userCred, instanceBackup)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to inherit from guest %s to instance backup %s", self.GetId(), instanceBackup.GetId())
- }
- err = self.InstanceCreateBackup(ctx, userCred, instanceBackup)
- if err != nil {
- return nil, httperrors.NewInternalServerError("start create backup task failed: %s", err)
- }
- return nil, nil
- }
- func (self *SGuest) InstaceCreateSnapshot(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- instanceSnapshot *SInstanceSnapshot,
- pendingUsage *SRegionQuota,
- ) error {
- self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_SNAPSHOT, "instance snapshot")
- return instanceSnapshot.StartCreateInstanceSnapshotTask(ctx, userCred, pendingUsage, "")
- }
- func (self *SGuest) InstanceCreateBackup(ctx context.Context, userCred mcclient.TokenCredential, instanceBackup *SInstanceBackup) error {
- self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_BACKUP, "instance backup")
- return instanceBackup.StartCreateInstanceBackupTask(ctx, userCred, "")
- }
- func (self *SGuest) PerformInstanceSnapshotReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResetInput) (jsonutils.JSONObject, error) {
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- if self.Status != api.VM_READY {
- return nil, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
- }
- obj, err := InstanceSnapshotManager.FetchByIdOrName(ctx, userCred, input.InstanceSnapshot)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to fetch instance snapshot %q", input.InstanceSnapshot)
- }
- instanceSnapshot := obj.(*SInstanceSnapshot)
- if instanceSnapshot.GuestId != self.GetId() {
- return nil, httperrors.NewBadRequestError("instance snapshot %q not belong server %q", instanceSnapshot.GetName(), self.GetId())
- }
- if instanceSnapshot.Status != api.INSTANCE_SNAPSHOT_READY {
- return nil, httperrors.NewBadRequestError("Instance snapshot not ready")
- }
- if input.WithMemory && !instanceSnapshot.WithMemory {
- return nil, httperrors.NewBadRequestError("Instance snapshot not with memory statefile")
- }
- err = self.StartSnapshotResetTask(ctx, userCred, instanceSnapshot, input.AutoStart, input.WithMemory)
- if err != nil {
- return nil, httperrors.NewInternalServerError("start snapshot reset failed %s", err)
- }
- return nil, nil
- }
- func (self *SGuest) StartSnapshotResetTask(ctx context.Context, userCred mcclient.TokenCredential, instanceSnapshot *SInstanceSnapshot, autoStart *bool, withMemory bool) error {
- data := jsonutils.NewDict()
- if autoStart != nil && *autoStart {
- data.Set("auto_start", jsonutils.JSONTrue)
- }
- data.Add(jsonutils.NewBool(withMemory), "with_memory")
- self.SetStatus(ctx, userCred, api.VM_START_SNAPSHOT_RESET, "start snapshot reset task")
- instanceSnapshot.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_RESET, "start snapshot reset task")
- log.Errorf("====data: %s", data)
- if task, err := taskman.TaskManager.NewTask(
- ctx, "InstanceSnapshotResetTask", instanceSnapshot, userCred, data, "", "", nil,
- ); err != nil {
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) PerformSnapshotAndClone(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.ServerSnapshotAndCloneInput,
- ) (jsonutils.JSONObject, error) {
- if err := self.ValidateEncryption(ctx, userCred); err != nil {
- return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
- }
- newlyGuestName := input.Name
- if len(input.Name) == 0 {
- return nil, httperrors.NewMissingParameterError("name")
- }
- count := 1
- if input.Count != nil {
- count = *input.Count
- if count <= 0 {
- return nil, httperrors.NewInputParameterError("count must > 0")
- }
- }
- if len(input.PreferHostId) > 0 {
- if len(input.PreferHostId) > 0 {
- iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, input.PreferHostId)
- if iHost == nil {
- return nil, httperrors.NewBadRequestError("Host %s not found", input.PreferHostId)
- }
- host := iHost.(*SHost)
- input.PreferHostId = host.Id
- }
- }
- lockman.LockRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
- // validate create instance snapshot and set snapshot pending usage
- snapshotUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
- if err != nil {
- return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
- }
- input.ServerCreateSnapshotParams = params
- // set guest pending usage
- pendingUsage, pendingRegionUsage, err := self.getGuestUsage(count)
- keys, err := self.GetQuotaKeys()
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
- return nil, errors.Wrap(err, "GetQuotaKeys")
- }
- pendingUsage.SetKeys(keys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
- return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
- }
- regionKeys, err := self.GetRegionalQuotaKeys()
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- return nil, errors.Wrap(err, "GetRegionalQuotaKeys")
- }
- pendingRegionUsage.SetKeys(regionKeys)
- err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingRegionUsage)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- return nil, errors.Wrap(err, "CheckSetPendingQuota")
- }
- // migrate snapshotUsage into regionUsage, then discard snapshotUsage
- pendingRegionUsage.Snapshot = snapshotUsage.Snapshot
- instanceSnapshotName, err := db.GenerateName(ctx, InstanceSnapshotManager, self.GetOwnerId(),
- fmt.Sprintf("%s-%s", input.Name, rand.String(8)))
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
- return nil, httperrors.NewInternalServerError("Generate snapshot name failed %s", err)
- }
- instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(
- ctx, userCred, self, instanceSnapshotName,
- input.AutoDeleteInstanceSnapshot != nil && *input.AutoDeleteInstanceSnapshot, false)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
- return nil, httperrors.NewInternalServerError("create instance snapshot failed: %s", err)
- } else {
- cancelRegionUsage := &SRegionQuota{Snapshot: snapshotUsage.Snapshot}
- quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, cancelRegionUsage, true)
- }
- err = self.StartInstanceSnapshotAndCloneTask(
- ctx, userCred, newlyGuestName, &pendingUsage, &pendingRegionUsage, instanceSnapshot, input)
- if err != nil {
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
- return nil, err
- }
- return nil, nil
- }
- func (self *SGuest) StartInstanceSnapshotAndCloneTask(
- ctx context.Context, userCred mcclient.TokenCredential, newlyGuestName string,
- pendingUsage *SQuota, pendingRegionUsage *SRegionQuota, instanceSnapshot *SInstanceSnapshot,
- input api.ServerSnapshotAndCloneInput,
- ) error {
- params := jsonutils.NewDict()
- params.Set("guest_params", jsonutils.Marshal(input))
- if task, err := taskman.TaskManager.NewTask(
- ctx, "InstanceSnapshotAndCloneTask", instanceSnapshot, userCred, params, "", "", pendingUsage, pendingRegionUsage); err != nil {
- return err
- } else {
- self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_SNAPSHOT, "instance snapshot")
- task.ScheduleRun(nil)
- return nil
- }
- }
- func (manager *SGuestManager) CreateGuestFromInstanceSnapshot(
- ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSnapshotAndCloneInput, isp *SInstanceSnapshot,
- ) (*SGuest, *jsonutils.JSONDict, error) {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- if guestName, err := db.GenerateName(ctx, manager, isp.GetOwnerId(), input.Name); err != nil {
- return nil, nil, errors.Wrap(err, "db.GenerateName")
- } else {
- input.Name = guestName
- }
- input.InstanceSnapshotId = isp.Id
- params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- iGuest, err := db.DoCreate(manager, ctx, userCred, nil, params, isp.GetOwnerId())
- if err != nil {
- return nil, nil, errors.Wrap(err, "db.DoCreate")
- }
- guest := iGuest.(*SGuest)
- notes := map[string]string{
- "instance_snapshot_id": isp.Id,
- "guest_id": isp.GuestId,
- }
- logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_SNAPSHOT_AND_CLONE, notes, userCred, true)
- func() {
- lockman.LockObject(ctx, guest)
- defer lockman.ReleaseObject(ctx, guest)
- guest.PostCreate(ctx, userCred, guest.GetOwnerId(), nil, params)
- }()
- return guest, params, nil
- }
- func (self *SGuest) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Hypervisor != api.HYPERVISOR_BAREMETAL {
- return nil, httperrors.NewInvalidStatusError("not a baremetal server")
- }
- host, _ := self.GetHost()
- if host == nil {
- return nil, httperrors.NewInvalidStatusError("no valid host")
- }
- if !host.IsBaremetal {
- return nil, httperrors.NewInvalidStatusError("host is not a baremetal")
- }
- return host.GetDetailsJnlp(ctx, userCred, query)
- }
- func (guest *SGuest) StartDeleteGuestSnapshots(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "GuestDeleteSnapshotsTask", guest, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (guest *SGuest) ValidateChangeNicBillingModeInput(ctx context.Context, input api.ServerNicTrafficLimit, needResetTraffic bool) (api.ServerNicTrafficLimit, bool, error) {
- var err error
- var gn *SGuestnetwork
- if len(input.Mac) == 0 {
- gns, err := guest.GetNetworks("")
- if err != nil {
- return input, needResetTraffic, errors.Wrap(err, "get guest networks")
- }
- if len(gns) == 0 {
- return input, needResetTraffic, httperrors.NewBadRequestError("no guest network found")
- }
- if len(gns) > 1 {
- return input, needResetTraffic, httperrors.NewBadRequestError("multiple guest networks found, please specify the mac")
- }
- input.Mac = gns[0].MacAddr
- gn = &gns[0]
- } else {
- input.Mac = netutils2.FormatMac(input.Mac)
- gn, err = guest.GetGuestnetworkByMac(input.Mac)
- if err != nil {
- return input, needResetTraffic, errors.Wrap(err, "get guest network by mac")
- }
- }
- if input.BillingType == "" {
- input.BillingType = gn.BillingType
- }
- if input.ChargeType == "" {
- input.ChargeType = gn.ChargeType
- }
- var changed bool
- input, changed, err = input.Validate(gn.BillingType, gn.ChargeType, gn.TxTrafficLimit, gn.RxTrafficLimit)
- if err != nil {
- return input, needResetTraffic, errors.Wrap(err, "validate input")
- }
- if changed {
- if gn.BillingType == billing_api.BILLING_TYPE_POSTPAID && gn.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
- // used to be postpaid traffic billing, need to stop the traffic log
- // do nothing
- }
- if input.BillingType == billing_api.BILLING_TYPE_POSTPAID && input.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
- // need to start the traffic log
- GuestNetworkTrafficLogManager.logTraffic(ctx, guest, gn, nil, time.Now(), true)
- }
- if input.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
- // need to measure the traffic from zero
- needResetTraffic = true
- }
- }
- return input, needResetTraffic, nil
- }
- func (guest *SGuest) doChangeNicBillingMode(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerNicTrafficLimit, needResetTraffic bool, action string) error {
- if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, guest.Status)
- }
- var err error
- input, needResetTraffic, err = guest.ValidateChangeNicBillingModeInput(ctx, input, needResetTraffic)
- if err != nil {
- return errors.Wrap(err, "validateChangeNicBillingModeInput")
- }
- taskName := "GuestSetNicTrafficsTask"
- if needResetTraffic {
- taskName = "GuestResetNicTrafficsTask"
- }
- params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- params.Set("old_status", jsonutils.NewString(guest.Status))
- guest.SetStatus(ctx, userCred, api.VM_SYNC_TRAFFIC_LIMIT, action)
- task, err := taskman.TaskManager.NewTask(ctx, taskName, guest, userCred, params, "", "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- task.ScheduleRun(nil)
- return nil
- }
- // 重置网卡计费方式
- func (guest *SGuest) PerformResetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input api.ServerNicTrafficLimit) (jsonutils.JSONObject, error) {
- err := guest.doChangeNicBillingMode(ctx, userCred, input, true, "PerformResetNicTrafficLimit")
- if err != nil {
- return nil, errors.Wrap(err, "doChangeNicBillingMode")
- }
- return nil, nil
- }
- // 设置网卡计费方式
- func (guest *SGuest) PerformSetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input api.ServerNicTrafficLimit) (jsonutils.JSONObject, error) {
- err := guest.doChangeNicBillingMode(ctx, userCred, input, false, "PerformSetNicTrafficLimit")
- if err != nil {
- return nil, errors.Wrap(err, "doChangeNicBillingMode")
- }
- return nil, nil
- }
- func (self *SGuest) PerformBindGroups(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- groupIdSet, err := self.checkGroups(ctx, userCred, query, data)
- if err != nil {
- return nil, err
- }
- groupGuests, err := GroupguestManager.FetchByGuestId(self.Id)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, false)
- return nil, err
- }
- for i := range groupGuests {
- groupId := groupGuests[i].GroupId
- if groupIdSet.Has(groupId) {
- groupIdSet.Delete(groupId)
- }
- }
- for _, groupId := range groupIdSet.UnsortedList() {
- _, err := GroupguestManager.Attach(ctx, groupId, self.Id)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, false)
- return nil, errors.Wrapf(err, "fail to attch group %s to guest %s", groupId, self.Id)
- }
- }
- // ignore error
- err = self.ClearSchedDescCache()
- if err != nil {
- log.Errorf("fail to clear scheduler desc cache after unbinding groups successfully")
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, true)
- return nil, nil
- }
- func (self *SGuest) PerformUnbindGroups(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- groupIdSet, err := self.checkGroups(ctx, userCred, query, data)
- if err != nil {
- return nil, err
- }
- groupGuests, err := GroupguestManager.FetchByGuestId(self.Id)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, false)
- return nil, err
- }
- for i := range groupGuests {
- joint := groupGuests[i]
- if !groupIdSet.Has(joint.GroupId) {
- continue
- }
- err := joint.Detach(ctx, userCred)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, false)
- return nil, errors.Wrapf(err, "fail to detach group %s to guest %s", joint.GroupId, self.Id)
- }
- }
- // ignore error
- err = self.ClearSchedDescCache()
- if err != nil {
- log.Errorf("fail to clear scheduler desc cache after binding groups successfully")
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, true)
- return nil, nil
- }
- // 绑定主机快照策略
- // 主机只能绑定一个快照策略,已绑定时报错
- // 若主机下任意磁盘已绑定快照策略则报错
- func (self *SGuest) PerformBindSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
- if len(input.SnapshotpolicyId) == 0 {
- return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
- }
- spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
- if err != nil {
- return nil, err
- }
- sp := spObj.(*SSnapshotPolicy)
- if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
- return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
- }
- // 主机只能绑定一个快照策略
- cnt, err := SnapshotPolicyResourceManager.GetBindingCount(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
- if err != nil {
- return nil, errors.Wrap(err, "GetBindingCount")
- }
- if cnt > 0 {
- return nil, httperrors.NewConflictError("guest already bound to a snapshot policy")
- }
- // 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
- disks, err := self.GetDisks()
- if err != nil {
- return nil, errors.Wrap(err, "GetDisks")
- }
- for _, d := range disks {
- diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
- if err != nil {
- return nil, errors.Wrap(err, "GetBindingCount for disk")
- }
- if diskCnt > 0 {
- return nil, httperrors.NewConflictError("guest has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", d.Name)
- }
- }
- sr := &SSnapshotPolicyResource{}
- sr.SetModelManager(SnapshotPolicyResourceManager, sr)
- sr.SnapshotpolicyId = sp.Id
- sr.ResourceId = self.Id
- sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
- if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
- return nil, errors.Wrap(err, "Insert")
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_BIND, input, userCred, true)
- return nil, nil
- }
- // 设置主机快照策略
- // 可覆盖当前主机绑定的快照策略,若主机下任意磁盘已绑定快照策略,则自动解除磁盘快照策略
- func (self *SGuest) PerformSetSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
- if len(input.SnapshotpolicyId) == 0 {
- return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
- }
- spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
- if err != nil {
- return nil, err
- }
- sp := spObj.(*SSnapshotPolicy)
- if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
- return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
- }
- if err := SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
- return nil, errors.Wrap(err, "RemoveByResource")
- }
- // 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
- disks, err := self.GetDisks()
- if err != nil {
- return nil, errors.Wrap(err, "GetDisks")
- }
- for _, d := range disks {
- if err := SnapshotPolicyResourceManager.RemoveByResource(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
- return nil, errors.Wrap(err, "RemoveByResource")
- }
- }
- sr := &SSnapshotPolicyResource{}
- sr.SetModelManager(SnapshotPolicyResourceManager, sr)
- sr.SnapshotpolicyId = sp.Id
- sr.ResourceId = self.Id
- sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
- if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
- return nil, errors.Wrap(err, "Insert")
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UPDATE, input, userCred, true)
- return nil, nil
- }
- func (self *SGuest) checkGroups(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
- data jsonutils.JSONObject) (sets.String, error) {
- groupIdArr := jsonutils.GetArrayOfPrefix(data, "group")
- if len(groupIdArr) == 0 {
- return nil, httperrors.NewMissingParameterError("group.0 group.1 ... ")
- }
- groupIdSet := sets.NewString()
- for i := range groupIdArr {
- groupIdStr, _ := groupIdArr[i].GetString()
- model, err := GroupManager.FetchByIdOrName(ctx, userCred, groupIdStr)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewInputParameterError("no such group %s", groupIdStr)
- }
- if err != nil {
- return nil, errors.Wrapf(err, "fail to fetch group by id or name %s", groupIdStr)
- }
- group := model.(*SGroup)
- if group.ProjectId != self.ProjectId {
- return nil, httperrors.NewForbiddenError("group and guest should belong to same project")
- }
- if group.Enabled.IsFalse() {
- return nil, httperrors.NewForbiddenError("can not bind or unbind disabled instance group")
- }
- groupIdSet.Insert(group.GetId())
- }
- return groupIdSet, nil
- }
- // 公网Ip转Eip
- // 要求虚拟机有公网IP,并且虚拟机状态为running 或 ready
- // 目前仅支持阿里云和腾讯云
- func (self *SGuest) PerformPublicipToEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestPublicipToEipInput) (jsonutils.JSONObject, error) {
- publicip, err := self.GetPublicIp()
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetPublicIp"))
- }
- if publicip == nil {
- return nil, httperrors.NewInputParameterError("The guest %s does not have any public IP", self.Name)
- }
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, self.Status)
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.IsSupportPublicipToEip() {
- return nil, httperrors.NewUnsupportOperationError("The %s guest not support public ip to eip operation", self.Hypervisor)
- }
- return nil, self.StartPublicipToEipTask(ctx, userCred, input.AutoStart, "")
- }
- func (self *SGuest) StartPublicipToEipTask(ctx context.Context, userCred mcclient.TokenCredential, autoStart bool, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Set("auto_start", jsonutils.NewBool(autoStart))
- task, err := taskman.TaskManager.NewTask(ctx, "GuestPublicipToEipTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- self.SetStatus(ctx, userCred, api.VM_START_EIP_CONVERT, "")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) SetAutoRenew(autoRenew bool) error {
- _, err := db.Update(self, func() error {
- self.AutoRenew = autoRenew
- return nil
- })
- return err
- }
- // 设置自动续费
- // 要求虚拟机状态为running 或 ready
- // 要求虚拟机计费类型为包年包月(预付费)
- func (self *SGuest) PerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAutoRenewInput) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
- return nil, httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, self.Status)
- }
- if self.BillingType != billing_api.BILLING_TYPE_PREPAID {
- return nil, httperrors.NewUnsupportOperationError("Only %s guest support this operation", billing_api.BILLING_TYPE_PREPAID)
- }
- if len(input.Duration) == 0 {
- input.Duration = "1M"
- }
- _, err := billing.ParseBillingCycle(input.Duration)
- if err != nil {
- return nil, httperrors.NewInputParameterError("invalid duration %s: %s", input.Duration, err)
- }
- if self.AutoRenew == input.AutoRenew {
- return nil, nil
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if !driver.IsSupportSetAutoRenew() {
- err := self.SetAutoRenew(input.AutoRenew)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- logclient.AddSimpleActionLog(self, logclient.ACT_SET_AUTO_RENEW, jsonutils.Marshal(input), userCred, true)
- return nil, nil
- }
- return nil, self.StartSetAutoRenewTask(ctx, userCred, input, "")
- }
- func (self *SGuest) StartSetAutoRenewTask(ctx context.Context, userCred mcclient.TokenCredential, input api.GuestAutoRenewInput, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Set("auto_renew", jsonutils.NewBool(input.AutoRenew))
- data.Set("duration", jsonutils.NewString(input.Duration))
- task, err := taskman.TaskManager.NewTask(ctx, "GuestSetAutoRenewTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- self.SetStatus(ctx, userCred, api.VM_SET_AUTO_RENEW, "")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SGuest) PerformRemoteUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerRemoteUpdateInput) (jsonutils.JSONObject, error) {
- err := self.StartRemoteUpdateTask(ctx, userCred, (input.ReplaceTags != nil && *input.ReplaceTags), "")
- if err != nil {
- return nil, errors.Wrap(err, "StartRemoteUpdateTask")
- }
- return nil, nil
- }
- func (self *SGuest) PerformOpenForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- req, err := guestdriver_types.NewOpenForwardRequestFromJSON(ctx, data)
- if err != nil {
- return nil, err
- }
- for _, nicDesc := range self.fetchNICShortDesc(ctx) {
- if nicDesc.VpcId != api.DEFAULT_VPC_ID {
- if req.Addr == "" {
- req.Addr = nicDesc.IpAddr
- }
- req.NetworkId = nicDesc.NetworkId
- }
- }
- if req.NetworkId == "" {
- return nil, httperrors.NewInputParameterError("guest has no vpc ip")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver")
- }
- resp, err := driver.RequestOpenForward(ctx, userCred, self, req)
- if err != nil {
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_WEBSSH, err, userCred, false)
- return nil, httperrors.NewGeneralError(err)
- }
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_WEBSSH, resp, userCred, true)
- return resp.JSON(), nil
- }
- func (self *SGuest) PerformCloseForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- req, err := guestdriver_types.NewCloseForwardRequestFromJSON(ctx, data)
- if err != nil {
- return nil, err
- }
- for _, nicDesc := range self.fetchNICShortDesc(ctx) {
- if nicDesc.VpcId != api.DEFAULT_VPC_ID {
- req.NetworkId = nicDesc.NetworkId
- }
- }
- if req.NetworkId == "" {
- return nil, httperrors.NewInputParameterError("guest has no vpc ip")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- resp, err := driver.RequestCloseForward(ctx, userCred, self, req)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return resp.JSON(), nil
- }
- func (self *SGuest) PerformListForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- req, err := guestdriver_types.NewListForwardRequestFromJSON(ctx, data)
- if err != nil {
- return nil, err
- }
- for _, nicDesc := range self.fetchNICShortDesc(ctx) {
- if nicDesc.VpcId != api.DEFAULT_VPC_ID {
- if req.Addr == "" {
- req.Addr = nicDesc.IpAddr
- }
- req.NetworkId = nicDesc.NetworkId
- }
- }
- if req.NetworkId == "" {
- return nil, httperrors.NewInputParameterError("guest has no vpc ip")
- }
- driver, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- resp, err := driver.RequestListForward(ctx, userCred, self, req)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return resp.JSON(), nil
- }
- // 更改存储
- func (self *SGuest) PerformChangeStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeStorageInput) (*api.ServerChangeStorageInput, error) {
- // validate input
- if input.TargetStorageId == "" {
- return nil, httperrors.NewNotEmptyError("Storage id is empty")
- }
- // validate storage
- storageObj, err := StorageManager.FetchByIdOrName(ctx, userCred, input.TargetStorageId)
- if err != nil {
- return nil, errors.Wrapf(err, "Found storage by %s", input.TargetStorageId)
- }
- storage := storageObj.(*SStorage)
- input.TargetStorageId = storage.GetId()
- // validate disk
- disks, err := self.GetDisks()
- if err != nil {
- return nil, errors.Wrapf(err, "Get server %s disks", self.GetName())
- }
- var changeDisks = []string{}
- for _, disk := range disks {
- if disk.StorageId != input.TargetStorageId {
- changeDisks = append(changeDisks, disk.Id)
- }
- }
- // driver validate
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
- return nil, err
- }
- return nil, self.StartGuestChangeStorageTask(ctx, userCred, input, changeDisks)
- }
- func (self *SGuest) StartGuestChangeStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeStorageInput, disks []string) error {
- params := api.ServerChangeStorageInternalInput{
- ServerChangeStorageInput: *input,
- Disks: disks,
- DiskCount: len(disks),
- GuestRunning: self.Status == api.VM_RUNNING,
- }
- reason := fmt.Sprintf("Change guest disks storage to %s", input.TargetStorageId)
- self.SetStatus(ctx, userCred, api.VM_DISK_CHANGE_STORAGE, reason)
- if task, err := taskman.TaskManager.NewTask(
- ctx, "GuestChangeDisksStorageTask", self, userCred, jsonutils.Marshal(params).(*jsonutils.JSONDict),
- "", "", nil); err != nil {
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeDiskStorageInput) (*api.ServerChangeDiskStorageInput, error) {
- // validate input
- if input.DiskId == "" {
- return nil, httperrors.NewNotEmptyError("Disk id is empty")
- }
- if input.TargetStorageId == "" {
- return nil, httperrors.NewNotEmptyError("Storage id is empty")
- }
- // validate disk
- disks, err := self.GetDisks()
- if err != nil {
- return nil, errors.Wrapf(err, "Get server %s disks", self.GetName())
- }
- var srcDisk *SDisk
- for _, disk := range disks {
- if input.DiskId == disk.GetId() || input.DiskId == disk.GetName() {
- srcDisk = &disk
- input.DiskId = disk.GetId()
- break
- }
- }
- if srcDisk == nil {
- return nil, httperrors.NewNotFoundError("Disk %s not found on server %s", input.DiskId, self.GetName())
- }
- // validate storage
- storageObj, err := StorageManager.FetchByIdOrName(ctx, userCred, input.TargetStorageId)
- if err != nil {
- return nil, errors.Wrapf(err, "Found storage by %s", input.TargetStorageId)
- }
- storage := storageObj.(*SStorage)
- input.TargetStorageId = storage.GetId()
- // driver validate
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
- return nil, err
- }
- {
- copyInput := api.ServerCopyDiskToStorageInput{
- KeepOriginDisk: input.KeepOriginDisk,
- GuestRunning: self.Status == api.VM_RUNNING,
- }
- err := self.CopyDiskToStorage(ctx, userCred, srcDisk, storage, copyInput, "")
- if err != nil {
- return nil, errors.Wrap(err, "CopyDiskToStorage")
- }
- }
- return nil, nil
- }
- func (guest *SGuest) CopyDiskToStorage(ctx context.Context, userCred mcclient.TokenCredential, srcDisk *SDisk, storage *SStorage, input api.ServerCopyDiskToStorageInput, parentTaskId string) error {
- diskConf := &api.DiskConfig{
- Index: -1,
- ImageId: srcDisk.TemplateId,
- SizeMb: srcDisk.DiskSize,
- Fs: srcDisk.FsFormat,
- DiskType: srcDisk.DiskType,
- }
- targetDisk, err := guest.CreateDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
- if err != nil {
- return errors.Wrapf(err, "Create target disk on storage %s", storage.GetName())
- }
- {
- err := db.InheritFromTo(ctx, userCred, srcDisk, targetDisk)
- if err != nil {
- return errors.Wrapf(err, "Inherit class metadata from src %s to target %s", srcDisk.GetName(), targetDisk.GetName())
- }
- }
- internalInput := &api.ServerChangeDiskStorageInternalInput{
- ServerChangeDiskStorageInput: api.ServerChangeDiskStorageInput{
- DiskId: srcDisk.Id,
- TargetStorageId: storage.Id,
- KeepOriginDisk: input.KeepOriginDisk,
- },
- StorageId: srcDisk.StorageId,
- TargetDiskId: targetDisk.GetId(),
- GuestRunning: input.GuestRunning,
- CloneDiskCount: input.CloneDiskCount,
- CompletedDiskCount: input.CompletedDiskCount,
- }
- {
- err := guest.StartChangeDiskStorageTask(ctx, userCred, internalInput, parentTaskId)
- if err != nil {
- return errors.Wrap(err, "StartChangeDiskStorageTask")
- }
- }
- return nil
- }
- func (self *SGuest) StartChangeDiskStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeDiskStorageInternalInput, parentTaskId string) error {
- reason := fmt.Sprintf("Change disk %s to storage %s", input.DiskId, input.TargetStorageId)
- self.SetStatus(ctx, userCred, api.VM_DISK_CHANGE_STORAGE, reason)
- driver, err := self.GetDriver()
- if err != nil {
- return err
- }
- return driver.StartChangeDiskStorageTask(self, ctx, userCred, input, parentTaskId)
- }
- func (self *SGuest) startSwitchToClonedDisk(ctx context.Context, userCred mcclient.TokenCredential, taskId string) error {
- task := taskman.TaskManager.FetchTaskById(taskId)
- if task == nil {
- return errors.Errorf("no task %s found", taskId)
- }
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- log.Infof("guest %s start switch to cloned disk task", self.Id)
- params := jsonutils.NewDict()
- params.Set("block_jobs_ready", jsonutils.JSONTrue)
- return params, nil
- })
- return nil
- }
- // 设置启动顺序
- func (self *SGuest) PerformSetBootIndex(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSetBootIndexInput) (jsonutils.JSONObject, error) {
- gds, err := self.GetGuestDisks()
- if err != nil {
- return nil, err
- }
- diskBootIndexes := map[int8]int8{}
- for i := 0; i < len(gds); i++ {
- diskBootIndexes[gds[i].Index] = gds[i].BootIndex
- }
- gcs, err := self.getCdroms()
- if err != nil {
- return nil, err
- }
- cdromBootIndexes := map[int]int8{}
- for i := 0; i < len(gcs); i++ {
- cdromBootIndexes[gcs[i].Ordinal] = gcs[i].BootIndex
- }
- for sDiskIndex, bootIndex := range input.Disks {
- iDiskIndex, err := strconv.Atoi(sDiskIndex)
- if err != nil {
- return nil, httperrors.NewInputParameterError("failed parse disk index %s", sDiskIndex)
- } else if iDiskIndex > 127 {
- return nil, httperrors.NewInputParameterError("disk inex %s is exceed 127", sDiskIndex)
- }
- diskIndex := int8(iDiskIndex)
- if _, ok := diskBootIndexes[diskIndex]; !ok {
- return nil, httperrors.NewBadRequestError("disk has no index %d", diskIndex)
- }
- diskBootIndexes[diskIndex] = bootIndex
- }
- for sCdromOrdinal, bootIndex := range input.Cdroms {
- cdromOrdinal, err := strconv.Atoi(sCdromOrdinal)
- if err != nil {
- return nil, httperrors.NewInputParameterError("failed parse cdrom ordinal %s", sCdromOrdinal)
- }
- if _, ok := cdromBootIndexes[cdromOrdinal]; !ok {
- return nil, httperrors.NewBadRequestError("cdrom has no ordinal %d", cdromOrdinal)
- }
- cdromBootIndexes[cdromOrdinal] = bootIndex
- }
- bm := bitmap.NewBitMap(128)
- for diskIndex, bootIndex := range diskBootIndexes {
- if bootIndex < 0 {
- continue
- }
- if bm.Has(int64(bootIndex)) {
- return nil, httperrors.NewBadRequestError("disk index %d boot index %d is duplicated", diskIndex, bootIndex)
- } else {
- bm.Set(int64(bootIndex))
- }
- }
- for cdromOrdinal, bootIndex := range cdromBootIndexes {
- if bootIndex < 0 {
- continue
- }
- if bm.Has(int64(bootIndex)) {
- return nil, httperrors.NewBadRequestError("cdrom ordianl %d boot index %d is duplicated", cdromOrdinal, bootIndex)
- } else {
- bm.Set(int64(bootIndex))
- }
- }
- for i := 0; i < len(gds); i++ {
- if gds[i].BootIndex != diskBootIndexes[gds[i].Index] {
- if err := gds[i].SetBootIndex(diskBootIndexes[gds[i].Index]); err != nil {
- log.Errorf("gds[i].SetBootIndex: %s", err)
- return nil, err
- }
- }
- }
- for i := 0; i < len(gcs); i++ {
- if gcs[i].BootIndex != cdromBootIndexes[gcs[i].Ordinal] {
- if err := gcs[i].SetBootIndex(cdromBootIndexes[gcs[i].Ordinal]); err != nil {
- log.Errorf("gcs[i].SetBootIndex: %s", err)
- return nil, err
- }
- }
- }
- if self.Bios == api.VM_BOOT_MODE_UEFI {
- data := jsonutils.NewDict()
- data.Set("set_uefi_boot_order", jsonutils.JSONTrue)
- if err := self.startSyncTask(ctx, userCred, false, "", data); err != nil {
- return nil, err
- }
- }
- return nil, nil
- }
- // 探测透传设备
- func (self *SGuest) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "GetHost")
- }
- driver, err := host.GetHostDriver()
- if err != nil {
- return nil, errors.Wrapf(err, "GetHostDriver")
- }
- hostDevs, err := driver.RequestProbeIsolatedDevices(ctx, userCred, host, data)
- if err != nil {
- return nil, errors.Wrap(err, "RequestProbeIsolatedDevices")
- }
- objs, err := hostDevs.GetArray()
- if err != nil {
- return nil, errors.Wrapf(err, "GetArray from %q", hostDevs)
- }
- devs := make([]*SIsolatedDevice, 0)
- for _, obj := range objs {
- id, err := obj.GetString("id")
- if err != nil {
- return nil, errors.Wrapf(err, "device %s", obj)
- }
- devObj, err := IsolatedDeviceManager.FetchById(id)
- if err != nil {
- return nil, errors.Wrapf(err, "FetchById %q", id)
- }
- dev := devObj.(*SIsolatedDevice)
- if dev.GuestId == "" {
- devs = append(devs, dev)
- }
- }
- return jsonutils.Marshal(devs), nil
- }
- func (self *SGuest) PerformCpuset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ServerCPUSetInput) (jsonutils.JSONObject, error) {
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "get host model")
- }
- allCores, err := host.getHostLogicalCores()
- if err != nil {
- return nil, err
- }
- if !sets.NewInt(allCores...).HasAll(data.CPUS...) {
- return nil, httperrors.NewInputParameterError("Host cores %v not contains input %v", allCores, data.CPUS)
- }
- hostReservedCpus, err := host.GetReservedCpus()
- if err != nil {
- return nil, errors.Wrap(err, "host get reserved cpus")
- }
- for i := range data.CPUS {
- if hostReservedCpus.Contains(data.CPUS[i]) {
- return nil, httperrors.NewBadRequestError("request cpu %d has been reserved", data.CPUS[i])
- }
- }
- pinnedMap, err := host.GetPinnedCpusetCores(ctx, userCred, []string{self.Id})
- if err != nil {
- return nil, errors.Wrap(err, "Get host pinned cpu cores")
- }
- if pinnedMap != nil {
- for i := range data.CPUS {
- if pinnedMap.Contains(data.CPUS[i]) {
- return nil, httperrors.NewBadRequestError("request cpu %d has been set by other guests", data.CPUS[i])
- }
- }
- }
- if err := self.SetMetadata(ctx, api.VM_METADATA_CGROUP_CPUSET, data, userCred); err != nil {
- return nil, errors.Wrap(err, "set metadata")
- }
- if err := host.updateHostReservedCpus(ctx, userCred); err != nil {
- return nil, errors.Wrap(err, "updateHostReservedCpus")
- }
- return nil, self.StartGuestCPUSetTask(ctx, userCred, data)
- }
- func (self *SGuest) StartGuestCPUSetTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCPUSetInput) error {
- task, err := taskman.TaskManager.NewTask(ctx, "GuestCPUSetTask", self, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), "", "")
- if err != nil {
- return errors.Wrap(err, "New GuestCPUSetTask")
- }
- return task.ScheduleRun(nil)
- }
- func (self *SGuest) PerformCpusetRemove(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ServerCPUSetRemoveInput) (*api.ServerCPUSetRemoveResp, error) {
- if err := self.RemoveMetadata(ctx, api.VM_METADATA_CGROUP_CPUSET, userCred); err != nil {
- return nil, errors.Wrapf(err, "remove metadata %q", api.VM_METADATA_CGROUP_CPUSET)
- }
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "get host model")
- }
- // TODO: maybe change to async task
- db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE, nil, userCred)
- resp := new(api.ServerCPUSetRemoveResp)
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if err := drv.RequestCPUSetRemove(ctx, userCred, host, self, data); err != nil {
- db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE_FAIL, err, userCred)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CPUSET_REMOVE, data, userCred, false)
- resp.Error = err.Error()
- } else {
- db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE, nil, userCred)
- logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CPUSET_REMOVE, data, userCred, true)
- resp.Done = true
- }
- return resp, nil
- }
- func (self *SGuest) getPinnedCpusetCores(ctx context.Context, userCred mcclient.TokenCredential) ([]int, error) {
- obj := self.GetMetadataJson(ctx, api.VM_METADATA_CGROUP_CPUSET, userCred)
- if obj == nil {
- return nil, nil
- }
- pinnedInput := new(api.ServerCPUSetInput)
- if err := obj.Unmarshal(pinnedInput); err != nil {
- return nil, errors.Wrap(err, "Unmarshal to ServerCPUSetInput")
- }
- return pinnedInput.CPUS, nil
- }
- func (self *SGuest) GetDetailsCpusetCores(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerGetCPUSetCoresInput) (*api.ServerGetCPUSetCoresResp, error) {
- host, err := self.GetHost()
- if err != nil {
- return nil, err
- }
- allCores, err := host.getHostLogicalCores()
- if err != nil {
- return nil, err
- }
- usedMap, err := host.GetPinnedCpusetCores(ctx, userCred, nil)
- if err != nil {
- return nil, err
- }
- resp := &api.ServerGetCPUSetCoresResp{
- HostCores: allCores,
- }
- if usedMap != nil {
- resp.HostUsedCores = usedMap.ToSlice()
- }
- // fetch cpuset pinned
- pinned, err := self.getPinnedCpusetCores(ctx, userCred)
- if err != nil {
- log.Errorf("getPinnedCpusetCores error: %v", err)
- } else {
- resp.PinnedCores = pinned
- }
- return resp, nil
- }
- func (self *SGuest) GetDetailsNumaInfo(ctx context.Context, userCred mcclient.TokenCredential, _ *api.ServerGetNumaInfoInput) (*api.ServerGetNumaInfoResp, error) {
- ret := new(api.ServerGetNumaInfoResp)
- devs, _ := self.GetIsolatedDevices()
- if len(devs) > 0 {
- ret.IsolatedDevicesNumaNode = make([]int8, 0)
- for i := range devs {
- if devs[i].NumaNode > 0 {
- ret.IsolatedDevicesNumaNode = append(ret.IsolatedDevicesNumaNode, devs[i].NumaNode)
- }
- }
- }
- ret.CpuNumaPin = self.CpuNumaPin
- return ret, nil
- }
- func (self *SGuest) GetDetailsHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ *api.ServerGetHardwareInfoInput) (*api.ServerGetHardwareInfoResp, error) {
- host, err := self.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "GetHost")
- }
- hostSpecObj := host.GetSpec(false)
- hostSpec := new(api.HostSpec)
- if hostSpecObj != nil {
- if err := hostSpecObj.Unmarshal(hostSpec); err != nil {
- return nil, errors.Wrap(err, "unmarshal host spec")
- }
- }
- motherboardInfo := &api.ServerHardwareInfoMotherboard{}
- if host.SysInfo != nil && host.SysInfo.Contains("motherboard_info") {
- if err := host.SysInfo.Unmarshal(motherboardInfo, "motherboard_info"); err != nil {
- return nil, errors.Wrapf(err, "unmarshal motherboard_info from host system info")
- }
- }
- cpuInfo := &api.ServerHardwareInfoCPU{
- Model: host.CpuDesc,
- Count: self.VcpuCount,
- }
- memInfo := &api.ServerHardwareInfoMemory{
- SizeMB: self.VmemSize,
- }
- // fill disks info
- diskInfos := make([]*api.ServerHardwareInfoDisk, 0)
- disks, err := self.GetDisks()
- if err != nil {
- return nil, errors.Wrap(err, "get disks")
- }
- for _, disk := range disks {
- storage, err := disk.GetStorage()
- if err != nil {
- return nil, errors.Wrapf(err, "get disk %s storage", disk.GetId())
- }
- hdInfo, err := storage.GetDetailsHardwareInfo(ctx, userCred, nil)
- if err != nil {
- return nil, errors.Wrapf(err, "get storage %s hardware info", storage.GetId())
- }
- info := &api.ServerHardwareInfoDisk{
- Id: disk.GetId(),
- StorageId: storage.GetId(),
- SizeMB: disk.DiskSize,
- Bandwidth: hdInfo.Bandwidth,
- }
- if hdInfo.Vendor != nil {
- info.Model = *hdInfo.Vendor
- }
- if hdInfo.Model != nil {
- info.Model = fmt.Sprintf("%s %s", info.Model, *hdInfo.Model)
- }
- diskInfos = append(diskInfos, info)
- }
- // fill GPU info
- devs, err := self.GetIsolatedDevices()
- if err != nil {
- return nil, errors.Wrap(err, "get isolated devices")
- }
- gpuInfos := make([]*api.ServerHardwareInfoGPU, 0)
- for _, dev := range devs {
- if !dev.IsGPU() {
- continue
- }
- info := &api.ServerHardwareInfoGPU{
- Id: dev.GetId(),
- Model: dev.Model,
- }
- if dev.PcieInfo != nil {
- info.PCIEInfo = dev.PcieInfo
- }
- modelObj, _ := IsolatedDeviceModelManager.GetByDevModel(dev.Model)
- if modelObj != nil {
- hdInfo, err := modelObj.GetDetailsHardwareInfo(ctx, userCred, nil)
- if err != nil {
- return nil, errors.Wrapf(err, "get device model %s hardware info", modelObj.GetId())
- }
- info.IsolatedDeviceModelHardwareInfo = hdInfo
- }
- gpuInfos = append(gpuInfos, info)
- }
- result := &api.ServerGetHardwareInfoResp{
- Motherboard: motherboardInfo,
- CPU: cpuInfo,
- Memory: memInfo,
- Disks: diskInfos,
- GPUs: gpuInfos,
- }
- return result, nil
- }
- func (self *SGuest) PerformCalculateRecordChecksum(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- checksum, err := db.CalculateModelChecksum(self)
- if err != nil {
- return nil, errors.Wrap(err, "CalculateModelChecksum")
- }
- return jsonutils.Marshal(map[string]string{
- "checksum": checksum,
- }), nil
- }
- func (self *SGuest) PerformEnableMemclean(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred)
- }
- func (self *SGuest) PerformSetTpm(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- enableTpm := jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_TPM, false)
- if enableTpm {
- return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_TPM, enableTpm, userCred)
- } else {
- return nil, self.RemoveMetadata(ctx, api.VM_METADATA_ENABLE_TPM, userCred)
- }
- }
- // 设置操作系统信息
- func (self *SGuest) PerformSetOsInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetOSInfoInput) (jsonutils.JSONObject, error) {
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if err := drv.ValidateSetOSInfo(ctx, userCred, self, &input); err != nil {
- return nil, err
- }
- if input.Type != "" {
- if _, err := db.Update(self, func() error {
- self.OsType = input.Type
- return nil
- }); err != nil {
- return nil, errors.Wrapf(err, "update os_type")
- }
- }
- for k, v := range map[string]string{
- api.VM_METADATA_OS_NAME: input.Type,
- api.VM_METADATA_OS_VERSION: input.Version,
- api.VM_METADATA_OS_DISTRO: input.Distribution,
- api.VM_METADATA_OS_ARCH: input.Arch,
- } {
- if len(v) == 0 {
- continue
- }
- if err := self.SetMetadata(ctx, k, v, userCred); err != nil {
- return nil, errors.Wrapf(err, "set metadata %s to %s", k, v)
- }
- }
- return nil, nil
- }
- // 同步操作系统信息
- func (self *SGuest) PerformSyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- drv, err := self.GetDriver()
- if err != nil {
- return nil, err
- }
- if err := drv.ValidateSyncOSInfo(ctx, userCred, self); err != nil {
- return nil, err
- }
- if self.Status == api.VM_READY {
- // start guest deploy task to sync os info
- return nil, self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
- } else {
- res, err := self.PerformQgaPing(ctx, userCred, nil, nil)
- if err != nil || res.Contains("ping_error") {
- return nil, httperrors.NewBadRequestError("qga ping failed is qga running?")
- }
- // try qga get os info
- return nil, self.startQgaSyncOsInfoTask(ctx, userCred, "")
- }
- }
- func (g *SGuest) PerformSetRootDiskMatcher(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, data *api.BaremetalRootDiskMatcher) (jsonutils.JSONObject, error) {
- if g.GetHypervisor() != api.HYPERVISOR_BAREMETAL {
- return nil, httperrors.NewNotAcceptableError("only %s support for setting root disk matcher", api.HYPERVISOR_BAREMETAL)
- }
- if err := baremetal.ValidateRootDiskMatcher(data); err != nil {
- return nil, err
- }
- if err := g.SetMetadata(ctx, api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER, jsonutils.Marshal(data), userCred); err != nil {
- return nil, errors.Wrapf(err, "set %s", api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
- }
- return nil, nil
- }
- func (g *SGuest) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerChangeBillingTypeInput) (jsonutils.JSONObject, error) {
- if !utils.IsInStringArray(g.Status, []string{api.VM_RUNNING, api.VM_READY}) {
- return nil, httperrors.NewServerStatusError("Cannot change guest billing type in status %s", g.Status)
- }
- if len(input.BillingType) == 0 {
- return nil, httperrors.NewMissingParameterError("billing_type")
- }
- if !utils.IsInStringArray(string(input.BillingType), []string{string(billing_api.BILLING_TYPE_POSTPAID), string(billing_api.BILLING_TYPE_PREPAID)}) {
- return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType)
- }
- if g.BillingType == input.BillingType {
- return nil, nil
- }
- return nil, g.StartChangeBillingTypeTask(ctx, userCred, "")
- }
- func (self *SGuest) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- self.SetStatus(ctx, userCred, apis.STATUS_CHANGE_BILLING_TYPE, "")
- kwargs := jsonutils.NewDict()
- task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeBillingTypeTask", self, userCred, kwargs, parentTaskId, "", nil)
- if err != nil {
- return err
- }
- return task.ScheduleRun(nil)
- }
- func (self *SGuest) PerformDisableAutoMergeSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- disableAutoMergeSnapshot := jsonutils.QueryBoolean(data, "disable_auto_merge_snapshot", false)
- return nil, self.SetMetadata(ctx, api.VM_METADATA_DISABLE_AUTO_MERGE_SNAPSHOT, disableAutoMergeSnapshot, userCred)
- }
- func (self *SGuest) PerformSetKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.KickstartConfig) (jsonutils.JSONObject, error) {
- // Check if kickstart has already been completed
- currentFlag := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
- if currentFlag == "true" {
- return jsonutils.Marshal(map[string]string{
- "message": "Kickstart has already been completed for this VM",
- "status": "failed",
- }), nil
- }
- if err := self.SetKickstartConfig(ctx, &input, userCred); err != nil {
- return nil, errors.Wrap(err, "set kickstart config")
- }
- // Determine and set kickstart type based on input
- kickstartType := determineKickstartType(&input)
- if err := self.SetKickstartType(ctx, kickstartType, userCred); err != nil {
- return nil, errors.Wrap(err, "set kickstart type")
- }
- if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, "0", userCred); err != nil {
- return nil, errors.Wrap(err, "set kickstart attempt")
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, "set kickstart config", userCred)
- // Check if VM needs to be restarted
- needRestart := false
- if utils.IsInStringArray(self.Status, []string{
- api.VM_RUNNING,
- api.VM_KICKSTART_INSTALLING,
- api.VM_KICKSTART_COMPLETED,
- api.VM_KICKSTART_FAILED,
- api.VM_BLOCK_STREAM,
- api.VM_MIGRATING,
- }) {
- needRestart = true
- }
- if err := self.SetKickstartStatus(ctx, api.VM_KICKSTART_PENDING, userCred); err != nil {
- return nil, errors.Wrap(err, "set kickstart status")
- }
- // If VM is running, restart it to apply kickstart config
- if needRestart {
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrap(err, "get driver for restart")
- }
- if err := driver.StartGuestRestartTask(self, ctx, userCred, false, "kickstart config updated"); err != nil {
- return nil, errors.Wrap(err, "start restart task")
- }
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- "message": "kickstart config updated, restarting VM",
- }), nil
- }
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- }), nil
- }
- func (self *SGuest) PerformKickstartComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- // Check if kickstart_completed_flag is already set to true
- currentFlag := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
- if currentFlag == "true" {
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- "message": "kickstart is already marked as completed",
- }), nil
- }
- // Set the kickstart_completed_flag metadata to true
- if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, "true", userCred); err != nil {
- return nil, errors.Wrap(err, "set kickstart completed flag")
- }
- // Update status to VM_KICKSTART_COMPLETED if in kickstart status
- if self.IsInKickstartStatus() && self.Status != api.VM_KICKSTART_COMPLETED {
- if err := self.SetStatus(ctx, userCred, api.VM_KICKSTART_COMPLETED, "kickstart manually marked as completed"); err != nil {
- return nil, errors.Wrap(err, "update kickstart status")
- }
- }
- // Check if restart is requested (default is true)
- restart := jsonutils.QueryBoolean(input, "restart", true)
- if restart {
- // Trigger VM restart to complete the kickstart process
- if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_KICKSTART_INSTALLING, api.VM_KICKSTART_COMPLETED, api.VM_KICKSTART_FAILED}) {
- driver, err := self.GetDriver()
- if err != nil {
- return nil, errors.Wrap(err, "get driver")
- }
- driver.StartGuestRestartTask(self, ctx, userCred, false, "")
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- "message": "kickstart marked as completed, restarting VM",
- }), nil
- }
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, "kickstart manually marked as completed", userCred)
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- "message": "kickstart marked as completed",
- }), nil
- }
- func (self *SGuest) PerformDeleteKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Status == api.VM_KICKSTART_INSTALLING {
- return nil, httperrors.NewInvalidStatusError("cannot delete kickstart config while installation is in progress")
- }
- if err := self.SetKickstartConfig(ctx, nil, userCred); err != nil {
- return nil, errors.Wrap(err, "delete kickstart config")
- }
- // 如果当前是kickstart状态,需要转换回适当的状态
- if self.IsInKickstartStatus() {
- if self.Status == api.VM_KICKSTART_PENDING {
- self.SetStatus(ctx, userCred, api.VM_READY, "kickstart config deleted")
- } else if self.Status == api.VM_KICKSTART_COMPLETED {
- self.SetStatus(ctx, userCred, api.VM_RUNNING, "kickstart config deleted")
- } else if self.Status == api.VM_KICKSTART_FAILED {
- self.SetStatus(ctx, userCred, api.VM_READY, "kickstart config deleted")
- }
- }
- // 清理kickstart相关metadata
- self.RemoveMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, userCred)
- self.RemoveMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
- db.OpsLog.LogEvent(self, db.ACT_DELETE, "delete kickstart config", userCred)
- return jsonutils.Marshal(map[string]string{
- "status": "success",
- }), nil
- }
- func (self *SGuest) PerformUpdateKickstartStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerUpdateKickstartStatusInput) (jsonutils.JSONObject, error) {
- if err := self.SetKickstartStatus(ctx, input.Status, userCred); err != nil {
- return nil, errors.Wrap(err, "update kickstart status")
- }
- if input.Status == api.VM_KICKSTART_COMPLETED {
- if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, "true", userCred); err != nil {
- log.Errorf("Failed to set kickstart completed flag for VM %s: %v", self.Name, err)
- }
- }
- if input.ErrorMessage != "" {
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, fmt.Sprintf("kickstart status: %s, error: %s", input.Status, input.ErrorMessage), userCred)
- } else {
- db.OpsLog.LogEvent(self, db.ACT_UPDATE, fmt.Sprintf("kickstart status: %s", input.Status), userCred)
- }
- return jsonutils.Marshal(map[string]string{
- "status": input.Status,
- }), nil
- }
|