guest_actions.go 251 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package models
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "net"
  20. "net/http"
  21. "strconv"
  22. "strings"
  23. "time"
  24. "unicode"
  25. "yunion.io/x/cloudmux/pkg/cloudprovider"
  26. "yunion.io/x/jsonutils"
  27. "yunion.io/x/log"
  28. "yunion.io/x/pkg/errors"
  29. "yunion.io/x/pkg/gotypes"
  30. "yunion.io/x/pkg/tristate"
  31. "yunion.io/x/pkg/util/billing"
  32. "yunion.io/x/pkg/util/httputils"
  33. "yunion.io/x/pkg/util/osprofile"
  34. "yunion.io/x/pkg/util/rand"
  35. "yunion.io/x/pkg/util/regutils"
  36. "yunion.io/x/pkg/util/sets"
  37. "yunion.io/x/pkg/utils"
  38. "yunion.io/x/sqlchemy"
  39. "yunion.io/x/onecloud/pkg/apis"
  40. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  41. api "yunion.io/x/onecloud/pkg/apis/compute"
  42. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  43. noapi "yunion.io/x/onecloud/pkg/apis/notify"
  44. schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
  45. "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
  46. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  47. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  48. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  49. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  50. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  51. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  52. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  53. "yunion.io/x/onecloud/pkg/cloudcommon/userdata"
  54. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  55. "yunion.io/x/onecloud/pkg/compute/baremetal"
  56. guestdriver_types "yunion.io/x/onecloud/pkg/compute/guestdrivers/types"
  57. "yunion.io/x/onecloud/pkg/compute/options"
  58. "yunion.io/x/onecloud/pkg/httperrors"
  59. "yunion.io/x/onecloud/pkg/mcclient"
  60. "yunion.io/x/onecloud/pkg/mcclient/auth"
  61. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  62. "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
  63. "yunion.io/x/onecloud/pkg/mcclient/modules/scheduler"
  64. "yunion.io/x/onecloud/pkg/util/bitmap"
  65. "yunion.io/x/onecloud/pkg/util/logclient"
  66. "yunion.io/x/onecloud/pkg/util/netutils2"
  67. "yunion.io/x/onecloud/pkg/util/seclib2"
  68. )
  69. // 获取VNC信息
  70. func (self *SGuest) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  71. ret := &cloudprovider.ServerVncOutput{}
  72. if self.PowerStates == api.VM_POWER_STATES_ON ||
  73. utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_MIGRATING}) {
  74. host, err := self.GetHost()
  75. if err != nil {
  76. return nil, httperrors.NewInternalServerError("get host %v", err)
  77. }
  78. if options.Options.ForceUseOriginVnc {
  79. input.Origin = true
  80. }
  81. driver, err := self.GetDriver()
  82. if err != nil {
  83. return nil, errors.Wrapf(err, "GetDriver")
  84. }
  85. ret, err = driver.GetGuestVncInfo(ctx, userCred, self, host, input)
  86. if err != nil {
  87. return nil, err
  88. }
  89. ret.Id = self.Id
  90. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CONSOLE, ret, userCred, true)
  91. return ret, nil
  92. }
  93. return ret, nil
  94. }
  95. // 获取公有云可变更配置
  96. // 目前支持: 阿里云,腾讯云,华为云
  97. func (guest *SGuest) GetDetailsModificationTypes(ctx context.Context, userCred mcclient.TokenCredential, input jsonutils.JSONObject) (*api.ServerModificationTypesOutput, error) {
  98. if len(guest.ExternalId) == 0 {
  99. return nil, httperrors.NewNotFoundError("guest external id is empty")
  100. }
  101. vm, err := guest.GetIVM(ctx)
  102. if err != nil {
  103. return nil, errors.Wrapf(err, "GetIVM")
  104. }
  105. ret := &api.ServerModificationTypesOutput{}
  106. ret.ModificationTypes = make([]api.ServerModificationType, 0)
  107. modificationTypes, err := vm.GetModificationTypes()
  108. if err != nil {
  109. return nil, errors.Wrapf(err, "GetModificationTypes")
  110. }
  111. for _, modificationType := range modificationTypes {
  112. ret.ModificationTypes = append(ret.ModificationTypes, api.ServerModificationType{Name: modificationType.InstanceType})
  113. }
  114. return ret, nil
  115. }
  116. func (self *SGuest) PreCheckPerformAction(
  117. ctx context.Context, userCred mcclient.TokenCredential,
  118. action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
  119. ) error {
  120. if err := self.SVirtualResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
  121. return err
  122. }
  123. if self.Hypervisor == api.HYPERVISOR_KVM {
  124. host, _ := self.GetHost()
  125. if host != nil && (host.HostStatus == api.HOST_OFFLINE || !host.Enabled.Bool()) &&
  126. utils.IsInStringArray(action,
  127. []string{
  128. "start", "restart", "stop", "reset", "rebuild-root",
  129. "change-config", "instance-snapshot", "snapshot-and-clone",
  130. "attach-isolated-device", "detach-isolated-deivce",
  131. "insert-iso", "eject-iso", "deploy", "create-backup",
  132. }) {
  133. return httperrors.NewInvalidStatusError(
  134. "host status %s and enabled %v, can't do server %s", host.HostStatus, host.Enabled.Bool(), action)
  135. }
  136. }
  137. return nil
  138. }
  139. // 远程执行QMP命令
  140. func (self *SGuest) PerformMonitor(
  141. ctx context.Context,
  142. userCred mcclient.TokenCredential,
  143. query jsonutils.JSONObject,
  144. input *api.ServerMonitorInput,
  145. ) (jsonutils.JSONObject, error) {
  146. if self.PowerStates == api.VM_POWER_STATES_ON ||
  147. utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM, api.VM_MIGRATING}) {
  148. if input.COMMAND == "" {
  149. return nil, httperrors.NewMissingParameterError("command")
  150. }
  151. return self.SendMonitorCommand(ctx, userCred, input)
  152. }
  153. return nil, httperrors.NewInvalidStatusError("Cannot send command in status %s", self.Status)
  154. }
  155. // +onecloud:swagger-gen-ignore
  156. func (self *SGuest) PerformEvent(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  157. data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  158. event, err := data.GetString("event")
  159. if err != nil {
  160. return nil, httperrors.NewMissingParameterError("event")
  161. }
  162. if event == "GUEST_PANICKED" {
  163. kwargs := jsonutils.NewDict()
  164. kwargs.Set("reason", jsonutils.NewString(event))
  165. if data.Contains("screen_dump_info") {
  166. screenDumpInfo := api.SGuestScreenDumpInfo{}
  167. if err := data.Unmarshal(&screenDumpInfo, "screen_dump_info"); err != nil {
  168. log.Errorf("failed unmarshal screen_dump_info %s", err)
  169. } else {
  170. kwargs.Set("screen_dump_name", jsonutils.NewString(screenDumpInfo.S3ObjectName))
  171. if _, err := self.SaveGuestScreenDump(ctx, userCred, &screenDumpInfo); err != nil {
  172. log.Errorf("SaveGuestScreenDump failed %s", err)
  173. }
  174. }
  175. }
  176. db.OpsLog.LogEvent(self, db.ACT_GUEST_PANICKED, kwargs.String(), userCred)
  177. logclient.AddSimpleActionLog(self, logclient.ACT_GUEST_PANICKED, kwargs.String(), userCred, true)
  178. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  179. Obj: self,
  180. Action: notifyclient.ActionServerPanicked,
  181. IsFail: true,
  182. })
  183. }
  184. return nil, nil
  185. }
  186. func (self *SGuest) GetDetailsDesc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  187. host, err := self.GetHost()
  188. if err != nil {
  189. return nil, errors.Wrapf(err, "GetHost")
  190. }
  191. driver, err := self.GetDriver()
  192. if err != nil {
  193. return nil, err
  194. }
  195. return driver.GetJsonDescAtHost(ctx, userCred, self, host, nil)
  196. }
  197. // 保存镜像
  198. func (self *SGuest) PerformSaveImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSaveImageInput) (api.ServerSaveImageInput, error) {
  199. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  200. return input, httperrors.NewInputParameterError("Cannot save image in status %s", self.Status)
  201. }
  202. driver, err := self.GetDriver()
  203. if err != nil {
  204. return input, errors.Wrapf(err, "GetDriver")
  205. }
  206. input.Restart = ((self.Status == api.VM_RUNNING) || input.AutoStart) && !driver.IsAllowSaveImageOnRunning()
  207. if len(input.Name) == 0 && len(input.GenerateName) == 0 {
  208. return input, httperrors.NewInputParameterError("Image name is required")
  209. }
  210. disks := self.CategorizeDisks()
  211. if disks.Root == nil {
  212. return input, httperrors.NewInputParameterError("No root image")
  213. }
  214. input.OsType = self.OsType
  215. if len(input.OsType) == 0 {
  216. input.OsType = "Linux"
  217. }
  218. input.OsArch = self.OsArch
  219. if apis.IsARM(self.OsArch) || apis.IsRISCV(self.OsArch) {
  220. if osArch := self.GetMetadata(ctx, "os_arch", nil); len(osArch) == 0 {
  221. host, _ := self.GetHost()
  222. input.OsArch = host.CpuArchitecture
  223. }
  224. }
  225. factory, _ := cloudprovider.GetProviderFactory(driver.GetProvider())
  226. if factory == nil || factory.IsOnPremise() { // OneCloud or VMware
  227. lockman.LockObject(ctx, disks.Root)
  228. defer lockman.ReleaseObject(ctx, disks.Root)
  229. var err error
  230. input.ImageId, err = disks.Root.PrepareSaveImage(ctx, userCred, input)
  231. if err != nil {
  232. return input, errors.Wrapf(err, "PrepareSaveImage")
  233. }
  234. }
  235. if len(input.Name) == 0 {
  236. input.Name = input.GenerateName
  237. }
  238. logclient.AddSimpleActionLog(self, logclient.ACT_SAVE_IMAGE, input, userCred, true)
  239. return input, self.StartGuestSaveImage(ctx, userCred, input, "")
  240. }
  241. func (self *SGuest) StartGuestSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSaveImageInput, parentTaskId string) error {
  242. driver, err := self.GetDriver()
  243. if err != nil {
  244. return errors.Wrapf(err, "GetDriver")
  245. }
  246. return driver.StartGuestSaveImage(ctx, userCred, self, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId)
  247. }
  248. // 保存主机模板
  249. func (self *SGuest) PerformSaveGuestImage(ctx context.Context, userCred mcclient.TokenCredential,
  250. query jsonutils.JSONObject, input api.ServerSaveGuestImageInput) (jsonutils.JSONObject, error) {
  251. if !utils.IsInStringArray(self.Status, []string{api.VM_READY}) {
  252. return nil, httperrors.NewBadRequestError("Cannot save image in status %s", self.Status)
  253. }
  254. if len(input.Name) == 0 && len(input.GenerateName) == 0 {
  255. return nil, httperrors.NewMissingParameterError("Image name is required")
  256. }
  257. if self.Hypervisor != api.HYPERVISOR_KVM {
  258. return nil, httperrors.NewBadRequestError("Support only by KVM Hypervisor")
  259. }
  260. disks := self.CategorizeDisks()
  261. if disks.Root == nil {
  262. return nil, httperrors.NewInternalServerError("No root image")
  263. }
  264. if len(self.EncryptKeyId) > 0 && (input.EncryptKeyId == nil || len(*input.EncryptKeyId) == 0) {
  265. // server encrypted, so image must be encrypted
  266. input.EncryptKeyId = &self.EncryptKeyId
  267. } else if len(self.EncryptKeyId) > 0 && input.EncryptKeyId != nil && len(*input.EncryptKeyId) > 0 && self.EncryptKeyId != *input.EncryptKeyId {
  268. return nil, errors.Wrap(httperrors.ErrConflict, "input encrypt key not match with server encrypt key")
  269. }
  270. diskList := append(disks.Data, disks.Root)
  271. kwargs := imageapi.GuestImageCreateInput{}
  272. kwargs.GuestImageCreateInputBase = input.GuestImageCreateInputBase
  273. kwargs.Properties = make(map[string]string)
  274. if len(kwargs.ProjectId) == 0 {
  275. kwargs.ProjectId = self.ProjectId
  276. }
  277. for _, disk := range diskList {
  278. kwargs.Images = append(kwargs.Images, imageapi.GuestImageCreateInputSubimage{
  279. DiskFormat: disk.DiskFormat,
  280. VirtualSize: disk.DiskSize,
  281. })
  282. }
  283. if len(input.Notes) > 0 {
  284. kwargs.Properties["notes"] = input.Notes
  285. }
  286. osType := self.OsType
  287. if len(osType) == 0 {
  288. osType = "Linux"
  289. }
  290. kwargs.Properties["os_type"] = osType
  291. if apis.IsARM(self.OsArch) || apis.IsRISCV(self.OsArch) {
  292. var osArch string
  293. if osArch = self.GetMetadata(ctx, "os_arch", nil); len(osArch) == 0 {
  294. host, _ := self.GetHost()
  295. osArch = host.CpuArchitecture
  296. }
  297. kwargs.Properties["os_arch"] = osArch
  298. kwargs.OsArch = self.OsArch
  299. }
  300. s := auth.GetSession(ctx, userCred, consts.GetRegion())
  301. ret, err := image.GuestImages.Create(s, jsonutils.Marshal(kwargs))
  302. if err != nil {
  303. return nil, err
  304. }
  305. guestImageId, _ := ret.GetString("id")
  306. // set class metadata
  307. cm, err := self.GetAllClassMetadata()
  308. if err != nil {
  309. return nil, errors.Wrap(err, "unable to GetAllClassMetadata")
  310. }
  311. if len(cm) > 0 {
  312. _, err = image.GuestImages.PerformAction(s, guestImageId, "set-class-metadata", jsonutils.Marshal(cm))
  313. if err != nil {
  314. return nil, errors.Wrapf(err, "unable to SetClassMetadata for guest image %s", guestImageId)
  315. }
  316. }
  317. guestImageInfo := struct {
  318. RootImage imageapi.SubImageInfo
  319. DataImages []imageapi.SubImageInfo
  320. }{}
  321. ret.Unmarshal(&guestImageInfo)
  322. if len(guestImageInfo.DataImages) != len(disks.Data) {
  323. return nil, fmt.Errorf("create subimage of guest image error")
  324. }
  325. imageIds := make([]string, 0, len(guestImageInfo.DataImages)+1)
  326. for _, info := range guestImageInfo.DataImages {
  327. imageIds = append(imageIds, info.ID)
  328. }
  329. imageIds = append(imageIds, guestImageInfo.RootImage.ID)
  330. taskParams := jsonutils.NewDict()
  331. if input.AutoStart != nil && *input.AutoStart {
  332. taskParams.Add(jsonutils.JSONTrue, "auto_start")
  333. }
  334. taskParams.Add(jsonutils.Marshal(imageIds), "image_ids")
  335. log.Infof("before StartGuestSaveGuestImage image_ids: %s", imageIds)
  336. return nil, self.StartGuestSaveGuestImage(ctx, userCred, taskParams, "")
  337. }
  338. func (self *SGuest) StartGuestSaveGuestImage(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
  339. driver, err := self.GetDriver()
  340. if err != nil {
  341. return err
  342. }
  343. return driver.StartGuestSaveGuestImage(ctx, userCred, self, data, parentTaskId)
  344. }
  345. // 同步配置
  346. func (self *SGuest) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  347. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  348. return nil, httperrors.NewResourceBusyError("Cannot sync in status %s", self.Status)
  349. }
  350. if err := self.StartSyncTask(ctx, userCred, false, ""); err != nil {
  351. return nil, err
  352. }
  353. return nil, nil
  354. }
  355. func (self *SGuest) GetQemuVersion(userCred mcclient.TokenCredential) string {
  356. return self.GetMetadata(context.Background(), "__qemu_version", userCred)
  357. }
  358. func (self *SGuest) GetQemuCmdline(userCred mcclient.TokenCredential) string {
  359. return self.GetMetadata(context.Background(), "__qemu_cmdline", userCred)
  360. }
  361. func (self *SGuest) GetDetailsQemuInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*api.ServerQemuInfo, error) {
  362. version := self.GetQemuVersion(userCred)
  363. cmdline := self.GetQemuCmdline(userCred)
  364. return &api.ServerQemuInfo{
  365. Version: version,
  366. Cmdline: cmdline,
  367. }, nil
  368. }
  369. // if qemuVer >= compareVer return true
  370. func (self *SGuest) CheckQemuVersion(qemuVer, compareVer string) bool {
  371. if len(qemuVer) == 0 {
  372. return false
  373. }
  374. compareVersion := strings.Split(compareVer, ".")
  375. guestVersion := strings.Split(qemuVer, ".")
  376. var i = 0
  377. for ; i < len(guestVersion); i++ {
  378. if i >= len(compareVersion) {
  379. return true
  380. }
  381. v, _ := strconv.ParseInt(guestVersion[i], 10, 0)
  382. compareV, _ := strconv.ParseInt(compareVersion[i], 10, 0)
  383. if v < compareV {
  384. return false
  385. } else if v > compareV {
  386. return true
  387. }
  388. }
  389. if i < len(compareVersion)-1 {
  390. return false
  391. }
  392. return true
  393. }
  394. func (self *SGuest) validateMigrate(
  395. ctx context.Context,
  396. userCred mcclient.TokenCredential,
  397. migrateInput *api.GuestMigrateInput,
  398. liveMigrateInput *api.GuestLiveMigrateInput,
  399. ) error {
  400. isLiveMigrate := false
  401. if liveMigrateInput != nil {
  402. isLiveMigrate = true
  403. }
  404. driver, err := self.GetDriver()
  405. if err != nil {
  406. return errors.Wrapf(err, "GetDriver")
  407. }
  408. if isLiveMigrate {
  409. // do live migrate check
  410. if !driver.IsSupportLiveMigrate() {
  411. return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
  412. }
  413. if err := driver.CheckLiveMigrate(ctx, self, userCred, *liveMigrateInput); err != nil {
  414. return err
  415. }
  416. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_SUSPEND}) {
  417. if len(liveMigrateInput.PreferHostId) > 0 {
  418. iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, liveMigrateInput.PreferHostId)
  419. if iHost == nil {
  420. return httperrors.NewBadRequestError("Host %s not found", liveMigrateInput.PreferHostId)
  421. }
  422. host := iHost.(*SHost)
  423. liveMigrateInput.PreferHostId = host.Id
  424. }
  425. return nil
  426. }
  427. return httperrors.NewBadRequestError("Cannot live migrate in status %s", self.Status)
  428. } else {
  429. // do migrate check
  430. if !driver.IsSupportMigrate() {
  431. return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
  432. }
  433. if !migrateInput.IsRescueMode && self.Status != api.VM_READY {
  434. return httperrors.NewServerStatusError("Cannot normal migrate guest in status %s, try rescue mode or server-live-migrate?", self.Status)
  435. }
  436. if err := driver.CheckMigrate(ctx, self, userCred, *migrateInput); err != nil {
  437. return err
  438. }
  439. if len(migrateInput.PreferHostId) > 0 {
  440. iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, migrateInput.PreferHostId)
  441. if iHost == nil {
  442. return httperrors.NewBadRequestError("Host %s not found", migrateInput.PreferHostId)
  443. }
  444. host := iHost.(*SHost)
  445. migrateInput.PreferHostId = host.Id
  446. }
  447. return nil
  448. }
  449. }
  450. func (self *SGuest) validateConvertToKvm(
  451. ctx context.Context,
  452. userCred mcclient.TokenCredential,
  453. migrateInput *api.GuestMigrateInput,
  454. ) error {
  455. if len(migrateInput.PreferHostId) > 0 {
  456. iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, migrateInput.PreferHostId)
  457. if iHost == nil {
  458. return httperrors.NewBadRequestError("Host %s not found", migrateInput.PreferHostId)
  459. }
  460. host := iHost.(*SHost)
  461. migrateInput.PreferHostId = host.Id
  462. }
  463. if self.Status != api.VM_READY {
  464. return httperrors.NewServerStatusError("can't convert guest in status %s", self.Status)
  465. }
  466. return nil
  467. }
  468. // 迁移调度
  469. func (self *SGuest) PerformMigrateForecast(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerMigrateForecastInput) (jsonutils.JSONObject, error) {
  470. var (
  471. mInput *api.GuestMigrateInput = nil
  472. lmInput *api.GuestLiveMigrateInput = nil
  473. )
  474. if input.ConvertToKvm {
  475. mInput = &api.GuestMigrateInput{PreferHostId: input.PreferHostId}
  476. if err := self.validateConvertToKvm(ctx, userCred, mInput); err != nil {
  477. return nil, err
  478. }
  479. input.PreferHostId = mInput.PreferHostId
  480. } else {
  481. if input.LiveMigrate {
  482. lmInput = &api.GuestLiveMigrateInput{
  483. PreferHostId: input.PreferHostId,
  484. SkipCpuCheck: &input.SkipCpuCheck,
  485. }
  486. if err := self.validateMigrate(ctx, userCred, nil, lmInput); err != nil {
  487. return nil, err
  488. }
  489. input.PreferHostId = lmInput.PreferHostId
  490. } else {
  491. mInput = &api.GuestMigrateInput{
  492. PreferHostId: input.PreferHostId,
  493. IsRescueMode: input.IsRescueMode,
  494. }
  495. if err := self.validateMigrate(ctx, userCred, mInput, nil); err != nil {
  496. return nil, err
  497. }
  498. input.PreferHostId = mInput.PreferHostId
  499. }
  500. }
  501. schedParams := self.GetSchedMigrateParams(userCred, input)
  502. if input.ConvertToKvm {
  503. schedParams.Hypervisor = api.HYPERVISOR_KVM
  504. }
  505. s := auth.GetAdminSession(ctx, options.Options.Region)
  506. _, res, err := scheduler.SchedManager.DoScheduleForecast(s, schedParams, 1)
  507. if err != nil {
  508. return nil, errors.Wrap(err, "Do schedule migrate forecast")
  509. }
  510. return res, nil
  511. }
  512. func (self *SGuest) GetSchedMigrateParams(
  513. userCred mcclient.TokenCredential,
  514. input *api.ServerMigrateForecastInput,
  515. ) *schedapi.ScheduleInput {
  516. schedDesc := self.ToSchedDesc()
  517. if input.PreferHostId != "" {
  518. schedDesc.ServerConfig.PreferHost = input.PreferHostId
  519. }
  520. schedDesc.ResetCpuNumaPin = input.ResetCpuNumaPin
  521. if input.LiveMigrate {
  522. schedDesc.LiveMigrate = input.LiveMigrate
  523. if self.GetMetadata(context.Background(), "__cpu_mode", userCred) != api.CPU_MODE_QEMU {
  524. host, _ := self.GetHost()
  525. schedDesc.CpuDesc = host.CpuDesc
  526. schedDesc.CpuMicrocode = host.CpuMicrocode
  527. schedDesc.CpuMode = api.CPU_MODE_HOST
  528. } else {
  529. schedDesc.CpuMode = api.CPU_MODE_QEMU
  530. }
  531. schedDesc.SkipCpuCheck = &input.SkipCpuCheck
  532. host, _ := self.GetHost()
  533. if host != nil {
  534. schedDesc.TargetHostKernel, _ = host.SysInfo.GetString("kernel_version")
  535. schedDesc.SkipKernelCheck = &input.SkipKernelCheck
  536. schedDesc.HostMemPageSizeKB = host.PageSizeKB
  537. }
  538. if self.IsSchedulerNumaAllocate() {
  539. cpuNumaPin := make([]schedapi.SCpuNumaPin, 0)
  540. self.CpuNumaPin.Unmarshal(&cpuNumaPin)
  541. schedDesc.CpuNumaPin = cpuNumaPin
  542. }
  543. }
  544. schedDesc.ReuseNetwork = true
  545. return schedDesc
  546. }
  547. // 冷迁移虚拟机
  548. func (self *SGuest) PerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestMigrateInput) (jsonutils.JSONObject, error) {
  549. if err := self.validateMigrate(ctx, userCred, input, nil); err != nil {
  550. return nil, err
  551. }
  552. return nil, self.StartMigrateTask(ctx, userCred, input.IsRescueMode, input.AutoStart, self.Status, input.PreferHostId, "")
  553. }
  554. func (self *SGuest) StartMigrateTask(
  555. ctx context.Context, userCred mcclient.TokenCredential,
  556. isRescueMode, autoStart bool, guestStatus, preferHostId, parentTaskId string,
  557. ) error {
  558. vmStatus := api.VM_START_MIGRATE
  559. data := jsonutils.NewDict()
  560. if isRescueMode {
  561. data.Set("is_rescue_mode", jsonutils.JSONTrue)
  562. }
  563. if len(preferHostId) > 0 {
  564. data.Set("prefer_host_id", jsonutils.NewString(preferHostId))
  565. }
  566. if autoStart {
  567. data.Set("auto_start", jsonutils.JSONTrue)
  568. }
  569. if self.HostId == preferHostId {
  570. vmStatus = api.VM_STARTING
  571. data.Set("reset_cpu_numa_pin", jsonutils.JSONTrue)
  572. }
  573. data.Set("guest_status", jsonutils.NewString(guestStatus))
  574. dedicateMigrateTask := "GuestMigrateTask"
  575. if len(self.ExternalId) > 0 {
  576. dedicateMigrateTask = "ManagedGuestMigrateTask" //托管私有云
  577. }
  578. self.SetStatus(ctx, userCred, vmStatus, "")
  579. if task, err := taskman.TaskManager.NewTask(ctx, dedicateMigrateTask, self, userCred, data, parentTaskId, "", nil); err != nil {
  580. log.Errorln(err)
  581. return err
  582. } else {
  583. task.ScheduleRun(nil)
  584. }
  585. return nil
  586. }
  587. // 在线迁移虚拟机
  588. func (self *SGuest) PerformLiveMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestLiveMigrateInput) (jsonutils.JSONObject, error) {
  589. if err := self.validateMigrate(ctx, userCred, nil, input); err != nil {
  590. return nil, err
  591. }
  592. if input.EnableTLS == nil {
  593. input.EnableTLS = &options.Options.EnableTlsMigration
  594. }
  595. return nil, self.StartGuestLiveMigrateTask(ctx, userCred,
  596. self.Status, input.PreferHostId, input.SkipCpuCheck,
  597. input.SkipKernelCheck, input.EnableTLS, input.QuicklyFinish, input.MaxBandwidthMb, input.KeepDestGuestOnFailed, "",
  598. )
  599. }
  600. func (self *SGuest) StartGuestLiveMigrateTask(
  601. ctx context.Context, userCred mcclient.TokenCredential,
  602. guestStatus, preferHostId string,
  603. skipCpuCheck, skipKernelCheck, enableTLS, quicklyFinish *bool,
  604. maxBandwidthMb *int64, keepDestGuestOnFailed *bool, parentTaskId string,
  605. ) error {
  606. self.SetStatus(ctx, userCred, api.VM_START_MIGRATE, "")
  607. data := jsonutils.NewDict()
  608. if len(preferHostId) > 0 {
  609. data.Set("prefer_host_id", jsonutils.NewString(preferHostId))
  610. }
  611. if skipCpuCheck != nil {
  612. data.Set("skip_cpu_check", jsonutils.NewBool(*skipCpuCheck))
  613. }
  614. if skipKernelCheck != nil {
  615. data.Set("skip_kernel_check", jsonutils.NewBool(*skipKernelCheck))
  616. }
  617. if enableTLS != nil {
  618. data.Set("enable_tls", jsonutils.NewBool(*enableTLS))
  619. }
  620. if quicklyFinish != nil {
  621. data.Set("quickly_finish", jsonutils.NewBool(*quicklyFinish))
  622. }
  623. if maxBandwidthMb != nil {
  624. data.Set("max_bandwidth_mb", jsonutils.NewInt(*maxBandwidthMb))
  625. }
  626. if keepDestGuestOnFailed != nil {
  627. data.Set("keep_dest_guest_on_failed", jsonutils.NewBool(*keepDestGuestOnFailed))
  628. }
  629. data.Set("guest_status", jsonutils.NewString(guestStatus))
  630. dedicateMigrateTask := "GuestLiveMigrateTask"
  631. if len(self.ExternalId) > 0 {
  632. dedicateMigrateTask = "ManagedGuestLiveMigrateTask" //托管私有云
  633. }
  634. if task, err := taskman.TaskManager.NewTask(ctx, dedicateMigrateTask, self, userCred, data, parentTaskId, "", nil); err != nil {
  635. log.Errorln(err)
  636. return err
  637. } else {
  638. task.ScheduleRun(nil)
  639. }
  640. return nil
  641. }
  642. // +onecloud:swagger-gen-ignore
  643. func (self *SGuest) PerformSetLiveMigrateParams(
  644. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetLiveMigrateParamsInput,
  645. ) (jsonutils.JSONObject, error) {
  646. if self.Status != api.VM_LIVE_MIGRATING {
  647. return nil, httperrors.NewServerStatusError("cannot set migrate params in status %s", self.Status)
  648. }
  649. if input.MaxBandwidthMB == nil && input.DowntimeLimitMS == nil {
  650. return nil, httperrors.NewInputParameterError("empty input")
  651. }
  652. arguments := map[string]interface{}{}
  653. if input.MaxBandwidthMB != nil {
  654. arguments["max-bandwidth"] = *input.MaxBandwidthMB * 1024 * 1024
  655. }
  656. if input.DowntimeLimitMS != nil {
  657. arguments["downtime-limit"] = *input.DowntimeLimitMS
  658. }
  659. cmd := map[string]interface{}{
  660. "execute": "migrate-set-parameters",
  661. "arguments": arguments,
  662. }
  663. log.Infof("set live migrate params input: %s", jsonutils.Marshal(cmd).String())
  664. monitorInput := &api.ServerMonitorInput{
  665. COMMAND: jsonutils.Marshal(cmd).String(),
  666. QMP: true,
  667. }
  668. return self.SendMonitorCommand(ctx, userCred, monitorInput)
  669. }
  670. // 取消在线迁移
  671. func (self *SGuest) PerformCancelLiveMigrate(
  672. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
  673. ) (jsonutils.JSONObject, error) {
  674. if self.Status != api.VM_LIVE_MIGRATING {
  675. return nil, httperrors.NewServerStatusError("cannot set migrate params in status %s", self.Status)
  676. }
  677. driver, err := self.GetDriver()
  678. if err != nil {
  679. return nil, errors.Wrapf(err, "GetDriver")
  680. }
  681. return nil, driver.RequestCancelLiveMigrate(ctx, self, userCred)
  682. }
  683. // 克隆虚拟机
  684. func (self *SGuest) PerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  685. if self.IsEncrypted() {
  686. return nil, httperrors.NewForbiddenError("cannot clone encrypted server")
  687. }
  688. if len(self.BackupHostId) > 0 {
  689. return nil, httperrors.NewBadRequestError("Can't clone guest with backup guest")
  690. }
  691. driver, err := self.GetDriver()
  692. if err != nil {
  693. return nil, err
  694. }
  695. if !driver.IsSupportGuestClone() {
  696. return nil, httperrors.NewBadRequestError("Guest hypervisor %s does not support clone", self.Hypervisor)
  697. }
  698. if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  699. return nil, httperrors.NewInvalidStatusError("Cannot clone VM in status %s", self.Status)
  700. }
  701. cloneInput, err := cmdline.FetchServerCreateInputByJSON(data)
  702. if err != nil {
  703. return nil, httperrors.NewInputParameterError("Unmarshal input error %s", err)
  704. }
  705. if len(cloneInput.Name) == 0 {
  706. return nil, httperrors.NewMissingParameterError("name")
  707. }
  708. err = db.NewNameValidator(ctx, GuestManager, userCred, cloneInput.Name, nil)
  709. if err != nil {
  710. return nil, err
  711. }
  712. createInput := self.ToCreateInput(ctx, userCred)
  713. createInput.Name = cloneInput.Name
  714. createInput.AutoStart = cloneInput.AutoStart
  715. createInput.EipBw = cloneInput.EipBw
  716. createInput.Eip = cloneInput.Eip
  717. createInput.EipChargeType = cloneInput.EipChargeType
  718. if err := GuestManager.validateEip(ctx, userCred, createInput, createInput.PreferRegion, createInput.PreferManager); err != nil {
  719. return nil, err
  720. }
  721. dataDict := createInput.JSON(createInput)
  722. // ownerId := db.SOwnerId{DomainId: createInput.Domain, ProjectId: createInput.Project}
  723. model, err := db.DoCreate(GuestManager, ctx, userCred, query, dataDict, userCred)
  724. if err != nil {
  725. return nil, httperrors.NewGeneralError(err)
  726. }
  727. func() {
  728. lockman.LockObject(ctx, model)
  729. defer lockman.ReleaseObject(ctx, model)
  730. model.PostCreate(ctx, userCred, userCred, query, dataDict)
  731. }()
  732. db.OpsLog.LogEvent(model, db.ACT_CREATE, model.GetShortDesc(ctx), userCred)
  733. logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CREATE, "", userCred, true)
  734. pendingUsage, pendingRegionUsage := getGuestResourceRequirements(ctx, userCred, *createInput, userCred, 1, false)
  735. task, err := taskman.TaskManager.NewTask(ctx,
  736. "GuestCloneTask",
  737. model.(db.IStandaloneModel),
  738. userCred,
  739. dataDict,
  740. "", "",
  741. &pendingUsage, &pendingRegionUsage)
  742. if err != nil {
  743. log.Errorln(err)
  744. return nil, err
  745. }
  746. task.ScheduleRun(nil)
  747. return nil, nil
  748. }
  749. // 重置密码
  750. func (self *SGuest) PerformSetPassword(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetPasswordInput) (jsonutils.JSONObject, error) {
  751. if self.Hypervisor == api.HYPERVISOR_KVM && self.Status == api.VM_RUNNING {
  752. inputQga := &api.ServerQgaSetPasswordInput{
  753. Username: input.Username,
  754. Password: input.Password,
  755. }
  756. if inputQga.Username == "" {
  757. if self.OsType == osprofile.OS_TYPE_WINDOWS {
  758. inputQga.Username = api.VM_DEFAULT_WINDOWS_LOGIN_USER
  759. } else {
  760. inputQga.Username = api.VM_DEFAULT_LINUX_LOGIN_USER
  761. }
  762. }
  763. if inputQga.Password == "" && input.ResetPassword {
  764. inputQga.Password = seclib2.RandomPassword2(12)
  765. }
  766. return self.PerformQgaSetPassword(ctx, userCred, query, inputQga)
  767. } else {
  768. inputDeploy := api.ServerDeployInput{}
  769. inputDeploy.AutoStart = input.AutoStart
  770. inputDeploy.Password = input.Password
  771. inputDeploy.ResetPassword = input.ResetPassword
  772. if input.Username != "" {
  773. inputDeploy.LoginAccount = input.Username
  774. }
  775. return self.PerformDeploy(ctx, userCred, query, inputDeploy)
  776. }
  777. }
  778. func (self *SGuest) GetDetailsCreateParams(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  779. input := self.ToCreateInput(ctx, userCred)
  780. return input.JSON(input), nil
  781. }
  782. func (self *SGuest) saveOldPassword(ctx context.Context, userCred mcclient.TokenCredential) {
  783. loginKey := self.GetMetadata(ctx, api.VM_METADATA_LOGIN_KEY, userCred)
  784. if len(loginKey) > 0 {
  785. password, err := utils.DescryptAESBase64(self.Id, loginKey)
  786. if err == nil && len(password) <= 30 {
  787. for _, r := range password {
  788. if !unicode.IsPrint(r) {
  789. return
  790. }
  791. }
  792. self.SetMetadata(ctx, api.VM_METADATA_LAST_LOGIN_KEY, loginKey, userCred)
  793. }
  794. }
  795. }
  796. func (self *SGuest) GetOldPassword(ctx context.Context, userCred mcclient.TokenCredential) string {
  797. loginSecret := self.GetMetadata(ctx, api.VM_METADATA_LAST_LOGIN_KEY, userCred)
  798. password, _ := utils.DescryptAESBase64(self.Id, loginSecret)
  799. return password
  800. }
  801. // 重置密码或秘钥信息&探测虚拟机镜像
  802. func (self *SGuest) PerformDeploy(
  803. ctx context.Context,
  804. userCred mcclient.TokenCredential,
  805. query jsonutils.JSONObject,
  806. input api.ServerDeployInput,
  807. ) (jsonutils.JSONObject, error) {
  808. self.saveOldPassword(ctx, userCred)
  809. if input.DeleteKeypair || len(input.KeypairId) > 0 {
  810. if len(input.KeypairId) > 0 {
  811. _, err := validators.ValidateModel(ctx, userCred, KeypairManager, &input.KeypairId)
  812. if err != nil {
  813. return nil, err
  814. }
  815. }
  816. if self.KeypairId != input.KeypairId {
  817. okey := self.getKeypair()
  818. if okey != nil {
  819. input.DeletePublicKey = okey.PublicKey
  820. }
  821. diff, err := db.Update(self, func() error {
  822. self.KeypairId = input.KeypairId
  823. return nil
  824. })
  825. if err != nil {
  826. return nil, httperrors.NewInternalServerError("update keypairId %v", err)
  827. }
  828. db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
  829. input.ResetPassword = true
  830. }
  831. }
  832. if len(input.Password) > 0 {
  833. err := seclib2.ValidatePassword(input.Password)
  834. if err != nil {
  835. return nil, err
  836. }
  837. input.ResetPassword = true
  838. }
  839. driver, err := self.GetDriver()
  840. if err != nil {
  841. return nil, errors.Wrapf(err, "GetDriver")
  842. }
  843. // 变更密码/密钥时需要Restart才能生效。更新普通字段不需要Restart, Azure需要在运行状态下操作
  844. doRestart := false
  845. if input.ResetPassword {
  846. doRestart = driver.IsNeedRestartForResetLoginInfo()
  847. if len(input.LoginAccount) > 0 {
  848. if len(input.LoginAccount) > 32 {
  849. return nil, httperrors.NewInputParameterError("login_account is longer than 32 chars")
  850. }
  851. if err := GuestManager.ValidateNameLoginAccount(input.LoginAccount); err != nil {
  852. return nil, err
  853. }
  854. }
  855. }
  856. deployStatus, err := driver.GetDeployStatus()
  857. if err != nil {
  858. return nil, httperrors.NewInputParameterError("%v", err)
  859. }
  860. if utils.IsInStringArray(self.Status, deployStatus) {
  861. if (doRestart && self.Status == api.VM_RUNNING) || (self.Status != api.VM_RUNNING && (input.AutoStart || input.Restart)) {
  862. input.Restart = true
  863. } else {
  864. // 避免前端直接传restart参数, 越过校验
  865. input.Restart = false
  866. }
  867. err := self.StartGuestDeployTask(ctx, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), "deploy", "")
  868. if err != nil {
  869. return nil, err
  870. }
  871. return nil, nil
  872. }
  873. return nil, httperrors.NewServerStatusError("Cannot deploy in status %s", self.Status)
  874. }
  875. func (self *SGuest) ValidateAttachDisk(ctx context.Context, disk *SDisk) error {
  876. storage, _ := disk.GetStorage()
  877. host, _ := self.GetHost()
  878. if provider := storage.GetCloudprovider(); provider != nil {
  879. if provider.Id != host.ManagerId {
  880. return httperrors.NewInputParameterError("Disk %s and guest not belong to the same account", disk.Name)
  881. }
  882. if storage.ZoneId != host.ZoneId {
  883. return httperrors.NewInputParameterError("Disk %s and guest not belong to the same zone", disk.Name)
  884. }
  885. }
  886. if disk.IsLocal() {
  887. attached, err := disk.isAttached()
  888. if err != nil {
  889. return httperrors.NewInternalServerError("isAttached check failed %s", err)
  890. }
  891. if attached {
  892. return httperrors.NewInputParameterError("Disk %s has been attached", disk.Name)
  893. }
  894. }
  895. if len(disk.GetPathAtHost(host)) == 0 {
  896. return httperrors.NewInputParameterError("Disk %s not belong the guest's host", disk.Name)
  897. }
  898. if disk.Status != api.DISK_READY {
  899. return httperrors.NewInputParameterError("Disk in %s not able to attach", disk.Status)
  900. }
  901. driver, err := self.GetDriver()
  902. if err != nil {
  903. return errors.Wrapf(err, "GetDriver")
  904. }
  905. guestStatus, err := driver.GetAttachDiskStatus()
  906. if err != nil {
  907. return err
  908. }
  909. if !utils.IsInStringArray(self.Status, guestStatus) {
  910. return httperrors.NewInputParameterError("Guest %s not support attach disk in status %s", self.Name, self.Status)
  911. }
  912. ok, err := self.IsInSameClass(ctx, &disk.SStandaloneAnonResourceBase)
  913. if err != nil {
  914. return err
  915. }
  916. if self.EncryptKeyId != disk.EncryptKeyId {
  917. return errors.Wrapf(httperrors.ErrConflict, "conflict encryption key between server and disk")
  918. }
  919. if !ok {
  920. return httperrors.NewForbiddenError("the class metadata of guest and disk is different")
  921. }
  922. return nil
  923. }
  924. // 绑定磁盘
  925. func (self *SGuest) PerformAttachdisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerAttachDiskInput) (jsonutils.JSONObject, error) {
  926. if len(input.DiskId) == 0 {
  927. return nil, httperrors.NewMissingParameterError("disk_id")
  928. }
  929. if input.BootIndex != nil {
  930. if isDup, err := self.isBootIndexDuplicated(*input.BootIndex); err != nil {
  931. return nil, err
  932. } else if isDup {
  933. return nil, httperrors.NewInputParameterError("boot index %d is duplicated", *input.BootIndex)
  934. }
  935. }
  936. diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
  937. if err != nil {
  938. return nil, err
  939. }
  940. if err := self.ValidateAttachDisk(ctx, diskObj.(*SDisk)); err != nil {
  941. return nil, err
  942. }
  943. taskData := jsonutils.NewDict()
  944. taskData.Add(jsonutils.NewString(input.DiskId), "disk_id")
  945. if input.BootIndex != nil && *input.BootIndex >= 0 {
  946. taskData.Add(jsonutils.NewInt(int64(*input.BootIndex)), "boot_index")
  947. }
  948. driver, err := self.GetDriver()
  949. if err != nil {
  950. return nil, errors.Wrapf(err, "GetDriver")
  951. }
  952. self.SetStatus(ctx, userCred, api.VM_ATTACH_DISK, "")
  953. return nil, driver.StartGuestAttachDiskTask(ctx, userCred, self, taskData, "")
  954. }
  955. func (self *SGuest) StartRestartNetworkTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, ip string, inBlockStream bool) error {
  956. data := jsonutils.NewDict()
  957. data.Set("ip", jsonutils.NewString(ip))
  958. data.Set("in_block_stream", jsonutils.NewBool(inBlockStream))
  959. if task, err := taskman.TaskManager.NewTask(ctx, "GuestRestartNetworkTask", self, userCred, data, parentTaskId, "", nil); err != nil {
  960. log.Errorln(err)
  961. return err
  962. } else {
  963. task.ScheduleRun(nil)
  964. }
  965. return nil
  966. }
  967. func (self *SGuest) StartQgaRestartNetworkTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId, device, ipMask, gateway, ip6Mask, gateway6 string) error {
  968. data := jsonutils.NewDict()
  969. data.Set("device", jsonutils.NewString(device))
  970. if len(ipMask) > 0 {
  971. data.Set("ip_mask", jsonutils.NewString(ipMask))
  972. data.Set("gateway", jsonutils.NewString(gateway))
  973. }
  974. if len(ip6Mask) > 0 {
  975. data.Set("ip6_mask", jsonutils.NewString(ip6Mask))
  976. data.Set("gateway6", jsonutils.NewString(gateway6))
  977. }
  978. if task, err := taskman.TaskManager.NewTask(ctx, "GuestQgaRestartNetworkTask", self, userCred, data, parentTaskId, "", nil); err != nil {
  979. log.Errorln(err)
  980. return err
  981. } else {
  982. task.ScheduleRun(nil)
  983. }
  984. return nil
  985. }
  986. func (self *SGuest) startSyncTask(ctx context.Context, userCred mcclient.TokenCredential, firewallOnly bool, parentTaskId string, data *jsonutils.JSONDict) error {
  987. if firewallOnly {
  988. data.Add(jsonutils.JSONTrue, "fw_only")
  989. } else if err := self.SetStatus(ctx, userCred, api.VM_SYNC_CONFIG, ""); err != nil {
  990. log.Errorln(err)
  991. return err
  992. }
  993. return self.doSyncTask(ctx, data, userCred, parentTaskId)
  994. }
  995. func (self *SGuest) StartSyncTask(ctx context.Context, userCred mcclient.TokenCredential, firewallOnly bool, parentTaskId string) error {
  996. return self.startSyncTask(ctx, userCred, firewallOnly, parentTaskId, jsonutils.NewDict())
  997. }
  998. func (self *SGuest) StartSyncTaskWithoutSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, fwOnly bool, parentTaskId string) error {
  999. data := jsonutils.NewDict()
  1000. data.Set("without_sync_status", jsonutils.JSONTrue)
  1001. data.Set("fw_only", jsonutils.NewBool(fwOnly))
  1002. return self.doSyncTask(ctx, data, userCred, parentTaskId)
  1003. }
  1004. func (self *SGuest) doSyncTask(ctx context.Context, data *jsonutils.JSONDict, userCred mcclient.TokenCredential, parentTaskId string) error {
  1005. if task, err := taskman.TaskManager.NewTask(ctx, "GuestSyncConfTask", self, userCred, data, parentTaskId, "", nil); err != nil {
  1006. return err
  1007. } else {
  1008. task.ScheduleRun(nil)
  1009. }
  1010. return nil
  1011. }
  1012. // 挂起虚拟机
  1013. func (self *SGuest) PerformSuspend(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1014. if self.Status == api.VM_RUNNING {
  1015. err := self.StartSuspendTask(ctx, userCred, "")
  1016. return nil, err
  1017. }
  1018. return nil, httperrors.NewInvalidStatusError("Cannot suspend VM in status %s", self.Status)
  1019. }
  1020. func (self *SGuest) StartSuspendTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1021. err := self.SetStatus(ctx, userCred, api.VM_START_SUSPEND, "do suspend")
  1022. if err != nil {
  1023. return err
  1024. }
  1025. driver, err := self.GetDriver()
  1026. if err != nil {
  1027. return errors.Wrapf(err, "GetDriver")
  1028. }
  1029. return driver.StartSuspendTask(ctx, userCred, self, nil, parentTaskId)
  1030. }
  1031. // 恢复虚拟机挂起状态
  1032. func (self *SGuest) PerformResume(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResumeInput) (jsonutils.JSONObject, error) {
  1033. if self.Status == api.VM_SUSPEND {
  1034. err := self.StartResumeTask(ctx, userCred, "")
  1035. return nil, err
  1036. }
  1037. return nil, httperrors.NewInvalidStatusError("Cannot resume VM in status %s", self.Status)
  1038. }
  1039. func (self *SGuest) StartResumeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1040. err := self.SetStatus(ctx, userCred, api.VM_RESUMING, "do resume")
  1041. if err != nil {
  1042. return err
  1043. }
  1044. driver, err := self.GetDriver()
  1045. if err != nil {
  1046. return errors.Wrapf(err, "GetDriver")
  1047. }
  1048. return driver.StartResumeTask(ctx, userCred, self, nil, parentTaskId)
  1049. }
  1050. func (self *SGuest) PerformRestoreVirtualIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1051. config := self.GetMetadataJson(ctx, api.VM_METADATA_VIRTUAL_ISOLATED_DEVICE_CONFIG, userCred)
  1052. if config == nil {
  1053. return nil, nil
  1054. }
  1055. devConfigs := make([]api.IsolatedDeviceConfig, 0)
  1056. err := config.Unmarshal(&devConfigs)
  1057. if err != nil {
  1058. return nil, errors.Wrap(err, "unmarshal virtual dev configs")
  1059. }
  1060. devs, err := self.GetIsolatedDevices()
  1061. if err != nil {
  1062. return nil, errors.Wrap(err, "GetIsolatedDevices")
  1063. }
  1064. devCount := map[string]int{}
  1065. for i := range devConfigs {
  1066. key := devConfigs[i].DevType + "-" + devConfigs[i].Model
  1067. if cnt, ok := devCount[key]; ok {
  1068. devCount[key] = cnt + 1
  1069. } else {
  1070. devCount[key] = 1
  1071. }
  1072. }
  1073. for i := range devs {
  1074. key := devConfigs[i].DevType + "-" + devConfigs[i].Model
  1075. if cnt, ok := devCount[key]; ok {
  1076. devCount[key] = cnt - 1
  1077. }
  1078. }
  1079. host, err := self.GetHost()
  1080. if err != nil {
  1081. return nil, errors.Wrap(err, "get host")
  1082. }
  1083. lockman.LockObject(ctx, host)
  1084. defer lockman.ReleaseObject(ctx, host)
  1085. usedDeviceMap := map[string]*SIsolatedDevice{}
  1086. for key, cnt := range devCount {
  1087. if cnt <= 0 {
  1088. continue
  1089. }
  1090. segs := strings.SplitN(key, "-", 2)
  1091. devConfig := &api.IsolatedDeviceConfig{
  1092. Model: segs[1],
  1093. DevType: segs[0],
  1094. }
  1095. err := IsolatedDeviceManager.attachHostDeviceToGuestByModel(ctx, self, host, devConfig, userCred, usedDeviceMap, nil)
  1096. if err != nil {
  1097. return nil, errors.Wrap(err, "attachHostDeviceToGuestByModel")
  1098. }
  1099. }
  1100. return nil, nil
  1101. }
  1102. // 开机
  1103. func (self *SGuest) PerformStart(
  1104. ctx context.Context,
  1105. userCred mcclient.TokenCredential,
  1106. query jsonutils.JSONObject,
  1107. input api.GuestPerformStartInput,
  1108. ) (jsonutils.JSONObject, error) {
  1109. 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}
  1110. if utils.IsInStringArray(self.Status, validStartStatuses) {
  1111. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  1112. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  1113. }
  1114. if !self.guestDisksStorageTypeIsShared() {
  1115. host, _ := self.GetHost()
  1116. guestStats, err := host.GetNotReadyGuestsStat()
  1117. if err != nil {
  1118. return nil, err
  1119. }
  1120. if float32(guestStats.GuestVcpuCount+self.VcpuCount) > host.GetVirtualCPUCount() {
  1121. log.Debugf("GuestPerformStart: guestStats: %s host: %f request: %d", jsonutils.Marshal(guestStats), host.GetVirtualCPUCount(), self.VcpuCount)
  1122. return nil, httperrors.NewInsufficientResourceError("host virtual cpu not enough")
  1123. }
  1124. if float32(guestStats.GuestVmemSize+self.VmemSize) > host.GetVirtualMemorySize() {
  1125. log.Debugf("GuestPerformStart: guestStats: %s host: %f request: %d", jsonutils.Marshal(guestStats), host.GetVirtualMemorySize(), self.VmemSize)
  1126. return nil, httperrors.NewInsufficientResourceError("host virtual memory not enough")
  1127. }
  1128. }
  1129. if self.isAllDisksReady() {
  1130. kwargs := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  1131. driver, err := self.GetDriver()
  1132. if err != nil {
  1133. return nil, errors.Wrapf(err, "GetDriver")
  1134. }
  1135. err = driver.PerformStart(ctx, userCred, self, kwargs, "")
  1136. return nil, err
  1137. } else {
  1138. return nil, httperrors.NewInvalidStatusError("Some disk not ready")
  1139. }
  1140. } else {
  1141. return nil, httperrors.NewInvalidStatusError("Cannot do start server in status %s", self.Status)
  1142. }
  1143. }
  1144. func (self *SGuest) StartGuestDeployTask(
  1145. ctx context.Context, userCred mcclient.TokenCredential,
  1146. kwargs *jsonutils.JSONDict, action string, parentTaskId string,
  1147. ) error {
  1148. self.SetStatus(ctx, userCred, api.VM_START_DEPLOY, "")
  1149. if kwargs == nil {
  1150. kwargs = jsonutils.NewDict()
  1151. }
  1152. kwargs.Add(jsonutils.NewString(action), "deploy_action")
  1153. taskName := "GuestDeployTask"
  1154. if self.BackupHostId != "" {
  1155. taskName = "HAGuestDeployTask"
  1156. }
  1157. task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, kwargs, parentTaskId, "", nil)
  1158. if err != nil {
  1159. return err
  1160. }
  1161. task.ScheduleRun(nil)
  1162. return nil
  1163. }
  1164. func (self *SGuest) EventNotify(ctx context.Context, userCred mcclient.TokenCredential, action noapi.SAction) {
  1165. detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
  1166. if action != notifyclient.ActionCreate && action != notifyclient.ActionRebuildRoot && action != notifyclient.ActionResetPassword {
  1167. return
  1168. }
  1169. meta, err := self.GetAllMetadata(ctx, userCred)
  1170. if err != nil {
  1171. return
  1172. }
  1173. details.Add(jsonutils.NewString(self.getNotifyIps()), "ips")
  1174. osName := meta[api.VM_METADATA_OS_NAME]
  1175. if osName == "Windows" {
  1176. details.Add(jsonutils.JSONTrue, "windows")
  1177. }
  1178. loginAccount := meta[api.VM_METADATA_LOGIN_ACCOUNT]
  1179. if len(loginAccount) > 0 {
  1180. details.Add(jsonutils.NewString(loginAccount), "login_account")
  1181. }
  1182. keypair := self.getKeypairName()
  1183. if len(keypair) > 0 {
  1184. details.Add(jsonutils.NewString(keypair), "keypair")
  1185. } else {
  1186. loginKey := meta[api.VM_METADATA_LOGIN_KEY]
  1187. if len(loginKey) > 0 {
  1188. passwd, err := utils.DescryptAESBase64(self.Id, loginKey)
  1189. if err == nil {
  1190. details.Add(jsonutils.NewString(passwd), "password")
  1191. }
  1192. }
  1193. }
  1194. }
  1195. var resourceType string
  1196. if self.Hypervisor == api.HYPERVISOR_BAREMETAL {
  1197. resourceType = noapi.TOPIC_RESOURCE_BAREMETAL
  1198. }
  1199. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  1200. Obj: self,
  1201. ResourceType: resourceType,
  1202. Action: action,
  1203. ObjDetailsDecorator: detailsDecro,
  1204. AdvanceDays: 0,
  1205. })
  1206. }
  1207. func (self *SGuest) NotifyServerEvent(
  1208. ctx context.Context, userCred mcclient.TokenCredential, event string, priority notify.TNotifyPriority,
  1209. loginInfo bool, kwargs *jsonutils.JSONDict, notifyAdmin bool,
  1210. ) {
  1211. meta, err := self.GetAllMetadata(ctx, userCred)
  1212. if err != nil {
  1213. return
  1214. }
  1215. if kwargs == nil {
  1216. kwargs = jsonutils.NewDict()
  1217. }
  1218. kwargs.Add(jsonutils.NewString(self.Name), "name")
  1219. kwargs.Add(jsonutils.NewString(self.Hypervisor), "hypervisor")
  1220. host, _ := self.GetHost()
  1221. if host != nil {
  1222. brand := host.GetBrand()
  1223. if brand == api.CLOUD_PROVIDER_ONECLOUD {
  1224. brand = api.ComputeI18nTable.Lookup(ctx, brand)
  1225. }
  1226. kwargs.Add(jsonutils.NewString(brand), "brand")
  1227. }
  1228. if loginInfo {
  1229. kwargs.Add(jsonutils.NewString(self.getNotifyIps()), "ips")
  1230. osName := meta[api.VM_METADATA_OS_NAME]
  1231. if osName == "Windows" {
  1232. kwargs.Add(jsonutils.JSONTrue, "windows")
  1233. }
  1234. loginAccount := meta[api.VM_METADATA_LOGIN_ACCOUNT]
  1235. if len(loginAccount) > 0 {
  1236. kwargs.Add(jsonutils.NewString(loginAccount), "account")
  1237. }
  1238. keypair := self.getKeypairName()
  1239. if len(keypair) > 0 {
  1240. kwargs.Add(jsonutils.NewString(keypair), "keypair")
  1241. } else {
  1242. loginKey := meta[api.VM_METADATA_LOGIN_KEY]
  1243. if len(loginKey) > 0 {
  1244. passwd, err := utils.DescryptAESBase64(self.Id, loginKey)
  1245. if err == nil {
  1246. kwargs.Add(jsonutils.NewString(passwd), "password")
  1247. }
  1248. }
  1249. }
  1250. }
  1251. notifyclient.NotifyWithCtx(ctx, []string{userCred.GetUserId()}, false, priority, event, kwargs)
  1252. if notifyAdmin {
  1253. notifyclient.SystemNotifyWithCtx(ctx, priority, event, kwargs)
  1254. }
  1255. }
  1256. func (self *SGuest) NotifyAdminServerEvent(ctx context.Context, event string, priority notify.TNotifyPriority) {
  1257. kwargs := jsonutils.NewDict()
  1258. kwargs.Add(jsonutils.NewString(self.Name), "name")
  1259. kwargs.Add(jsonutils.NewString(self.Hypervisor), "hypervisor")
  1260. tc, _ := self.GetTenantCache(ctx)
  1261. if tc != nil {
  1262. kwargs.Add(jsonutils.NewString(tc.Name), "tenant")
  1263. } else {
  1264. kwargs.Add(jsonutils.NewString(self.ProjectId), "tenant")
  1265. }
  1266. notifyclient.SystemNotifyWithCtx(ctx, priority, event, kwargs)
  1267. }
  1268. func (self *SGuest) StartGuestStopTask(ctx context.Context, userCred mcclient.TokenCredential, timeoutSecs int, isForce, stopCharging bool, parentTaskId string) error {
  1269. if len(parentTaskId) == 0 {
  1270. self.SetStatus(ctx, userCred, api.VM_START_STOP, "")
  1271. }
  1272. params := jsonutils.NewDict()
  1273. if isForce {
  1274. params.Add(jsonutils.NewBool(isForce), "is_force")
  1275. } else {
  1276. params.Add(jsonutils.NewInt(int64(timeoutSecs)), "timeout")
  1277. }
  1278. params.Add(jsonutils.NewBool(stopCharging), "stop_charging")
  1279. if len(parentTaskId) > 0 {
  1280. params.Add(jsonutils.JSONTrue, "subtask")
  1281. }
  1282. driver, err := self.GetDriver()
  1283. if err != nil {
  1284. return errors.Wrapf(err, "GetDriver")
  1285. }
  1286. shutdownMode := api.VM_SHUTDOWN_MODE_KEEP_CHARGING
  1287. if stopCharging && driver.IsSupportShutdownMode() {
  1288. shutdownMode = api.VM_SHUTDOWN_MODE_STOP_CHARGING
  1289. }
  1290. _, err = db.Update(self, func() error {
  1291. self.ShutdownMode = shutdownMode
  1292. return nil
  1293. })
  1294. if err != nil {
  1295. return errors.Wrapf(err, "db.Update")
  1296. }
  1297. return driver.StartGuestStopTask(self, ctx, userCred, params, parentTaskId)
  1298. }
  1299. func (self *SGuest) insertIso(imageId string, cdromOrdinal int64) bool {
  1300. cdrom := self.getCdrom(true, cdromOrdinal)
  1301. return cdrom.insertIso(imageId)
  1302. }
  1303. func (self *SGuest) InsertIsoSucc(cdromOrdinal int64, imageId string, path string, size int64, name string, bootIndex *int8) (*SGuestcdrom, bool) {
  1304. cdrom := self.getCdrom(false, cdromOrdinal)
  1305. return cdrom, cdrom.insertIsoSucc(imageId, path, size, name, bootIndex)
  1306. }
  1307. func (self *SGuest) GetDetailsIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1308. var cdromOrdinal int64 = 0
  1309. if query.Contains("ordinal") {
  1310. cdromOrdinal, _ = query.Int("ordinal")
  1311. }
  1312. cdrom := self.getCdrom(false, cdromOrdinal)
  1313. desc := jsonutils.NewDict()
  1314. if len(cdrom.ImageId) > 0 {
  1315. desc.Set("image_id", jsonutils.NewString(cdrom.ImageId))
  1316. desc.Set("status", jsonutils.NewString("inserting"))
  1317. }
  1318. if len(cdrom.Path) > 0 {
  1319. desc.Set("name", jsonutils.NewString(cdrom.Name))
  1320. desc.Set("size", jsonutils.NewInt(int64(cdrom.Size)))
  1321. desc.Set("status", jsonutils.NewString("ready"))
  1322. }
  1323. return desc, nil
  1324. }
  1325. // 获取Kickstart信息
  1326. func (self *SGuest) GetDetailsKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1327. result := jsonutils.NewDict()
  1328. kickstartConfig, err := self.GetKickstartConfig(ctx, userCred)
  1329. if err != nil {
  1330. return nil, httperrors.NewInternalServerError("Failed to get kickstart config: %v", err)
  1331. }
  1332. status := self.GetKickstartStatus(ctx, userCred)
  1333. attempt := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, userCred)
  1334. if attempt == "" {
  1335. attempt = "0"
  1336. }
  1337. kickstartType := self.GetKickstartType(ctx, userCred)
  1338. if kickstartConfig != nil {
  1339. configDict := jsonutils.Marshal(kickstartConfig)
  1340. result.Set("config", configDict)
  1341. } else {
  1342. result.Set("config", jsonutils.NewDict())
  1343. }
  1344. result.Set("status", jsonutils.NewString(status))
  1345. result.Set("attempt", jsonutils.NewString(attempt))
  1346. result.Set("type", jsonutils.NewString(kickstartType))
  1347. return result, nil
  1348. }
  1349. // 挂载ISO镜像
  1350. func (self *SGuest) PerformInsertiso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1351. if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
  1352. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  1353. }
  1354. cdromOrdinal, _ := data.Int("cdrom_ordinal")
  1355. if cdromOrdinal < 0 {
  1356. return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
  1357. }
  1358. cdrom := self.getCdrom(false, cdromOrdinal)
  1359. if cdrom != nil && len(cdrom.ImageId) > 0 {
  1360. return nil, httperrors.NewBadRequestError("CD-ROM not empty, please eject first")
  1361. }
  1362. imageId, _ := data.GetString("image_id")
  1363. image, err := parseIsoInfo(ctx, userCred, imageId)
  1364. if err != nil {
  1365. log.Errorln(err)
  1366. return nil, err
  1367. }
  1368. var bootIndex *int8
  1369. if data.Contains("boot_index") {
  1370. bd, _ := data.Int("boot_index")
  1371. bd8 := int8(bd)
  1372. bootIndex = &bd8
  1373. if isDup, err := self.isBootIndexDuplicated(bd8); err != nil {
  1374. return nil, err
  1375. } else if isDup {
  1376. return nil, httperrors.NewInputParameterError("boot index %d is duplicated", bd8)
  1377. }
  1378. }
  1379. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  1380. err = self.StartInsertIsoTask(ctx, cdromOrdinal, image.Id, false, bootIndex, self.HostId, userCred, "")
  1381. return nil, err
  1382. } else {
  1383. return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
  1384. }
  1385. }
  1386. // 卸载ISO镜像
  1387. func (self *SGuest) PerformEjectiso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1388. if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
  1389. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  1390. }
  1391. cdromOrdinal, _ := data.Int("cdrom_ordinal")
  1392. if cdromOrdinal < 0 {
  1393. return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
  1394. }
  1395. cdrom := self.getCdrom(false, cdromOrdinal)
  1396. if cdrom == nil || len(cdrom.ImageId) == 0 {
  1397. return nil, httperrors.NewBadRequestError("No ISO to eject")
  1398. }
  1399. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  1400. err := self.StartEjectisoTask(ctx, cdromOrdinal, userCred, "")
  1401. return nil, err
  1402. } else {
  1403. return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
  1404. }
  1405. }
  1406. func (self *SGuest) StartEjectisoTask(ctx context.Context, cdromOrdinal int64, userCred mcclient.TokenCredential, parentTaskId string) error {
  1407. data := jsonutils.NewDict()
  1408. data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
  1409. task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectISOTask", self, userCred, data, parentTaskId, "", nil)
  1410. if err != nil {
  1411. return err
  1412. }
  1413. task.ScheduleRun(nil)
  1414. return nil
  1415. }
  1416. func (self *SGuest) StartInsertIsoTask(ctx context.Context, cdromOrdinal int64, imageId string, boot bool, bootIndex *int8, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
  1417. self.insertIso(imageId, cdromOrdinal)
  1418. data := jsonutils.NewDict()
  1419. data.Add(jsonutils.NewString(imageId), "image_id")
  1420. data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
  1421. data.Add(jsonutils.NewString(hostId), "host_id")
  1422. if boot {
  1423. data.Add(jsonutils.JSONTrue, "boot")
  1424. }
  1425. if bootIndex != nil {
  1426. data.Add(jsonutils.NewInt(int64(*bootIndex)), "boot_index")
  1427. }
  1428. taskName := "GuestInsertIsoTask"
  1429. if self.BackupHostId != "" {
  1430. taskName = "HaGuestInsertIsoTask"
  1431. }
  1432. task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
  1433. if err != nil {
  1434. return err
  1435. }
  1436. task.ScheduleRun(nil)
  1437. return nil
  1438. }
  1439. func (self *SGuest) insertVfd(imageId string, floppyOrdinal int64) bool {
  1440. floppy := self.getFloppy(true, floppyOrdinal)
  1441. return floppy.insertVfd(imageId)
  1442. }
  1443. func (self *SGuest) InsertVfdSucc(floppyOrdinal int64, imageId string, path string, size int64, name string) bool {
  1444. floppy := self.getFloppy(true, floppyOrdinal)
  1445. return floppy.insertVfdSucc(imageId, path, size, name)
  1446. }
  1447. func (self *SGuest) GetDetailsVfd(floppyOrdinal int64, userCred mcclient.TokenCredential) jsonutils.JSONObject {
  1448. floppy := self.getFloppy(false, floppyOrdinal)
  1449. desc := jsonutils.NewDict()
  1450. if len(floppy.ImageId) > 0 {
  1451. desc.Set("image_id", jsonutils.NewString(floppy.ImageId))
  1452. desc.Set("status", jsonutils.NewString("inserting"))
  1453. }
  1454. if len(floppy.Path) > 0 {
  1455. desc.Set("name", jsonutils.NewString(floppy.Name))
  1456. desc.Set("size", jsonutils.NewInt(int64(floppy.Size)))
  1457. desc.Set("status", jsonutils.NewString("ready"))
  1458. }
  1459. return desc
  1460. }
  1461. // 挂载软盘
  1462. func (self *SGuest) PerformInsertvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerInsertVfdInput) (jsonutils.JSONObject, error) {
  1463. if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
  1464. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  1465. }
  1466. if data.FloppyOrdinal < 0 {
  1467. return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
  1468. }
  1469. floppy := self.getFloppy(false, data.FloppyOrdinal)
  1470. if floppy != nil && len(floppy.ImageId) > 0 {
  1471. return nil, httperrors.NewBadRequestError("Floppy not empty, please eject first")
  1472. }
  1473. image, err := parseIsoInfo(ctx, userCred, data.ImageId)
  1474. if err != nil {
  1475. log.Errorln(err)
  1476. return nil, err
  1477. }
  1478. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  1479. err = self.StartInsertVfdTask(ctx, data.FloppyOrdinal, image.Id, false, self.HostId, userCred, "")
  1480. return nil, err
  1481. } else {
  1482. return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
  1483. }
  1484. }
  1485. // 卸载软盘
  1486. func (self *SGuest) PerformEjectvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerEjectVfdInput) (jsonutils.JSONObject, error) {
  1487. if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
  1488. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  1489. }
  1490. if data.FloppyOrdinal < 0 {
  1491. return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
  1492. }
  1493. floppy := self.getFloppy(false, data.FloppyOrdinal)
  1494. if floppy == nil || len(floppy.ImageId) == 0 {
  1495. return nil, httperrors.NewBadRequestError("No VFD to eject")
  1496. }
  1497. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  1498. err := self.StartEjectvfdTask(ctx, userCred, "")
  1499. return nil, err
  1500. } else {
  1501. return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
  1502. }
  1503. }
  1504. func (self *SGuest) StartEjectvfdTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1505. task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectVFDTask", self, userCred, nil, parentTaskId, "", nil)
  1506. if err != nil {
  1507. return err
  1508. }
  1509. task.ScheduleRun(nil)
  1510. return nil
  1511. }
  1512. func (self *SGuest) StartInsertVfdTask(ctx context.Context, floppyOrdinal int64, imageId string, boot bool, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
  1513. self.insertVfd(imageId, floppyOrdinal)
  1514. data := jsonutils.NewDict()
  1515. data.Add(jsonutils.NewString(imageId), "image_id")
  1516. data.Add(jsonutils.NewString(hostId), "host_id")
  1517. if boot {
  1518. data.Add(jsonutils.JSONTrue, "boot")
  1519. }
  1520. taskName := "GuestInsertVfdTask"
  1521. if self.BackupHostId != "" {
  1522. taskName = "HaGuestInsertVfdTask"
  1523. }
  1524. task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
  1525. if err != nil {
  1526. return err
  1527. }
  1528. task.ScheduleRun(nil)
  1529. return nil
  1530. }
  1531. func (self *SGuest) RebalanceVirtualIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential) error {
  1532. devs, err := self.GetIsolatedDevices()
  1533. if err != nil {
  1534. return errors.Wrap(err, "guest get isolated devices")
  1535. }
  1536. if len(devs) == 0 {
  1537. return nil
  1538. }
  1539. host, err := self.GetHost()
  1540. if err != nil {
  1541. return errors.Wrap(err, "guest get host")
  1542. }
  1543. var numaNodeBalance = true
  1544. detachDevs := make([]SIsolatedDevice, 0)
  1545. originDevConfigs := make([]api.IsolatedDeviceConfig, 0)
  1546. for i := range devs {
  1547. if utils.IsInStringArray(devs[i].DevType, api.VITRUAL_DEVICE_TYPES) {
  1548. originDevConfigs = append(originDevConfigs, api.IsolatedDeviceConfig{
  1549. Model: devs[i].Model,
  1550. DevType: devs[i].DevType,
  1551. })
  1552. detachDevs = append(detachDevs, devs[i])
  1553. isBalance, err := host.VirtualDeviceNumaBalance(devs[i].DevType, devs[i].NumaNode)
  1554. if err != nil {
  1555. return errors.Wrap(err, "VirtualDeviceNumaBalance")
  1556. }
  1557. if numaNodeBalance && !isBalance {
  1558. numaNodeBalance = false
  1559. }
  1560. }
  1561. }
  1562. log.Infof("Guest %s on host %s start virtual devices numa node balance %v", self.Id, host.Id, numaNodeBalance)
  1563. if !numaNodeBalance {
  1564. err := self.SetMetadata(ctx, api.VM_METADATA_VIRTUAL_ISOLATED_DEVICE_CONFIG, originDevConfigs, userCred)
  1565. if err != nil {
  1566. return errors.Wrap(err, "set metadata virtual isolated device config")
  1567. }
  1568. lockman.LockObject(ctx, host)
  1569. defer lockman.ReleaseObject(ctx, host)
  1570. for i := 0; i < len(detachDevs); i++ {
  1571. err := self.detachIsolateDevice(ctx, userCred, &detachDevs[i])
  1572. if err != nil {
  1573. return errors.Wrapf(err, "detach device %s", detachDevs[i].GetId())
  1574. }
  1575. }
  1576. usedDeviceMap := map[string]*SIsolatedDevice{}
  1577. for i := range detachDevs {
  1578. devConfig := &api.IsolatedDeviceConfig{
  1579. Model: detachDevs[i].Model,
  1580. DevType: detachDevs[i].DevType,
  1581. }
  1582. err := IsolatedDeviceManager.attachHostDeviceToGuestByModel(ctx, self, host, devConfig, userCred, usedDeviceMap, nil)
  1583. if err != nil {
  1584. return errors.Wrap(err, "attachHostDeviceToGuestByModel")
  1585. }
  1586. }
  1587. }
  1588. return nil
  1589. }
  1590. func (self *SGuest) StartGueststartTask(
  1591. ctx context.Context, userCred mcclient.TokenCredential,
  1592. data *jsonutils.JSONDict, parentTaskId string,
  1593. ) error {
  1594. schedStart := self.Hypervisor == api.HYPERVISOR_KVM && self.guestDisksStorageTypeIsShared()
  1595. startFromCreate := false
  1596. if !gotypes.IsNil(data) {
  1597. startFromCreate = jsonutils.QueryBoolean(data, "start_from_create", false)
  1598. }
  1599. if options.Options.IgnoreNonrunningGuests {
  1600. host := HostManager.FetchHostById(self.HostId)
  1601. if !startFromCreate && host != nil && host.EnableNumaAllocate {
  1602. schedStart = true
  1603. }
  1604. }
  1605. if !startFromCreate && self.IsSchedulerNumaAllocate() {
  1606. // clean cpu numa pin
  1607. err := self.SetCpuNumaPin(ctx, userCred, nil, nil)
  1608. if err != nil {
  1609. return errors.Wrap(err, "clean cpu numa pin")
  1610. }
  1611. }
  1612. if options.Options.VirtualDeviceNumaBalance {
  1613. err := self.RebalanceVirtualIsolatedDevices(ctx, userCred)
  1614. if err != nil {
  1615. return errors.Wrap(err, "rebalance virtual isolated devices")
  1616. }
  1617. }
  1618. if schedStart {
  1619. return self.GuestSchedStartTask(ctx, userCred, data, parentTaskId)
  1620. } else {
  1621. return self.GuestNonSchedStartTask(ctx, userCred, data, parentTaskId)
  1622. }
  1623. }
  1624. func (self *SGuest) GuestSchedStartTask(
  1625. ctx context.Context, userCred mcclient.TokenCredential,
  1626. data *jsonutils.JSONDict, parentTaskId string,
  1627. ) error {
  1628. self.SetStatus(ctx, userCred, api.VM_SCHEDULE, "")
  1629. task, err := taskman.TaskManager.NewTask(ctx, "GuestSchedStartTask", self, userCred, data,
  1630. parentTaskId, "", nil)
  1631. if err != nil {
  1632. return err
  1633. }
  1634. task.ScheduleRun(nil)
  1635. return nil
  1636. }
  1637. func (self *SGuest) GuestNonSchedStartTask(
  1638. ctx context.Context, userCred mcclient.TokenCredential,
  1639. data *jsonutils.JSONDict, parentTaskId string,
  1640. ) error {
  1641. self.SetStatus(ctx, userCred, api.VM_START_START, "")
  1642. taskName := "GuestStartTask"
  1643. if self.BackupHostId != "" {
  1644. taskName = "HAGuestStartTask"
  1645. }
  1646. if self.IsSchedulerNumaAllocate() {
  1647. srcSchedCpuNumaPin := make([]schedapi.SCpuNumaPin, 0)
  1648. err := self.CpuNumaPin.Unmarshal(&srcSchedCpuNumaPin)
  1649. if err != nil {
  1650. return errors.Wrap(err, "unmarshal cpu_numa_pin")
  1651. }
  1652. // set cpu numa pin
  1653. err = self.SetCpuNumaPin(ctx, userCred, srcSchedCpuNumaPin, nil)
  1654. if err != nil {
  1655. return nil
  1656. }
  1657. }
  1658. task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
  1659. if err != nil {
  1660. return err
  1661. }
  1662. task.ScheduleRun(nil)
  1663. return nil
  1664. }
  1665. func (self *SGuest) StartGuestCreateTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput, pendingUsage quotas.IQuota, parentTaskId string) error {
  1666. if input.FakeCreate {
  1667. self.fixFakeServerInfo(ctx, userCred)
  1668. return nil
  1669. }
  1670. driver, err := self.GetDriver()
  1671. if err != nil {
  1672. return errors.Wrapf(err, "GetDriver")
  1673. }
  1674. return driver.StartGuestCreateTask(self, ctx, userCred, input.JSON(input), pendingUsage, parentTaskId)
  1675. }
  1676. func (self *SGuest) fixFakeServerInfo(ctx context.Context, userCred mcclient.TokenCredential) {
  1677. status := []string{api.VM_READY, api.VM_RUNNING}
  1678. rand.Seed(time.Now().Unix())
  1679. db.Update(self, func() error {
  1680. self.Status = status[rand.Intn(len(status))]
  1681. self.PowerStates = api.VM_POWER_STATES_ON
  1682. if self.Status == api.VM_READY {
  1683. self.PowerStates = api.VM_POWER_STATES_OFF
  1684. }
  1685. return nil
  1686. })
  1687. disks, _ := self.GetDisks()
  1688. for i := range disks {
  1689. db.Update(&disks[i], func() error {
  1690. disks[i].Status = api.DISK_READY
  1691. return nil
  1692. })
  1693. }
  1694. networks, _ := self.GetNetworks("")
  1695. for i := range networks {
  1696. if len(networks[i].IpAddr) > 0 {
  1697. continue
  1698. }
  1699. network, _ := networks[i].GetNetwork()
  1700. if network != nil {
  1701. db.Update(&networks[i], func() error {
  1702. networks[i].IpAddr, _ = network.GetFreeIP(ctx, userCred, nil, nil, "", api.IPAllocationRandom, false, api.AddressTypeIPv4)
  1703. return nil
  1704. })
  1705. }
  1706. }
  1707. if eip, _ := self.GetEipOrPublicIp(); eip != nil && len(eip.IpAddr) == 0 {
  1708. db.Update(eip, func() error {
  1709. if len(eip.NetworkId) > 0 {
  1710. if network, _ := eip.GetNetwork(); network != nil {
  1711. eip.IpAddr, _ = network.GetFreeIP(ctx, userCred, nil, nil, "", api.IPAllocationRandom, false, api.AddressTypeIPv4)
  1712. return nil
  1713. }
  1714. }
  1715. return nil
  1716. })
  1717. }
  1718. }
  1719. func (self *SGuest) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1720. if len(parentTaskId) == 0 {
  1721. self.SetStatus(ctx, userCred, apis.STATUS_START_SYNC_STATUS, "")
  1722. }
  1723. driver, err := self.GetDriver()
  1724. if err != nil {
  1725. return errors.Wrapf(err, "GetDriver")
  1726. }
  1727. return driver.StartGuestSyncstatusTask(self, ctx, userCred, parentTaskId)
  1728. }
  1729. func (self *SGuest) StartAutoDeleteGuestTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1730. db.OpsLog.LogEvent(self, db.ACT_DELETE, "auto-delete after stop", userCred)
  1731. opts := api.ServerDeleteInput{}
  1732. return self.StartDeleteGuestTask(ctx, userCred, parentTaskId, opts)
  1733. }
  1734. func (self *SGuest) StartDeleteGuestTask(
  1735. ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string,
  1736. opts api.ServerDeleteInput,
  1737. ) error {
  1738. params := jsonutils.NewDict()
  1739. params.Add(jsonutils.NewString(self.Status), "guest_status")
  1740. params.Update(jsonutils.Marshal(opts))
  1741. self.SetStatus(ctx, userCred, api.VM_START_DELETE, "")
  1742. if self.HostId != "" {
  1743. driver, err := self.GetDriver()
  1744. if err != nil {
  1745. return errors.Wrapf(err, "GetDriver")
  1746. }
  1747. return driver.StartDeleteGuestTask(ctx, userCred, self, params, parentTaskId)
  1748. } else {
  1749. task, err := taskman.TaskManager.NewTask(ctx, "GuestDeleteWithoutHostTask", self, userCred, params, parentTaskId, "", nil)
  1750. if err != nil {
  1751. return errors.Wrap(err, "NewTask GuestDeleteWithoutHostTask")
  1752. }
  1753. task.ScheduleRun(nil)
  1754. return nil
  1755. }
  1756. }
  1757. // 清除虚拟机记录(仅数据库操作)
  1758. func (self *SGuest) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1759. err := self.validateDeleteCondition(ctx, true)
  1760. if err != nil {
  1761. return nil, err
  1762. }
  1763. host, _ := self.GetHost()
  1764. if host != nil && host.GetEnabled() {
  1765. return nil, httperrors.NewInvalidStatusError("Cannot purge server on enabled host")
  1766. }
  1767. opts := api.ServerDeleteInput{
  1768. Purge: true,
  1769. }
  1770. err = self.StartDeleteGuestTask(ctx, userCred, "", opts)
  1771. return nil, err
  1772. }
  1773. func (self *SGuest) setKeypairId(userCred mcclient.TokenCredential, keypairId string) error {
  1774. diff, err := db.Update(self, func() error {
  1775. self.KeypairId = keypairId
  1776. return nil
  1777. })
  1778. if err != nil {
  1779. db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
  1780. }
  1781. return err
  1782. }
  1783. // 重装系统(更换系统镜像)
  1784. func (self *SGuest) PerformRebuildRoot(
  1785. ctx context.Context,
  1786. userCred mcclient.TokenCredential,
  1787. query jsonutils.JSONObject,
  1788. input *api.ServerRebuildRootInput,
  1789. ) (*api.SGuest, error) {
  1790. driver, err := self.GetDriver()
  1791. if err != nil {
  1792. return nil, err
  1793. }
  1794. input, err = driver.ValidateRebuildRoot(ctx, userCred, self, input)
  1795. if err != nil {
  1796. return nil, err
  1797. }
  1798. if len(input.UserData) > 0 {
  1799. // validate UserData
  1800. if err := userdata.ValidateUserdata(input.UserData, self.OsType); err != nil {
  1801. return nil, httperrors.NewInputParameterError("Invalid userdata: %v", err)
  1802. }
  1803. self.setUserData(ctx, userCred, input.UserData)
  1804. }
  1805. if len(input.ImageId) > 0 {
  1806. img, err := CachedimageManager.getImageInfo(ctx, userCred, input.ImageId, false)
  1807. if err != nil {
  1808. return nil, httperrors.NewNotFoundError("failed to find %s", input.ImageId)
  1809. }
  1810. err = driver.ValidateImage(ctx, img)
  1811. if err != nil {
  1812. return nil, err
  1813. }
  1814. host, err := self.GetHost()
  1815. if err != nil {
  1816. return nil, errors.Wrapf(err, "GetHost")
  1817. }
  1818. region, err := host.GetRegion()
  1819. if err != nil {
  1820. return nil, errors.Wrapf(err, "GetRegion")
  1821. }
  1822. // compare os arch
  1823. if len(img.Properties["os_arch"]) > 0 && len(self.OsArch) > 0 && !apis.IsSameArch(self.OsArch, img.Properties["os_arch"]) {
  1824. return nil, httperrors.NewConflictError("root disk image(%s) and guest(%s) OsArch mismatch", img.Properties["os_arch"], self.OsArch)
  1825. } else if len(self.InstanceType) > 0 {
  1826. sku, _ := ServerSkuManager.FetchSkuByNameAndProvider(self.InstanceType, region.Provider, true)
  1827. if sku != nil && len(sku.CpuArch) > 0 && len(img.Properties["os_arch"]) > 0 && !apis.IsSameArch(img.Properties["os_arch"], sku.CpuArch) {
  1828. return nil, httperrors.NewConflictError("root disk image(%s) and sku(%s) architecture mismatch", img.Properties["os_arch"], sku.CpuArch)
  1829. }
  1830. }
  1831. diskCat := self.CategorizeDisks()
  1832. if gotypes.IsNil(diskCat.Root) {
  1833. return nil, httperrors.NewInputParameterError("no root disk is found")
  1834. }
  1835. if img.MinDiskMB == 0 || img.Status != imageapi.IMAGE_STATUS_ACTIVE {
  1836. return nil, httperrors.NewInputParameterError("invlid image")
  1837. }
  1838. if img.MinDiskMB > diskCat.Root.DiskSize {
  1839. return nil, httperrors.NewInputParameterError("image size exceeds root disk size")
  1840. }
  1841. osType, _ := img.Properties["os_type"]
  1842. osName := self.GetMetadata(ctx, "os_name", userCred)
  1843. if len(osName) == 0 && len(osType) == 0 && strings.ToLower(osType) != strings.ToLower(osName) {
  1844. return nil, httperrors.NewBadRequestError("Cannot switch OS between %s-%s", osName, osType)
  1845. }
  1846. input.ImageId = img.Id
  1847. }
  1848. templateId := self.GetTemplateId()
  1849. if templateId != input.ImageId && len(templateId) > 0 && len(input.ImageId) > 0 && !driver.IsRebuildRootSupportChangeUEFI() {
  1850. q := CachedimageManager.Query().In("id", []string{input.ImageId, templateId})
  1851. images := []SCachedimage{}
  1852. err := db.FetchModelObjects(CachedimageManager, q, &images)
  1853. if err != nil {
  1854. return nil, errors.Wrap(err, "FetchModelObjects")
  1855. }
  1856. if len(images) == 2 && images[0].UEFI != images[1].UEFI {
  1857. return nil, httperrors.NewUnsupportOperationError("Can not rebuild root with with diff uefi image")
  1858. }
  1859. }
  1860. rebuildStatus, err := driver.GetRebuildRootStatus()
  1861. if err != nil {
  1862. return nil, httperrors.NewInputParameterError("%v", err)
  1863. }
  1864. if !driver.IsRebuildRootSupportChangeImage() && len(input.ImageId) > 0 {
  1865. if len(templateId) == 0 {
  1866. return nil, httperrors.NewBadRequestError("No template for root disk, cannot rebuild root")
  1867. }
  1868. if input.ImageId != templateId {
  1869. return nil, httperrors.NewInputParameterError("%s not support rebuild root with a different image", driver.GetHypervisor())
  1870. }
  1871. }
  1872. if !utils.IsInStringArray(self.Status, rebuildStatus) {
  1873. return nil, httperrors.NewInvalidStatusError("Cannot reset root in status %s", self.Status)
  1874. }
  1875. if self.Status == api.VM_READY && self.ShutdownMode == api.VM_SHUTDOWN_MODE_STOP_CHARGING {
  1876. return nil, httperrors.NewInvalidStatusError("Cannot reset root with %s", self.ShutdownMode)
  1877. }
  1878. autoStart := false
  1879. if input.AutoStart != nil {
  1880. autoStart = *input.AutoStart
  1881. }
  1882. var needStop = false
  1883. if self.Status == api.VM_RUNNING {
  1884. needStop = true
  1885. }
  1886. resetPasswd := input.ResetPassword
  1887. passwd := input.Password
  1888. if len(passwd) > 0 {
  1889. err = seclib2.ValidatePassword(passwd)
  1890. if err != nil {
  1891. return nil, errors.Wrap(err, "ValidatePassword")
  1892. }
  1893. }
  1894. if len(input.KeypairId) > 0 {
  1895. _, err := validators.ValidateModel(ctx, userCred, KeypairManager, &input.KeypairId)
  1896. if err != nil {
  1897. return nil, err
  1898. }
  1899. if self.KeypairId != input.KeypairId {
  1900. err = self.setKeypairId(userCred, input.KeypairId)
  1901. if err != nil {
  1902. return nil, httperrors.NewGeneralError(err)
  1903. }
  1904. }
  1905. resetPasswd = true
  1906. } else {
  1907. err = self.setKeypairId(userCred, "")
  1908. if err != nil {
  1909. return nil, httperrors.NewGeneralError(err)
  1910. }
  1911. }
  1912. allDisks := false
  1913. if input.AllDisks != nil {
  1914. allDisks = *input.AllDisks
  1915. }
  1916. input.ResetPassword = resetPasswd
  1917. if input.ResetPassword {
  1918. if len(input.LoginAccount) > 0 {
  1919. if len(input.LoginAccount) > 32 {
  1920. return nil, httperrors.NewInputParameterError("login_account is longer than 32 chars")
  1921. }
  1922. if err := GuestManager.ValidateNameLoginAccount(input.LoginAccount); err != nil {
  1923. return nil, err
  1924. }
  1925. }
  1926. }
  1927. return nil, self.StartRebuildRootTask(ctx, userCred, input.ImageId, needStop, autoStart, allDisks, &input.ServerDeployInputBase)
  1928. }
  1929. func (self *SGuest) GetTemplateId() string {
  1930. gdc := self.CategorizeDisks()
  1931. if gdc.Root != nil {
  1932. return gdc.Root.GetTemplateId()
  1933. }
  1934. return ""
  1935. }
  1936. func (self *SGuest) StartRebuildRootTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, needStop, autoStart bool, allDisk bool, deployInput *api.ServerDeployInputBase) error {
  1937. data := jsonutils.NewDict()
  1938. if len(imageId) == 0 {
  1939. imageId = self.GetTemplateId()
  1940. if len(imageId) == 0 {
  1941. return httperrors.NewBadRequestError("No template for root disk, cannot rebuild root")
  1942. }
  1943. }
  1944. data.Set("image_id", jsonutils.NewString(imageId))
  1945. if needStop {
  1946. data.Set("need_stop", jsonutils.JSONTrue)
  1947. }
  1948. if autoStart {
  1949. data.Set("auto_start", jsonutils.JSONTrue)
  1950. }
  1951. /*if resetPasswd {
  1952. data.Set("reset_password", jsonutils.JSONTrue)
  1953. } else {
  1954. data.Set("reset_password", jsonutils.JSONFalse)
  1955. }
  1956. if len(passwd) > 0 {
  1957. data.Set("password", jsonutils.NewString(passwd))
  1958. }*/
  1959. if allDisk {
  1960. data.Set("all_disks", jsonutils.JSONTrue)
  1961. } else {
  1962. data.Set("all_disks", jsonutils.JSONFalse)
  1963. }
  1964. data.Set("deploy_params", jsonutils.Marshal(deployInput))
  1965. self.SetStatus(ctx, userCred, api.VM_REBUILD_ROOT, "request start rebuild root")
  1966. task, err := taskman.TaskManager.NewTask(ctx, "GuestRebuildRootTask", self, userCred, data, "", "", nil)
  1967. if err != nil {
  1968. return err
  1969. }
  1970. task.ScheduleRun(nil)
  1971. return nil
  1972. }
  1973. func (self *SGuest) DetachDisk(ctx context.Context, disk *SDisk, userCred mcclient.TokenCredential) {
  1974. guestdisk := self.GetGuestDisk(disk.Id)
  1975. if guestdisk != nil {
  1976. guestdisk.Detach(ctx, userCred)
  1977. }
  1978. }
  1979. // 创建虚拟机磁盘
  1980. func (self *SGuest) PerformCreatedisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1981. diskSize := 0
  1982. disksConf := make([]*api.DiskConfig, 0)
  1983. diskSizes := make(map[string]int, 0)
  1984. diskDefArray, err := cmdline.FetchDiskConfigsByJSON(data)
  1985. if err != nil {
  1986. return nil, err
  1987. }
  1988. if len(diskDefArray) == 0 {
  1989. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No Disk Info Provided", userCred, false)
  1990. return nil, httperrors.NewBadRequestError("No Disk Info Provided")
  1991. }
  1992. for diskIdx := 0; diskIdx < len(diskDefArray); diskIdx += 1 {
  1993. diskInfo, err := parseDiskInfo(ctx, userCred, diskDefArray[diskIdx])
  1994. if err != nil {
  1995. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
  1996. return nil, httperrors.NewBadRequestError("%v", err)
  1997. }
  1998. if len(diskInfo.Backend) == 0 {
  1999. diskInfo.Backend = self.getDefaultStorageType()
  2000. }
  2001. disksConf = append(disksConf, diskInfo)
  2002. if _, ok := diskSizes[diskInfo.Backend]; !ok {
  2003. diskSizes[diskInfo.Backend] = diskInfo.SizeMb
  2004. } else {
  2005. diskSizes[diskInfo.Backend] += diskInfo.SizeMb
  2006. }
  2007. diskSize += diskInfo.SizeMb
  2008. }
  2009. host, _ := self.GetHost()
  2010. if host == nil {
  2011. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No valid host", userCred, false)
  2012. return nil, httperrors.NewBadRequestError("No valid host")
  2013. }
  2014. for backend, size := range diskSizes {
  2015. storage := host.GetLeastUsedStorage(backend)
  2016. if storage == nil {
  2017. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "No valid storage on current host", userCred, false)
  2018. return nil, httperrors.NewBadRequestError("No valid storage on current host")
  2019. }
  2020. if storage.GetCapacity() > 0 && storage.GetCapacity() < int64(size) {
  2021. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, "Not eough storage space on current host", userCred, false)
  2022. return nil, httperrors.NewBadRequestError("Not eough storage space on current host")
  2023. }
  2024. }
  2025. pendingUsage := &SQuota{
  2026. Storage: diskSize,
  2027. }
  2028. keys, err := self.GetQuotaKeys()
  2029. if err != nil {
  2030. return nil, err
  2031. }
  2032. pendingUsage.SetKeys(keys)
  2033. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
  2034. if err != nil {
  2035. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
  2036. return nil, httperrors.NewOutOfQuotaError("%v", err)
  2037. }
  2038. lockman.LockObject(ctx, host)
  2039. defer lockman.ReleaseObject(ctx, host)
  2040. err = self.CreateDisksOnHost(ctx, userCred, host, disksConf, pendingUsage, false, options.Options.UseServerTagsForDisk, nil, nil, false)
  2041. if err != nil {
  2042. quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
  2043. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CREATE, err.Error(), userCred, false)
  2044. return nil, httperrors.NewBadRequestError("%v", err)
  2045. }
  2046. err = self.StartGuestCreateDiskTask(ctx, userCred, disksConf, "")
  2047. return nil, err
  2048. }
  2049. func (self *SGuest) StartGuestCreateDiskTask(ctx context.Context, userCred mcclient.TokenCredential, disks []*api.DiskConfig, parentTaskId string) error {
  2050. data := jsonutils.NewDict()
  2051. data.Add(jsonutils.Marshal(disks), "disks")
  2052. task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateDiskTask", self, userCred, data, parentTaskId, "", nil)
  2053. if err != nil {
  2054. return errors.Wrapf(err, "NewTask")
  2055. }
  2056. return task.ScheduleRun(nil)
  2057. }
  2058. // 卸载磁盘
  2059. func (self *SGuest) PerformDetachdisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDetachDiskInput) (jsonutils.JSONObject, error) {
  2060. if len(input.DiskId) == 0 {
  2061. return nil, httperrors.NewMissingParameterError("disk_id")
  2062. }
  2063. diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
  2064. if err != nil {
  2065. return nil, err
  2066. }
  2067. disk := diskObj.(*SDisk)
  2068. attached, err := self.isAttach2Disk(disk)
  2069. if err != nil {
  2070. return nil, httperrors.NewInternalServerError("check isAttach2Disk fail %s", err)
  2071. }
  2072. if !attached {
  2073. return nil, nil
  2074. }
  2075. driver, err := self.GetDriver()
  2076. if err != nil {
  2077. return nil, err
  2078. }
  2079. detachDiskStatus, err := driver.GetDetachDiskStatus()
  2080. if err != nil {
  2081. return nil, err
  2082. }
  2083. if input.KeepDisk && !driver.CanKeepDetachDisk() {
  2084. return nil, httperrors.NewInputParameterError("Cannot keep detached disk")
  2085. }
  2086. err = driver.ValidateDetachDisk(ctx, userCred, self, disk)
  2087. if err != nil {
  2088. return nil, err
  2089. }
  2090. if utils.IsInStringArray(self.Status, detachDiskStatus) {
  2091. self.SetStatus(ctx, userCred, api.VM_DETACH_DISK, "")
  2092. err = self.StartGuestDetachdiskTask(ctx, userCred, disk, input.KeepDisk, "", false, false)
  2093. return nil, err
  2094. }
  2095. return nil, httperrors.NewInvalidStatusError("Server in %s not able to detach disk", self.Status)
  2096. }
  2097. func (self *SGuest) StartGuestDetachdiskTask(
  2098. ctx context.Context, userCred mcclient.TokenCredential,
  2099. disk *SDisk, keepDisk bool, parentTaskId string, purge bool, syncDescOnly bool,
  2100. ) error {
  2101. taskData := jsonutils.NewDict()
  2102. taskData.Add(jsonutils.NewString(disk.Id), "disk_id")
  2103. taskData.Add(jsonutils.NewBool(keepDisk), "keep_disk")
  2104. taskData.Add(jsonutils.NewBool(purge), "purge")
  2105. // finally reuse fw_only option
  2106. taskData.Add(jsonutils.NewBool(syncDescOnly), "sync_desc_only")
  2107. if utils.IsInStringArray(disk.Status, []string{api.DISK_INIT, api.DISK_ALLOC_FAILED}) {
  2108. //删除非正常状态下的disk
  2109. taskData.Add(jsonutils.JSONFalse, "keep_disk")
  2110. db.Update(disk, func() error {
  2111. disk.AutoDelete = true
  2112. return nil
  2113. })
  2114. }
  2115. driver, err := self.GetDriver()
  2116. if err != nil {
  2117. return errors.Wrapf(err, "GetDriver")
  2118. }
  2119. disk.SetStatus(ctx, userCred, api.DISK_DETACHING, "")
  2120. return driver.StartGuestDetachdiskTask(ctx, userCred, self, taskData, parentTaskId)
  2121. }
  2122. func (self *SGuest) GetReleasedIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential) ([]api.ServerReleasedIsolatedDevice, error) {
  2123. devs := make([]api.ServerReleasedIsolatedDevice, 0)
  2124. if ret := self.GetMetadata(ctx, api.VM_METADATA_RELEASED_DEVICES, userCred); ret == "" {
  2125. return devs, nil
  2126. }
  2127. obj := self.GetMetadataJson(ctx, api.VM_METADATA_RELEASED_DEVICES, userCred)
  2128. if obj == nil {
  2129. return nil, errors.Error("get medata json")
  2130. }
  2131. if err := obj.Unmarshal(&devs); err != nil {
  2132. return nil, errors.Wrapf(err, "unmarshal json string: %s", obj.String())
  2133. }
  2134. return devs, nil
  2135. }
  2136. func (self *SGuest) SetReleasedIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs []SIsolatedDevice) error {
  2137. records := make([]api.ServerReleasedIsolatedDevice, 0)
  2138. for _, dev := range devs {
  2139. record := api.ServerReleasedIsolatedDevice{
  2140. DevType: dev.DevType,
  2141. Model: dev.Model,
  2142. }
  2143. records = append(records, record)
  2144. }
  2145. if err := self.SetMetadata(ctx, api.VM_METADATA_RELEASED_DEVICES, records, userCred); err != nil {
  2146. return errors.Wrap(err, "set metadata")
  2147. }
  2148. return nil
  2149. }
  2150. func (self *SGuest) DetachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs []SIsolatedDevice) error {
  2151. host, _ := self.GetHost()
  2152. lockman.LockObject(ctx, host)
  2153. defer lockman.ReleaseObject(ctx, host)
  2154. for i := 0; i < len(devs); i++ {
  2155. // check first
  2156. dev := devs[i]
  2157. if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
  2158. if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
  2159. msg := fmt.Sprintf("Can't separately detach dev type %s", dev.DevType)
  2160. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2161. return httperrors.NewBadRequestError("%s", msg)
  2162. } else {
  2163. if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
  2164. msg := fmt.Sprintf("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
  2165. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2166. return httperrors.NewBadRequestError("%s", msg)
  2167. }
  2168. }
  2169. }
  2170. }
  2171. for i := 0; i < len(devs); i++ {
  2172. err := self.detachIsolateDevice(ctx, userCred, &devs[i])
  2173. if err != nil {
  2174. return errors.Wrapf(err, "detach device %s", devs[i].GetId())
  2175. }
  2176. }
  2177. return nil
  2178. }
  2179. // 卸载透传设备
  2180. func (self *SGuest) PerformDetachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  2181. if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
  2182. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  2183. }
  2184. if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
  2185. (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
  2186. msg := fmt.Sprintf("Can't detach isolated device when guest is %s", self.GetStatus())
  2187. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2188. return nil, httperrors.NewInvalidStatusError("%s", msg)
  2189. }
  2190. var detachAllDevice = jsonutils.QueryBoolean(data, "detach_all", false)
  2191. devs := make([]SIsolatedDevice, 0)
  2192. if !detachAllDevice {
  2193. device, err := data.GetString("device")
  2194. if err != nil {
  2195. msg := "Missing isolated device"
  2196. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2197. return nil, httperrors.NewBadRequestError("%s", msg)
  2198. }
  2199. iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
  2200. if err != nil {
  2201. msgFmt := "Isolated device %s not found"
  2202. msg := fmt.Sprintf(msgFmt, device)
  2203. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2204. return nil, httperrors.NewBadRequestError(msgFmt, device)
  2205. }
  2206. devs = append(devs, *iDev.(*SIsolatedDevice))
  2207. } else {
  2208. devs, _ = self.GetIsolatedDevices()
  2209. }
  2210. if err := self.DetachIsolatedDevices(ctx, userCred, devs); err != nil {
  2211. return nil, err
  2212. }
  2213. return nil, self.StartIsolatedDevicesSyncTask(ctx, userCred, jsonutils.QueryBoolean(data, "auto_start", false), "")
  2214. }
  2215. func (self *SGuest) startDetachIsolateDeviceWithoutNic(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
  2216. iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
  2217. if err != nil {
  2218. msgFmt := "Isolated device %s not found"
  2219. msg := fmt.Sprintf(msgFmt, device)
  2220. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2221. return httperrors.NewBadRequestError(msgFmt, device)
  2222. }
  2223. dev := iDev.(*SIsolatedDevice)
  2224. return self.DetachIsolatedDevices(ctx, userCred, []SIsolatedDevice{*dev})
  2225. }
  2226. func (self *SGuest) detachIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice) error {
  2227. if dev.GuestId != self.Id {
  2228. msg := "Isolated device is not attached to this guest"
  2229. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
  2230. return httperrors.NewBadRequestError("%s", msg)
  2231. }
  2232. drv, _ := self.GetDriver()
  2233. if err := drv.BeforeDetachIsolatedDevice(ctx, userCred, self, dev); err != nil {
  2234. return errors.Wrapf(err, "BeforeDetachIsolatedDevice %s of guest %s", jsonutils.Marshal(dev), self.GetId())
  2235. }
  2236. _, err := db.Update(dev, func() error {
  2237. dev.GuestId = ""
  2238. dev.NetworkIndex = -1
  2239. dev.DiskIndex = -1
  2240. return nil
  2241. })
  2242. if err != nil {
  2243. return err
  2244. }
  2245. db.OpsLog.LogEvent(self, db.ACT_GUEST_DETACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
  2246. return nil
  2247. }
  2248. // 挂载透传设备
  2249. func (self *SGuest) PerformAttachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  2250. if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
  2251. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  2252. }
  2253. if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
  2254. (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
  2255. msg := fmt.Sprintf("Can't attach isolated device when guest is %v", self.GetStatus())
  2256. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
  2257. return nil, httperrors.NewInvalidStatusError("%s", msg)
  2258. }
  2259. var err error
  2260. autoStart := jsonutils.QueryBoolean(data, "auto_start", false)
  2261. if data.Contains("device") {
  2262. device, _ := data.GetString("device")
  2263. err = self.StartAttachIsolatedDeviceGpuOrUsb(ctx, userCred, device, autoStart)
  2264. } else if data.Contains("model") {
  2265. vmodel, _ := data.GetString("model")
  2266. var count int64 = 1
  2267. if data.Contains("count") {
  2268. count, _ = data.Int("count")
  2269. }
  2270. if count < 1 {
  2271. return nil, httperrors.NewBadRequestError("guest attach gpu count must > 0")
  2272. }
  2273. err = self.StartAttachIsolatedDevices(ctx, userCred, vmodel, int(count), autoStart)
  2274. } else {
  2275. return nil, httperrors.NewMissingParameterError("device||model")
  2276. }
  2277. if err != nil {
  2278. return nil, err
  2279. }
  2280. return nil, nil
  2281. }
  2282. func (self *SGuest) StartAttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModel string, count int, autoStart bool) error {
  2283. if err := self.startAttachIsolatedDevices(ctx, userCred, devModel, count); err != nil {
  2284. return err
  2285. }
  2286. // perform post attach task
  2287. return self.StartIsolatedDevicesSyncTask(ctx, userCred, autoStart, "")
  2288. }
  2289. func (self *SGuest) AttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModelCount map[string]int) error {
  2290. host, _ := self.GetHost()
  2291. lockman.LockObject(ctx, host)
  2292. defer lockman.ReleaseObject(ctx, host)
  2293. unusedDevs := []SIsolatedDevice{}
  2294. for devModel, count := range devModelCount {
  2295. devs, err := IsolatedDeviceManager.GetUnusedDevsOnHost(host.Id, devModel, count)
  2296. if err != nil {
  2297. return httperrors.NewInternalServerError("fetch gpu failed %s", err)
  2298. }
  2299. if len(devs) == 0 || len(devs) != count {
  2300. return httperrors.NewBadRequestError("require %d %s isolated device of host %s is not enough", count, devModel, host.GetName())
  2301. }
  2302. dev := devs[0]
  2303. if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
  2304. if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
  2305. return httperrors.NewBadRequestError("Can't separately attach dev type %s", dev.DevType)
  2306. } else {
  2307. if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
  2308. return httperrors.NewBadRequestError("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
  2309. }
  2310. }
  2311. }
  2312. if dev.DevType == api.LEGACY_VGPU_TYPE {
  2313. attachedGpus, err := self.GetIsolatedDevices()
  2314. if err != nil {
  2315. return errors.Wrap(err, "get isolated devices")
  2316. }
  2317. for i := range attachedGpus {
  2318. if attachedGpus[i].DevType == api.LEGACY_VGPU_TYPE {
  2319. return httperrors.NewBadRequestError("Nvidia vgpu count exceed > 1")
  2320. } else if utils.IsInStringArray(attachedGpus[i].DevType, api.VALID_GPU_TYPES) {
  2321. return httperrors.NewBadRequestError("Nvidia vgpu can't passthrough with other gpus")
  2322. }
  2323. }
  2324. } else if dev.DevType == api.CONTAINER_DEV_NVIDIA_MPS {
  2325. allDevs, err := IsolatedDeviceManager.GetUnusedDevsOnHost(host.Id, devModel, -1)
  2326. if err != nil {
  2327. return httperrors.NewInternalServerError("fetch gpu failed %s", err)
  2328. }
  2329. attachedGpus, err := self.GetIsolatedDevices()
  2330. if err != nil {
  2331. return httperrors.NewInternalServerError("get attached isolated devices %s", err)
  2332. }
  2333. attachedAddrs := map[string]struct{}{}
  2334. for i := range attachedGpus {
  2335. addr := strings.Split(attachedGpus[i].Addr, "-")[0]
  2336. attachedAddrs[addr] = struct{}{}
  2337. }
  2338. validDevs := []SIsolatedDevice{}
  2339. for i := range allDevs {
  2340. devAddr := strings.Split(allDevs[i].Addr, "-")[0]
  2341. if _, ok := attachedAddrs[devAddr]; ok {
  2342. continue
  2343. }
  2344. validDevs = append(validDevs, allDevs[i])
  2345. }
  2346. if len(validDevs) < count {
  2347. return httperrors.NewInsufficientResourceError("require %d %s isolated device of host %s is not enough", count, devModel, host.GetName())
  2348. }
  2349. devs = validDevs[:count]
  2350. }
  2351. unusedDevs = append(unusedDevs, devs...)
  2352. }
  2353. defer func() { go host.ClearSchedDescCache() }()
  2354. for i := 0; i < len(unusedDevs); i++ {
  2355. if err := self.attachIsolatedDevice(ctx, userCred, &unusedDevs[i], nil, nil); err != nil {
  2356. return errors.Wrapf(err, "attach device %s", unusedDevs[i].GetId())
  2357. }
  2358. }
  2359. return nil
  2360. }
  2361. func (self *SGuest) startAttachIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, devModel string, count int) error {
  2362. return self.AttachIsolatedDevices(ctx, userCred, map[string]int{devModel: count})
  2363. }
  2364. func (self *SGuest) StartAttachIsolatedDeviceGpuOrUsb(ctx context.Context, userCred mcclient.TokenCredential, device string, autoStart bool) error {
  2365. if err := self.startAttachIsolatedDevGeneral(ctx, userCred, device); err != nil {
  2366. return err
  2367. }
  2368. // perform post attach task
  2369. return self.StartIsolatedDevicesSyncTask(ctx, userCred, autoStart, "")
  2370. }
  2371. func (self *SGuest) startAttachIsolatedDevGeneral(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
  2372. iDev, err := IsolatedDeviceManager.FetchByIdOrName(ctx, userCred, device)
  2373. if err != nil {
  2374. msgFmt := "Isolated device %s not found"
  2375. msg := fmt.Sprintf(msgFmt, device)
  2376. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
  2377. return httperrors.NewBadRequestError(msgFmt, device)
  2378. }
  2379. dev := iDev.(*SIsolatedDevice)
  2380. if !utils.IsInStringArray(dev.DevType, api.VALID_ATTACH_TYPES) {
  2381. if devModel, err := IsolatedDeviceModelManager.GetByDevType(dev.DevType); err != nil {
  2382. return httperrors.NewBadRequestError("Can't separately attach dev type %s", dev.DevType)
  2383. } else {
  2384. if !devModel.HotPluggable.Bool() && self.GetStatus() == api.VM_RUNNING {
  2385. return httperrors.NewBadRequestError("dev type %s model %s unhotpluggable", dev.DevType, devModel.Model)
  2386. }
  2387. }
  2388. }
  2389. if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) {
  2390. return httperrors.NewInvalidStatusError("Can't attach GPU when status is %q", self.GetStatus())
  2391. }
  2392. if dev.DevType == api.LEGACY_VGPU_TYPE {
  2393. devs, err := self.GetIsolatedDevices()
  2394. if err != nil {
  2395. return errors.Wrap(err, "get isolated devices")
  2396. }
  2397. for i := range devs {
  2398. if devs[i].DevType == api.LEGACY_VGPU_TYPE {
  2399. return httperrors.NewBadRequestError("Nvidia vgpu count exceed > 1")
  2400. } else if utils.IsInStringArray(devs[i].DevType, api.VALID_GPU_TYPES) {
  2401. return httperrors.NewBadRequestError("Nvidia vgpu can't passthrough with other gpus")
  2402. }
  2403. }
  2404. }
  2405. host, _ := self.GetHost()
  2406. lockman.LockObject(ctx, host)
  2407. defer lockman.ReleaseObject(ctx, host)
  2408. err = self.attachIsolatedDevice(ctx, userCred, dev, nil, nil)
  2409. var msg string
  2410. if err != nil {
  2411. msg = err.Error()
  2412. } else {
  2413. go host.ClearSchedDescCache()
  2414. }
  2415. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, err == nil)
  2416. return err
  2417. }
  2418. func (self *SGuest) attachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice, networkIndex *int, diskIndex *int8) error {
  2419. if len(dev.GuestId) > 0 {
  2420. return fmt.Errorf("Isolated device already attached to another guest: %s", dev.GuestId)
  2421. }
  2422. if dev.HostId != self.HostId {
  2423. return fmt.Errorf("Isolated device and guest are not located in the same host")
  2424. }
  2425. drv, _ := self.GetDriver()
  2426. if err := drv.BeforeAttachIsolatedDevice(ctx, userCred, self, dev); err != nil {
  2427. return errors.Wrapf(err, "BeforeAttachIsolatedDevice %s of guest %s", jsonutils.Marshal(dev), self.GetId())
  2428. }
  2429. if _, err := db.Update(dev, func() error {
  2430. dev.GuestId = self.Id
  2431. if networkIndex != nil {
  2432. dev.NetworkIndex = *networkIndex
  2433. } else {
  2434. dev.NetworkIndex = -1
  2435. }
  2436. if diskIndex != nil {
  2437. dev.DiskIndex = *diskIndex
  2438. } else {
  2439. dev.DiskIndex = -1
  2440. }
  2441. return nil
  2442. }); err != nil {
  2443. return errors.Wrap(err, "db.Update")
  2444. }
  2445. db.OpsLog.LogEvent(self, db.ACT_GUEST_ATTACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
  2446. return nil
  2447. }
  2448. // 设置透传设备
  2449. func (self *SGuest) PerformSetIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  2450. if self.Hypervisor != api.HYPERVISOR_KVM && self.Hypervisor != api.HYPERVISOR_POD {
  2451. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  2452. }
  2453. if !utils.IsInStringArray(self.GetStatus(), []string{api.VM_READY, api.VM_RUNNING}) ||
  2454. (self.Hypervisor == api.HYPERVISOR_POD && self.GetStatus() != api.VM_READY) {
  2455. return nil, httperrors.NewInvalidStatusError("Can't set isolated device when guest is %s", self.GetStatus())
  2456. }
  2457. var addDevs []string
  2458. {
  2459. addDevices, err := data.Get("add_devices")
  2460. if err == nil {
  2461. arrAddDev, ok := addDevices.(*jsonutils.JSONArray)
  2462. if ok {
  2463. addDevs = arrAddDev.GetStringArray()
  2464. } else {
  2465. return nil, httperrors.NewInputParameterError("attach devices is not string array")
  2466. }
  2467. }
  2468. }
  2469. var delDevs []string
  2470. {
  2471. delDevices, err := data.Get("del_devices")
  2472. if err == nil {
  2473. arrDelDev, ok := delDevices.(*jsonutils.JSONArray)
  2474. if ok {
  2475. delDevs = arrDelDev.GetStringArray()
  2476. } else {
  2477. return nil, httperrors.NewInputParameterError("detach devices is not string array")
  2478. }
  2479. }
  2480. }
  2481. // detach first
  2482. for i := 0; i < len(delDevs); i++ {
  2483. err := self.startDetachIsolateDeviceWithoutNic(ctx, userCred, delDevs[i])
  2484. if err != nil {
  2485. return nil, err
  2486. }
  2487. }
  2488. for i := 0; i < len(addDevs); i++ {
  2489. err := self.startAttachIsolatedDevGeneral(ctx, userCred, addDevs[i])
  2490. if err != nil {
  2491. return nil, err
  2492. }
  2493. }
  2494. return nil, self.StartIsolatedDevicesSyncTask(ctx, userCred, jsonutils.QueryBoolean(data, "auto_start", false), "")
  2495. }
  2496. func (self *SGuest) StartIsolatedDevicesSyncTask(ctx context.Context, userCred mcclient.TokenCredential, autoStart bool, parentId string) error {
  2497. if self.GetStatus() == api.VM_RUNNING {
  2498. autoStart = false
  2499. }
  2500. data := jsonutils.Marshal(map[string]interface{}{
  2501. "auto_start": autoStart,
  2502. }).(*jsonutils.JSONDict)
  2503. if task, err := taskman.TaskManager.NewTask(ctx, "GuestIsolatedDeviceSyncTask", self, userCred, data, parentId, "", nil); err != nil {
  2504. return err
  2505. } else {
  2506. return task.ScheduleRun(nil)
  2507. }
  2508. }
  2509. func (self *SGuest) findGuestnetworkByInfo(info api.ServerNetworkInfo) (*SGuestnetwork, error) {
  2510. if len(info.IpAddr) > 0 {
  2511. gn, err := self.GetGuestnetworkByIp(info.IpAddr)
  2512. if err != nil {
  2513. if errors.Cause(err) == sql.ErrNoRows {
  2514. return nil, httperrors.NewNotFoundError("ip %s not found", info.IpAddr)
  2515. }
  2516. return nil, httperrors.NewGeneralError(err)
  2517. }
  2518. return gn, nil
  2519. } else if len(info.Ip6Addr) > 0 {
  2520. gn, err := self.GetGuestnetworkByIp6(info.Ip6Addr)
  2521. if err != nil {
  2522. if errors.Cause(err) == sql.ErrNoRows {
  2523. return nil, httperrors.NewNotFoundError("ipv6 %s not found", info.Ip6Addr)
  2524. }
  2525. return nil, httperrors.NewGeneralError(err)
  2526. }
  2527. return gn, nil
  2528. } else if len(info.Mac) > 0 {
  2529. gn, err := self.GetGuestnetworkByMac(info.Mac)
  2530. if err != nil {
  2531. if errors.Cause(err) == sql.ErrNoRows {
  2532. return nil, httperrors.NewNotFoundError("mac %s not found", info.Mac)
  2533. }
  2534. return nil, httperrors.NewGeneralError(err)
  2535. }
  2536. return gn, nil
  2537. } else {
  2538. gns, err := self.GetNetworks("")
  2539. if err != nil {
  2540. return nil, httperrors.NewGeneralError(err)
  2541. }
  2542. if info.Index >= 0 && info.Index < len(gns) {
  2543. return &gns[info.Index], nil
  2544. } else if info.Index >= 0 {
  2545. return nil, httperrors.NewNotFoundError("nic at index %d not found", info.Index)
  2546. }
  2547. return nil, httperrors.NewInputParameterError("no either ip_addr, ip6_addr, mac or index specified")
  2548. }
  2549. }
  2550. func (self *SGuest) getReuseAddr(gn *SGuestnetwork) string {
  2551. if self.GetHypervisor() != api.HYPERVISOR_BAREMETAL {
  2552. return ""
  2553. }
  2554. host, _ := self.GetHost()
  2555. hostNics := host.GetNics()
  2556. for _, hn := range hostNics {
  2557. if hn.GetMac().String() == gn.MacAddr {
  2558. return hn.IpAddr
  2559. }
  2560. }
  2561. return ""
  2562. }
  2563. // Change IPaddress of a guestnetwork
  2564. // first detach the network, then attach a network with identity mac address but different IP configurations
  2565. // TODO change IP address of a teaming NIC may fail!!
  2566. func (self *SGuest) PerformChangeIpaddr(
  2567. ctx context.Context,
  2568. userCred mcclient.TokenCredential,
  2569. query jsonutils.JSONObject,
  2570. input api.ServerChangeIpaddrInput,
  2571. ) (jsonutils.JSONObject, error) {
  2572. if self.Status != api.VM_READY && self.Status != api.VM_RUNNING && self.Status != api.VM_BLOCK_STREAM {
  2573. return nil, httperrors.NewInvalidStatusError("Cannot change network ip_addr in status %s", self.Status)
  2574. }
  2575. reserve := (input.Reserve != nil && *input.Reserve)
  2576. gn, err := self.findGuestnetworkByInfo(input.ServerNetworkInfo)
  2577. if err != nil {
  2578. return nil, errors.Wrap(err, "findGuestnetworkByInfo")
  2579. }
  2580. var conf *api.NetworkConfig
  2581. if input.NetConf != nil {
  2582. conf = input.NetConf
  2583. } else if len(input.NetDesc) > 0 {
  2584. netDescJson, err := jsonutils.ParseString(input.NetDesc)
  2585. if err != nil {
  2586. netDescJson = jsonutils.NewString(input.NetDesc)
  2587. }
  2588. netConf, err := cmdline.ParseNetworkConfigByJSON(netDescJson, -1)
  2589. if err != nil {
  2590. return nil, httperrors.NewInputParameterError("fail to parse net_desc %s: %s", input.NetDesc, err)
  2591. }
  2592. conf = netConf
  2593. } else {
  2594. return nil, httperrors.NewMissingParameterError("net_desc/net_conf")
  2595. }
  2596. if conf.BwLimit == 0 {
  2597. conf.BwLimit = gn.BwLimit
  2598. }
  2599. if conf.Index == 0 {
  2600. conf.Index = int(gn.Index)
  2601. }
  2602. conf, err = parseNetworkInfo(ctx, userCred, conf)
  2603. if err != nil {
  2604. return nil, httperrors.NewInputParameterError("parseNetworkInfo fail: %s", err)
  2605. }
  2606. if conf.StrictIPv6 && len(conf.Address) > 0 {
  2607. // strict ipv6 network, can't add ipv4 address
  2608. return nil, httperrors.NewBadRequestError("guest network has no ipv4 address")
  2609. }
  2610. reuseV4 := ""
  2611. if conf.Address == gn.IpAddr {
  2612. // 允许IPv4地址不变,只改IPv6地址
  2613. reuseV4 = conf.Address
  2614. }
  2615. reuseV6 := ""
  2616. if gn.Ip6Addr != "" && conf.Address6 != "" {
  2617. inputIP := net.ParseIP(conf.Address6)
  2618. gnIP := net.ParseIP(gn.Ip6Addr)
  2619. if string(inputIP) == string(gnIP) {
  2620. conf.Address6 = gn.Ip6Addr
  2621. reuseV6 = gn.Ip6Addr
  2622. }
  2623. }
  2624. err = isValidNetworkInfo(ctx, userCred, conf, reuseV4, reuseV6)
  2625. if err != nil {
  2626. return nil, httperrors.NewInputParameterError("isValidNetworkInfo fail: %s", err)
  2627. }
  2628. if len(conf.Network) == 0 {
  2629. return nil, httperrors.NewInputParameterError("no specific network")
  2630. }
  2631. netObj, err := NetworkManager.FetchById(conf.Network)
  2632. if err != nil {
  2633. if errors.Cause(err) == sql.ErrNoRows {
  2634. return nil, httperrors.NewResourceNotFoundError2("network", conf.Network)
  2635. } else {
  2636. return nil, errors.Wrapf(err, "NetworkManager.FetchById %s", conf.Network)
  2637. }
  2638. }
  2639. targetNetwork := netObj.(*SNetwork)
  2640. // host, _ := self.GetHost()
  2641. ngn, err := func() (*SGuestnetwork, error) {
  2642. lockman.LockObject(ctx, targetNetwork)
  2643. defer lockman.ReleaseObject(ctx, targetNetwork)
  2644. if len(conf.Mac) > 0 {
  2645. if conf.Mac != gn.MacAddr {
  2646. if self.Status != api.VM_READY {
  2647. // change mac
  2648. return nil, httperrors.NewInvalidStatusError("cannot change mac when guest is running")
  2649. }
  2650. // check mac duplication
  2651. cnt, err := GuestnetworkManager.Query().Equals("mac_addr", conf.Mac).CountWithError()
  2652. if err != nil {
  2653. return nil, httperrors.NewInternalServerError("check mac uniqueness fail %s", err)
  2654. }
  2655. if cnt > 0 {
  2656. return nil, httperrors.NewConflictError("mac addr %s has been occupied", conf.Mac)
  2657. }
  2658. }
  2659. } else {
  2660. conf.Mac = gn.MacAddr
  2661. }
  2662. if conf.Mac == gn.MacAddr && conf.Address == gn.IpAddr {
  2663. if len(gn.Ip6Addr) == 0 && len(conf.Address6) == 0 && !conf.RequireIPv6 {
  2664. return nil, nil
  2665. } else if len(gn.Ip6Addr) > 0 && conf.Address6 == gn.Ip6Addr {
  2666. return nil, nil
  2667. }
  2668. // reserve = true
  2669. }
  2670. if conf.StrictIPv6 {
  2671. conf.Address = ""
  2672. } else if len(conf.Address) == 0 || conf.Address != gn.IpAddr {
  2673. // need to allocate new address
  2674. addr4, err := targetNetwork.GetFreeIP(ctx, userCred, nil, nil, conf.Address, api.IPAllocationDirection(targetNetwork.AllocPolicy), reserve, api.AddressTypeIPv4)
  2675. if err != nil {
  2676. return nil, errors.Wrap(err, "GetFreeIPv4")
  2677. }
  2678. if len(conf.Address) > 0 && conf.RequireDesignatedIP && conf.Address != addr4 {
  2679. return nil, httperrors.NewConflictError("addr %s has been occupied", conf.Address)
  2680. }
  2681. conf.Address = addr4
  2682. }
  2683. if (conf.Address6 == "" && conf.RequireIPv6) || (len(conf.Address6) > 0 && conf.Address6 != gn.Ip6Addr) {
  2684. // need to allocate new IPv6 address
  2685. addr6, err := targetNetwork.GetFreeIP(ctx, userCred, nil, nil, conf.Address6, api.IPAllocationNone, reserve, api.AddressTypeIPv6)
  2686. if err != nil {
  2687. return nil, errors.Wrap(err, "GetFreeIPv6")
  2688. }
  2689. if len(conf.Address6) > 0 && conf.RequireDesignatedIP && conf.Address6 != addr6 {
  2690. return nil, httperrors.NewConflictError("addr %s has been occupied", conf.Address6)
  2691. }
  2692. conf.Address6 = addr6
  2693. }
  2694. ngn := *gn
  2695. _, err := db.Update(&ngn, func() error {
  2696. ngn.NetworkId = conf.Network
  2697. ngn.MacAddr = conf.Mac
  2698. ngn.IpAddr = conf.Address
  2699. ngn.Ip6Addr = conf.Address6
  2700. return nil
  2701. })
  2702. if err != nil {
  2703. return nil, errors.Wrap(err, "Update")
  2704. }
  2705. return &ngn, nil
  2706. }()
  2707. if err != nil {
  2708. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CHANGE_NIC, err, userCred, false)
  2709. return nil, err
  2710. }
  2711. if ngn == nil {
  2712. return nil, nil
  2713. }
  2714. //Get the detailed description of the NIC
  2715. networkJsonDesc := ngn.getJsonDesc()
  2716. newIpAddr := networkJsonDesc.Ip
  2717. newMacAddr := networkJsonDesc.Mac
  2718. newMaskLen := networkJsonDesc.Masklen
  2719. newGateway := networkJsonDesc.Gateway
  2720. ipMask := ""
  2721. if networkJsonDesc.Ip != "" {
  2722. ipMask = fmt.Sprintf("%s/%d", newIpAddr, newMaskLen)
  2723. }
  2724. ip6Mask := ""
  2725. if networkJsonDesc.Ip6 != "" {
  2726. ip6Mask = fmt.Sprintf("%s/%d", networkJsonDesc.Ip6, networkJsonDesc.Masklen6)
  2727. }
  2728. notes := gn.GetShortDesc(ctx)
  2729. if gn != nil {
  2730. if gn.IpAddr != ngn.IpAddr {
  2731. notes.Add(jsonutils.NewString(gn.IpAddr), "prev_ip")
  2732. }
  2733. if gn.Ip6Addr != ngn.Ip6Addr {
  2734. notes.Add(jsonutils.NewString(gn.Ip6Addr), "prev_ip6")
  2735. }
  2736. }
  2737. db.OpsLog.LogEvent(self, db.ACT_CHANGE_IPADDR, notes, userCred)
  2738. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CHANGE_NIC, notes, userCred, true)
  2739. restartNetwork := (input.RestartNetwork != nil && *input.RestartNetwork)
  2740. taskData := jsonutils.NewDict()
  2741. if self.Hypervisor == api.HYPERVISOR_KVM && restartNetwork && (self.Status == api.VM_RUNNING || self.Status == api.VM_BLOCK_STREAM) {
  2742. taskData.Set("restart_network", jsonutils.JSONTrue)
  2743. taskData.Set("prev_ip", jsonutils.NewString(gn.IpAddr))
  2744. taskData.Set("prev_mac", jsonutils.NewString(newMacAddr))
  2745. net, err := ngn.GetNetwork()
  2746. if err != nil {
  2747. return nil, errors.Wrapf(err, "GetNetwork")
  2748. }
  2749. taskData.Set("is_vpc_network", jsonutils.NewBool(net.isOneCloudVpcNetwork()))
  2750. if len(ipMask) > 0 {
  2751. taskData.Set("ip_mask", jsonutils.NewString(ipMask))
  2752. taskData.Set("gateway", jsonutils.NewString(newGateway))
  2753. }
  2754. if len(ip6Mask) > 0 {
  2755. taskData.Set("ip6_mask", jsonutils.NewString(ip6Mask))
  2756. taskData.Set("gateway6", jsonutils.NewString(networkJsonDesc.Gateway6))
  2757. }
  2758. if self.Status == api.VM_BLOCK_STREAM {
  2759. taskData.Set("in_block_stream", jsonutils.JSONTrue)
  2760. }
  2761. self.SetStatus(ctx, userCred, api.VM_RESTART_NETWORK, "restart network")
  2762. }
  2763. return nil, self.startSyncTask(ctx, userCred, false, "", taskData)
  2764. }
  2765. func (self *SGuest) GetIfNameByMac(ctx context.Context, userCred mcclient.TokenCredential, mac string) (string, error) {
  2766. //Find the network card according to the mac address, if it is empty, it means no network card is found
  2767. ifnameData, err := self.PerformQgaGetNetwork(ctx, userCred, nil, nil)
  2768. if err != nil {
  2769. return "", err
  2770. }
  2771. //Get the name of the network card
  2772. var parsedData []api.IfnameDetail
  2773. if err := ifnameData.Unmarshal(&parsedData); err != nil {
  2774. return "", err
  2775. }
  2776. var ifnameDevice string
  2777. //Finding a network card by its mac address
  2778. for _, detail := range parsedData {
  2779. if detail.HardwareAddress == mac {
  2780. ifnameDevice = detail.Name
  2781. }
  2782. }
  2783. return ifnameDevice, nil
  2784. }
  2785. // 卸载网卡
  2786. func (self *SGuest) PerformDetachnetwork(
  2787. ctx context.Context,
  2788. userCred mcclient.TokenCredential,
  2789. query jsonutils.JSONObject,
  2790. input api.ServerDetachnetworkInput,
  2791. ) (jsonutils.JSONObject, error) {
  2792. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) && !input.IsForce() {
  2793. return nil, httperrors.NewInvalidStatusError("Cannot detach network in status %s", self.Status)
  2794. }
  2795. driver, err := self.GetDriver()
  2796. if err != nil {
  2797. return nil, err
  2798. }
  2799. err = driver.ValidateDetachNetwork(ctx, userCred, self)
  2800. if err != nil {
  2801. if !input.IsForce() {
  2802. return nil, errors.Wrap(err, "ValidateDetachNetwork")
  2803. } else {
  2804. log.Errorf("ValidateDetachNetwork fail %s, ignore by force", err)
  2805. }
  2806. }
  2807. var gns []SGuestnetwork
  2808. if len(input.NetId) > 0 {
  2809. netObj, err := validators.ValidateModel(ctx, userCred, NetworkManager, &input.NetId)
  2810. if err != nil {
  2811. return nil, err
  2812. }
  2813. gns, err = self.GetNetworks(netObj.GetId())
  2814. if err != nil {
  2815. return nil, httperrors.NewGeneralError(err)
  2816. }
  2817. } else if len(input.IpAddr) > 0 || len(input.Ip6Addr) > 0 {
  2818. var gn *SGuestnetwork
  2819. var err error
  2820. if len(input.IpAddr) > 0 {
  2821. gn, err = self.GetGuestnetworkByIp(input.IpAddr)
  2822. } else if len(input.Ip6Addr) > 0 {
  2823. gn, err = self.GetGuestnetworkByIp6(input.Ip6Addr)
  2824. }
  2825. if err != nil {
  2826. if err == sql.ErrNoRows {
  2827. return nil, httperrors.NewNotFoundError("ip %s not found", input.IpAddr)
  2828. }
  2829. return nil, httperrors.NewGeneralError(err)
  2830. }
  2831. gns = []SGuestnetwork{*gn}
  2832. } else if len(input.Mac) > 0 {
  2833. gn, err := self.GetGuestnetworkByMac(input.Mac)
  2834. if err != nil {
  2835. if err == sql.ErrNoRows {
  2836. return nil, httperrors.NewNotFoundError("mac %s not found", input.Mac)
  2837. }
  2838. return nil, httperrors.NewGeneralError(err)
  2839. }
  2840. gns = []SGuestnetwork{*gn}
  2841. } else {
  2842. return nil, httperrors.NewMissingParameterError("net_id")
  2843. }
  2844. removeNics := make(map[string]*SGuestnetwork)
  2845. for i := range gns {
  2846. removeNics[gns[i].MacAddr] = &gns[i]
  2847. }
  2848. slaveNics, err := self.GetSlaveNetworks()
  2849. if err != nil {
  2850. return nil, errors.Wrap(err, "GetSlaveNetworks")
  2851. }
  2852. for i := range slaveNics {
  2853. teamMac := slaveNics[i].TeamWith
  2854. if len(teamMac) > 0 {
  2855. if _, ok := removeNics[teamMac]; ok {
  2856. // to remove slave's master NIC, should also remove slave itself
  2857. if _, ok2 := removeNics[slaveNics[i].MacAddr]; !ok2 {
  2858. // otherwise, report the error
  2859. return nil, errors.Wrap(errors.ErrInvalidStatus, "")
  2860. }
  2861. }
  2862. }
  2863. }
  2864. err = self.detachNetworks(ctx, userCred, gns, input.Reserve)
  2865. if err != nil {
  2866. return nil, errors.Wrap(err, "detachNetworks")
  2867. }
  2868. {
  2869. // 修正缺省网关
  2870. err := self.fixDefaultGateway(ctx, userCred)
  2871. if err != nil {
  2872. log.Errorf("fixDefaultGateway fail %s", err)
  2873. }
  2874. }
  2875. if !input.IsForce() && input.DisableSyncConfig != nil && !*input.DisableSyncConfig {
  2876. if self.Status == api.VM_READY {
  2877. err := self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
  2878. if err != nil {
  2879. return nil, errors.Wrap(err, "StartGuestDeployTask")
  2880. }
  2881. } else {
  2882. err := self.StartSyncTask(ctx, userCred, false, "")
  2883. if err != nil {
  2884. return nil, errors.Wrap(err, "StartSyncTask")
  2885. }
  2886. }
  2887. }
  2888. return nil, nil
  2889. }
  2890. func (guest *SGuest) fixDefaultGatewayByNics(ctx context.Context, userCred mcclient.TokenCredential, nics []SGuestnetwork) (bool, error) {
  2891. defaultGwCnt := 0
  2892. for i := range nics {
  2893. if nics[i].Virtual || len(nics[i].TeamWith) > 0 {
  2894. continue
  2895. }
  2896. if nics[i].IsDefault {
  2897. defaultGwCnt++
  2898. }
  2899. }
  2900. if defaultGwCnt == 1 {
  2901. return false, nil
  2902. }
  2903. nicList := netutils2.SNicInfoList{}
  2904. for i := range nics {
  2905. if nics[i].Virtual || len(nics[i].TeamWith) > 0 {
  2906. continue
  2907. }
  2908. net, _ := nics[i].GetNetwork()
  2909. if net != nil {
  2910. nicList = nicList.Add(nics[i].MacAddr, nics[i].IpAddr, net.GuestGateway, nics[i].Ip6Addr, net.GuestGateway6, nics[i].IsDefault)
  2911. }
  2912. }
  2913. gwMac, _ := nicList.FindDefaultNicMac()
  2914. if gwMac != "" {
  2915. err := guest.setDefaultGateway(ctx, userCred, gwMac)
  2916. if err != nil {
  2917. log.Errorf("setDefaultGateway fail %s", err)
  2918. return true, errors.Wrap(err, "setDefaultGateway")
  2919. }
  2920. }
  2921. return true, nil
  2922. }
  2923. func (guest *SGuest) fixDefaultGateway(ctx context.Context, userCred mcclient.TokenCredential) error {
  2924. nics, _ := guest.GetNetworks("")
  2925. _, err := guest.fixDefaultGatewayByNics(ctx, userCred, nics)
  2926. return err
  2927. }
  2928. // 挂载网卡
  2929. func (self *SGuest) PerformAttachnetwork(
  2930. ctx context.Context,
  2931. userCred mcclient.TokenCredential,
  2932. query jsonutils.JSONObject,
  2933. input *api.AttachNetworkInput,
  2934. ) (*api.SGuest, error) {
  2935. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  2936. return nil, httperrors.NewBadRequestError("Cannot attach network in status %s", self.Status)
  2937. }
  2938. // count := len(input.Nets)
  2939. if len(input.Nets) == 0 {
  2940. if len(input.NetDesc) > 0 {
  2941. for _, netDesc := range input.NetDesc {
  2942. netConf, err := cmdline.ParseNetworkConfigByJSON(jsonutils.NewString(netDesc), -1)
  2943. if err != nil {
  2944. return nil, httperrors.NewInputParameterError("fail to parse net_desc %s: %s", netDesc, err)
  2945. }
  2946. input.Nets = append(input.Nets, netConf)
  2947. }
  2948. }
  2949. if len(input.Nets) == 0 {
  2950. return nil, httperrors.NewMissingParameterError("nets/net_desc")
  2951. }
  2952. }
  2953. var inicCnt, enicCnt, isolatedDevCount, defaultGwCnt int
  2954. for i := range input.Nets {
  2955. err := isValidNetworkInfo(ctx, userCred, input.Nets[i], "", "")
  2956. if err != nil {
  2957. return nil, err
  2958. }
  2959. if IsExitNetworkInfo(ctx, userCred, input.Nets[i]) {
  2960. enicCnt += 1
  2961. // ebw = input.BwLimit
  2962. } else {
  2963. inicCnt += 1
  2964. // ibw = input.BwLimit
  2965. }
  2966. if input.Nets[i].BwLimit == 0 {
  2967. input.Nets[i].BwLimit = options.Options.DefaultBandwidth
  2968. }
  2969. if input.Nets[i].SriovDevice != nil {
  2970. if self.BackupHostId != "" {
  2971. return nil, httperrors.NewBadRequestError("Cannot create backup with isolated device")
  2972. }
  2973. devConfig, err := IsolatedDeviceManager.parseDeviceInfo(userCred, input.Nets[i].SriovDevice)
  2974. if err != nil {
  2975. return nil, httperrors.NewInputParameterError("parse isolated device description error %s", err)
  2976. }
  2977. err = IsolatedDeviceManager.isValidNicDeviceInfo(devConfig)
  2978. if err != nil {
  2979. return nil, err
  2980. }
  2981. input.Nets[i].SriovDevice = devConfig
  2982. input.Nets[i].Driver = api.NETWORK_DRIVER_VFIO
  2983. isolatedDevCount += 1
  2984. }
  2985. if len(input.Nets[i].Secgroups) > 0 {
  2986. secgroupIds, err := isValidSecgroups(ctx, userCred, input.Nets[i].Secgroups)
  2987. if err != nil {
  2988. return nil, err
  2989. }
  2990. input.Nets[i].Secgroups = secgroupIds
  2991. }
  2992. if input.Nets[i].IsDefault {
  2993. defaultGwCnt++
  2994. }
  2995. }
  2996. if defaultGwCnt > 1 {
  2997. return nil, errors.Wrapf(httperrors.ErrInputParameter, "more than 1 nic(%d) assigned as default gateway", defaultGwCnt)
  2998. }
  2999. pendingUsage := &SRegionQuota{
  3000. Port: inicCnt,
  3001. Eport: enicCnt,
  3002. //Bw: ibw,
  3003. //Ebw: ebw,
  3004. }
  3005. pendingUsageHost := &SQuota{IsolatedDevice: isolatedDevCount}
  3006. keys, err := self.GetRegionalQuotaKeys()
  3007. if err != nil {
  3008. return nil, err
  3009. }
  3010. quotakeys, err := self.GetQuotaKeys()
  3011. if err != nil {
  3012. return nil, err
  3013. }
  3014. pendingUsageHost.SetKeys(quotakeys)
  3015. pendingUsage.SetKeys(keys)
  3016. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
  3017. if err != nil {
  3018. return nil, httperrors.NewOutOfQuotaError("%v", err)
  3019. }
  3020. defer quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
  3021. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsageHost)
  3022. if err != nil {
  3023. return nil, httperrors.NewOutOfQuotaError("%v", err)
  3024. }
  3025. defer quotas.CancelPendingUsage(ctx, userCred, pendingUsageHost, pendingUsageHost, false)
  3026. host, _ := self.GetHost()
  3027. defer host.ClearSchedDescCache()
  3028. var defaultGwGn *SGuestnetwork
  3029. for i := range input.Nets {
  3030. gns, err := self.attach2NetworkDesc(ctx, userCred, host, input.Nets[i], pendingUsage, nil)
  3031. logclient.AddSimpleActionLog(self, logclient.ACT_ATTACH_NETWORK, input.Nets[i], userCred, err == nil)
  3032. if err != nil {
  3033. quotas.CancelPendingUsage(ctx, userCred, pendingUsage, pendingUsage, false)
  3034. return nil, httperrors.NewBadRequestError("%v", err)
  3035. }
  3036. for gnIdx := range gns {
  3037. if gns[gnIdx].IsDefault {
  3038. defaultGwGn = &gns[gnIdx]
  3039. }
  3040. }
  3041. if input.Nets[i].SriovDevice != nil {
  3042. err = self.allocSriovNicDevice(ctx, userCred, host, &gns[0], input.Nets[i], pendingUsageHost)
  3043. if err != nil {
  3044. quotas.CancelPendingUsage(ctx, userCred, pendingUsageHost, pendingUsageHost, false)
  3045. return nil, errors.Wrap(err, "self.allocSriovNicDevice")
  3046. }
  3047. }
  3048. if len(input.Nets[i].Secgroups) > 0 {
  3049. err = self.SaveNetworkSecgroups(ctx, userCred, input.Nets[i].Secgroups, gns[0].Index)
  3050. if err != nil {
  3051. return nil, errors.Wrap(err, "SaveNetworkSecgroups")
  3052. }
  3053. }
  3054. }
  3055. // adjust default gateway
  3056. if defaultGwGn != nil {
  3057. err := self.setDefaultGateway(ctx, userCred, defaultGwGn.MacAddr)
  3058. if err != nil {
  3059. log.Errorf("setDefaultGateway fail %s", err)
  3060. }
  3061. } else {
  3062. err := self.fixDefaultGateway(ctx, userCred)
  3063. if err != nil {
  3064. log.Errorf("fixDefaultGateway fail %s", err)
  3065. }
  3066. }
  3067. if input.DisableSyncConfig != nil && !*input.DisableSyncConfig {
  3068. if self.Status == api.VM_READY {
  3069. err := self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
  3070. if err != nil {
  3071. return nil, errors.Wrap(err, "StartGuestDeployTask")
  3072. }
  3073. } else {
  3074. err := self.StartSyncTask(ctx, userCred, false, "")
  3075. if err != nil {
  3076. return nil, errors.Wrap(err, "StartSyncTask")
  3077. }
  3078. }
  3079. }
  3080. return nil, nil
  3081. }
  3082. // 调整带宽
  3083. func (guest *SGuest) PerformChangeBandwidth(
  3084. ctx context.Context,
  3085. userCred mcclient.TokenCredential,
  3086. query jsonutils.JSONObject,
  3087. input api.ServerChangeBandwidthInput,
  3088. ) (jsonutils.JSONObject, error) {
  3089. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  3090. return nil, httperrors.NewBadRequestError("Cannot change bandwidth in status %s", guest.Status)
  3091. }
  3092. bandwidth := input.Bandwidth
  3093. if bandwidth < 0 {
  3094. return nil, httperrors.NewBadRequestError("Bandwidth must be non-negative")
  3095. }
  3096. guestnic, err := guest.findGuestnetworkByInfo(input.ServerNetworkInfo)
  3097. if err != nil {
  3098. return nil, errors.Wrap(err, "findGuestnetworkByInfo")
  3099. }
  3100. if guestnic.BwLimit != int(bandwidth) {
  3101. oldBw := guestnic.BwLimit
  3102. _, err := db.Update(guestnic, func() error {
  3103. guestnic.BwLimit = int(bandwidth)
  3104. return nil
  3105. })
  3106. if err != nil {
  3107. return nil, err
  3108. }
  3109. eventDesc := guestnic.GetShortDesc(ctx)
  3110. eventDesc.Add(jsonutils.NewInt(int64(oldBw)), "old_bw_limit_mbps")
  3111. db.OpsLog.LogEvent(guest, db.ACT_CHANGE_BANDWIDTH, eventDesc, userCred)
  3112. logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_CHANGE_BANDWIDTH, eventDesc, userCred, true)
  3113. if guest.Status == api.VM_READY || (input.NoSync != nil && *input.NoSync) {
  3114. // if no sync, just update db
  3115. return nil, nil
  3116. }
  3117. // otherwise, sync configure to host
  3118. return nil, guest.StartSyncTask(ctx, userCred, false, "")
  3119. }
  3120. return nil, nil
  3121. }
  3122. // 修改源地址检查
  3123. func (self *SGuest) PerformModifySrcCheck(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3124. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  3125. return nil, httperrors.NewBadRequestError("Cannot change setting in status %s", self.Status)
  3126. }
  3127. argValue := func(name string, val *bool) error {
  3128. if !data.Contains(name) {
  3129. return nil
  3130. }
  3131. b, err := data.Bool(name)
  3132. if err != nil {
  3133. return errors.Wrapf(err, "fetch arg %s", name)
  3134. }
  3135. *val = b
  3136. return nil
  3137. }
  3138. var (
  3139. srcIpCheck = self.SrcIpCheck.Bool()
  3140. srcMacCheck = self.SrcMacCheck.Bool()
  3141. )
  3142. if err := argValue("src_ip_check", &srcIpCheck); err != nil {
  3143. return nil, err
  3144. }
  3145. if err := argValue("src_mac_check", &srcMacCheck); err != nil {
  3146. return nil, err
  3147. }
  3148. // default: both check on
  3149. // switch: mac check off, also implies ip check off
  3150. // router: mac check on, ip check off
  3151. if !srcMacCheck && srcIpCheck {
  3152. srcIpCheck = false
  3153. }
  3154. if srcIpCheck != self.SrcIpCheck.Bool() || srcMacCheck != self.SrcMacCheck.Bool() {
  3155. diff, err := db.Update(self, func() error {
  3156. self.SrcIpCheck = tristate.NewFromBool(srcIpCheck)
  3157. self.SrcMacCheck = tristate.NewFromBool(srcMacCheck)
  3158. return nil
  3159. })
  3160. if err != nil {
  3161. return nil, err
  3162. }
  3163. db.OpsLog.LogEvent(self, db.ACT_GUEST_SRC_CHECK, diff, userCred)
  3164. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_SRC_CHECK, diff, userCred, true)
  3165. return nil, self.StartSyncTask(ctx, userCred, false, "")
  3166. }
  3167. return nil, nil
  3168. }
  3169. // 调整配置
  3170. func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerChangeConfigInput) (jsonutils.JSONObject, error) {
  3171. driver, err := self.GetDriver()
  3172. if err != nil {
  3173. return nil, err
  3174. }
  3175. if !driver.AllowReconfigGuest() {
  3176. return nil, httperrors.NewInvalidStatusError("Not allow to change config")
  3177. }
  3178. if len(self.BackupHostId) > 0 {
  3179. return nil, httperrors.NewBadRequestError("Guest have backup not allow to change config")
  3180. }
  3181. _, err = self.GetHost()
  3182. if err != nil {
  3183. return nil, httperrors.NewInvalidStatusError("no valid host")
  3184. }
  3185. confs, err := driver.ValidateGuestChangeConfigInput(ctx, self, input)
  3186. if err != nil {
  3187. return nil, errors.Wrap(err, "ValidateGuestChangeConfigInput")
  3188. }
  3189. if confs.CpuChanged() || confs.MemChanged() || confs.InstanceTypeChanged() {
  3190. changeStatus, err := driver.GetChangeInstanceTypeStatus()
  3191. if err != nil {
  3192. return nil, httperrors.NewInputParameterError("%v", err)
  3193. }
  3194. if !utils.IsInStringArray(self.Status, changeStatus) {
  3195. return nil, httperrors.NewInvalidStatusError("Cannot change config in %s for %s, requires %s", self.Status, self.GetHypervisor(), changeStatus)
  3196. }
  3197. }
  3198. if self.PowerStates == api.VM_POWER_STATES_ON && (confs.CpuChanged() || confs.MemChanged()) {
  3199. confs, err = driver.ValidateGuestHotChangeConfigInput(ctx, self, confs)
  3200. if err != nil {
  3201. return nil, httperrors.NewInvalidStatusError("cannot change CPU/Memory spec in power status %s: %s", self.PowerStates, err)
  3202. }
  3203. }
  3204. if len(confs.Create) > 0 {
  3205. attachStatus, err := driver.GetAttachDiskStatus()
  3206. if err != nil {
  3207. return nil, httperrors.NewInputParameterError("%v", err)
  3208. }
  3209. if !utils.IsInStringArray(self.Status, attachStatus) {
  3210. return nil, httperrors.NewInvalidStatusError("Cannot attach disk in %s for %s, requires %s", self.Status, self.GetHypervisor(), attachStatus)
  3211. }
  3212. }
  3213. log.Debugf("PerformChangeConfig %s", jsonutils.Marshal(confs).String())
  3214. pendingUsage := &SQuota{}
  3215. if added := confs.AddedCpu(); added > 0 {
  3216. pendingUsage.Cpu = added
  3217. }
  3218. if added := confs.AddedExtraCpu(); added > 0 {
  3219. pendingUsage.Cpu += added
  3220. }
  3221. if added := confs.AddedMem(); added > 0 {
  3222. pendingUsage.Memory = added
  3223. }
  3224. if added := confs.AddedDisk(); added > 0 {
  3225. pendingUsage.Storage = added
  3226. }
  3227. keys, err := self.GetQuotaKeys()
  3228. if err != nil {
  3229. return nil, err
  3230. }
  3231. pendingUsage.SetKeys(keys)
  3232. log.Debugf("ChangeConfig pendingUsage %s", jsonutils.Marshal(pendingUsage))
  3233. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
  3234. if err != nil {
  3235. return nil, errors.Wrap(err, "CheckSetPendingQuota")
  3236. }
  3237. // logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CHANGE_CONFIG, confs, userCred, true)
  3238. self.StartChangeConfigTask(ctx, userCred, confs, "", pendingUsage)
  3239. return nil, nil
  3240. }
  3241. func (self *SGuest) ChangeConfToSchedDesc(addCpu, addExtraCpu, addMem int, schedInputDisks []*api.DiskConfig) *schedapi.ScheduleInput {
  3242. region, _ := self.GetRegion()
  3243. devs, _ := self.GetIsolatedDevices()
  3244. desc := &schedapi.ScheduleInput{
  3245. ServerConfig: schedapi.ServerConfig{
  3246. ServerConfigs: &api.ServerConfigs{
  3247. Hypervisor: self.Hypervisor,
  3248. PreferRegion: region.Id,
  3249. PreferHost: self.HostId,
  3250. Disks: schedInputDisks,
  3251. },
  3252. Memory: addMem,
  3253. Ncpu: addCpu,
  3254. Project: self.ProjectId,
  3255. Domain: self.DomainId,
  3256. },
  3257. OsArch: self.OsArch,
  3258. ChangeConfig: true,
  3259. HasIsolatedDevice: len(devs) > 0,
  3260. ExtraCpuCount: addExtraCpu,
  3261. }
  3262. return desc
  3263. }
  3264. func (self *SGuest) StartChangeConfigTask(ctx context.Context, userCred mcclient.TokenCredential,
  3265. confs *api.ServerChangeConfigSettings, parentTaskId string, pendingUsage quotas.IQuota) error {
  3266. self.SetStatus(ctx, userCred, api.VM_CHANGE_FLAVOR, "")
  3267. task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeConfigTask", self, userCred, jsonutils.Marshal(confs).(*jsonutils.JSONDict), parentTaskId, "", pendingUsage)
  3268. if err != nil {
  3269. return err
  3270. }
  3271. task.ScheduleRun(nil)
  3272. return nil
  3273. }
  3274. func (self *SGuest) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  3275. eip, _ := self.GetEipOrPublicIp()
  3276. if eip != nil {
  3277. eip.DoPendingDelete(ctx, userCred)
  3278. }
  3279. disks, _ := self.GetDisks()
  3280. for i := range disks {
  3281. if !disks[i].IsDetachable() {
  3282. disks[i].DoPendingDelete(ctx, userCred)
  3283. } else {
  3284. log.Warningf("detachable disk on pending delete guests!!! should be removed earlier")
  3285. self.DetachDisk(ctx, &disks[i], userCred)
  3286. }
  3287. }
  3288. SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
  3289. return self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
  3290. }
  3291. // 从回收站恢复虚拟机
  3292. func (self *SGuest) PerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3293. if self.PendingDeleted && !self.Deleted {
  3294. err := self.DoCancelPendingDelete(ctx, userCred)
  3295. if err != nil {
  3296. return nil, errors.Wrap(err, "DoCancelPendingDelete")
  3297. }
  3298. self.RecoverUsages(ctx, userCred)
  3299. }
  3300. return nil, nil
  3301. }
  3302. func (self *SGuest) DoCancelPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  3303. eip, _ := self.GetEipOrPublicIp()
  3304. if eip != nil {
  3305. eip.DoCancelPendingDelete(ctx, userCred)
  3306. }
  3307. disks, err := self.GetDisks()
  3308. if err != nil {
  3309. return err
  3310. }
  3311. for _, disk := range disks {
  3312. disk.DoCancelPendingDelete(ctx, userCred)
  3313. }
  3314. if self.BillingType == billing_api.BILLING_TYPE_POSTPAID && !self.ExpiredAt.IsZero() {
  3315. err := SaveReleaseAt(ctx, self, userCred, time.Time{})
  3316. if err != nil {
  3317. return err
  3318. }
  3319. }
  3320. err = self.SVirtualResourceBase.DoCancelPendingDelete(ctx, userCred)
  3321. if err != nil {
  3322. return err
  3323. }
  3324. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  3325. Obj: self,
  3326. Action: notifyclient.ActionCreate,
  3327. })
  3328. return nil
  3329. }
  3330. func (self *SGuest) StartUndeployGuestTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, targetHostId string) error {
  3331. data := jsonutils.NewDict()
  3332. if len(targetHostId) > 0 {
  3333. data.Add(jsonutils.NewString(targetHostId), "target_host_id")
  3334. }
  3335. task, err := taskman.TaskManager.NewTask(ctx, "GuestUndeployTask", self, userCred, data, parentTaskId, "", nil)
  3336. if err != nil {
  3337. return err
  3338. }
  3339. task.ScheduleRun(nil)
  3340. return nil
  3341. }
  3342. // 重置虚拟机状态
  3343. func (self *SGuest) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  3344. data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3345. drv, err := self.GetDriver()
  3346. if err != nil {
  3347. return nil, err
  3348. }
  3349. isHard := jsonutils.QueryBoolean(data, "is_hard", false)
  3350. 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}) {
  3351. drv.StartGuestResetTask(self, ctx, userCred, isHard, "")
  3352. return nil, nil
  3353. }
  3354. return nil, httperrors.NewInvalidStatusError("Cannot reset VM in status %s", self.Status)
  3355. }
  3356. // 同步虚拟机状态
  3357. func (self *SGuest) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3358. var openTask = true
  3359. count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
  3360. if err != nil {
  3361. return nil, err
  3362. }
  3363. if count > 0 {
  3364. return nil, httperrors.NewBadRequestError("Guest has %d task active, can't sync status", count)
  3365. }
  3366. return nil, self.StartSyncstatus(ctx, userCred, "")
  3367. }
  3368. func (self *SGuest) isNotRunningStatus(status string) bool {
  3369. if status == api.VM_READY || status == api.VM_SUSPEND {
  3370. return true
  3371. }
  3372. return false
  3373. }
  3374. func (self *SGuest) SetStatus(ctx context.Context, userCred mcclient.TokenCredential, status, reason string) error {
  3375. if status == api.VM_RUNNING {
  3376. if err := self.SetPowerStates(api.VM_POWER_STATES_ON); err != nil {
  3377. return errors.Wrap(err, "input status is running")
  3378. }
  3379. } else if status == api.VM_READY {
  3380. if err := self.SetPowerStates(api.VM_POWER_STATES_OFF); err != nil {
  3381. return errors.Wrap(err, "input status is ready")
  3382. }
  3383. }
  3384. err := self.SVirtualResourceBase.SetStatus(ctx, userCred, status, reason)
  3385. if err != nil {
  3386. return errors.Wrap(err, "setStatus")
  3387. }
  3388. return nil
  3389. }
  3390. func (self *SGuest) SetPowerStates(powerStates string) error {
  3391. if self.PowerStates == powerStates {
  3392. return nil
  3393. }
  3394. _, err := db.Update(self, func() error {
  3395. self.PowerStates = powerStates
  3396. return nil
  3397. })
  3398. return errors.Wrapf(err, "Update power states to %s", powerStates)
  3399. }
  3400. func (self *SGuest) SetBackupGuestStatus(userCred mcclient.TokenCredential, status string, reason string) error {
  3401. if self.BackupGuestStatus == status {
  3402. return nil
  3403. }
  3404. oldStatus := self.BackupGuestStatus
  3405. _, err := db.Update(self, func() error {
  3406. self.BackupGuestStatus = status
  3407. return nil
  3408. })
  3409. if err != nil {
  3410. return errors.Wrap(err, "Update backup guest status")
  3411. }
  3412. if userCred != nil {
  3413. notes := fmt.Sprintf("%s=>%s", oldStatus, status)
  3414. if len(reason) > 0 {
  3415. notes = fmt.Sprintf("%s: %s", notes, reason)
  3416. }
  3417. db.OpsLog.LogEvent(self, db.ACT_UPDATE_BACKUP_GUEST_STATUS, notes, userCred)
  3418. logclient.AddSimpleActionLog(self, logclient.ACT_UPDATE_BACKUP_GUEST_STATUS, notes, userCred, true)
  3419. }
  3420. return nil
  3421. }
  3422. func (g *SGuest) SetStatusFromHost(ctx context.Context, userCred mcclient.TokenCredential, resp api.HostUploadGuestStatusInput, hasParentTask bool, originStatus string) error {
  3423. statusStr := resp.Status
  3424. switch statusStr {
  3425. case cloudprovider.CloudVMStatusRunning:
  3426. statusStr = api.VM_RUNNING
  3427. case cloudprovider.CloudVMStatusSuspend:
  3428. statusStr = api.VM_SUSPEND
  3429. case cloudprovider.CloudVMStatusStopped, api.VM_READY:
  3430. statusStr = api.VM_READY
  3431. case api.VM_BLOCK_STREAM, api.VM_BLOCK_STREAM_FAIL:
  3432. break
  3433. default:
  3434. if g.GetHypervisor() != api.HYPERVISOR_POD {
  3435. statusStr = api.VM_UNKNOWN
  3436. }
  3437. }
  3438. // Do not override kickstart_installing with running
  3439. if statusStr == api.VM_RUNNING && g.Status == api.VM_KICKSTART_INSTALLING {
  3440. statusStr = g.Status
  3441. log.Infof("guest %s is installing, skip set running status", g.Name)
  3442. }
  3443. if !hasParentTask {
  3444. // migrating status hack
  3445. // not change migrating when:
  3446. // guest.Status is migrating and task not has parent task
  3447. if originStatus == api.VM_MIGRATING && statusStr == api.VM_RUNNING && len(g.ExternalId) == 0 {
  3448. statusStr = originStatus
  3449. }
  3450. }
  3451. input := api.ServerPerformStatusInput{
  3452. PerformStatusInput: resp.PerformStatusInput,
  3453. }
  3454. input.Status = statusStr
  3455. if _, err := g.PerformStatus(ctx, userCred, nil, input); err != nil {
  3456. return errors.Wrapf(err, "perform status of %s", jsonutils.Marshal(resp))
  3457. }
  3458. return nil
  3459. }
  3460. func (m *SGuestManager) PerformUploadStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.HostUploadGuestsStatusInput) (*api.GuestUploadStatusesResponse, error) {
  3461. out := &api.GuestUploadStatusesResponse{
  3462. Guests: make(map[string]*api.GuestUploadStatusResponse),
  3463. }
  3464. for id, status := range input.Guests {
  3465. gst := m.FetchGuestById(id)
  3466. if gst == nil {
  3467. out.Guests[id] = &api.GuestUploadStatusResponse{
  3468. Error: "not found guest",
  3469. }
  3470. continue
  3471. }
  3472. resp, err := gst.PerformUploadStatus(ctx, userCred, query, *status)
  3473. out.Guests[id] = resp
  3474. if err != nil {
  3475. resp.Error = err.Error()
  3476. continue
  3477. }
  3478. }
  3479. return out, nil
  3480. }
  3481. func (self *SGuest) PerformUploadStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUploadGuestStatusInput) (*api.GuestUploadStatusResponse, error) {
  3482. err := self.SetStatusFromHost(ctx, userCred, input, false, "")
  3483. if err != nil {
  3484. return nil, errors.Wrap(err, "set status from host")
  3485. }
  3486. resp := &api.GuestUploadStatusResponse{
  3487. OK: true,
  3488. Containers: make(map[string]*api.GuestUploadContainerStatusResponse),
  3489. }
  3490. for cId, cStatus := range input.Containers {
  3491. ctr, err := GetContainerManager().FetchById(cId)
  3492. if err != nil {
  3493. resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
  3494. Error: err.Error(),
  3495. }
  3496. continue
  3497. }
  3498. if _, err := ctr.(*SContainer).PerformStatus(ctx, userCred, query, *cStatus); err != nil {
  3499. resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
  3500. Error: err.Error(),
  3501. }
  3502. } else {
  3503. resp.Containers[cId] = &api.GuestUploadContainerStatusResponse{
  3504. OK: true,
  3505. }
  3506. }
  3507. }
  3508. return resp, nil
  3509. }
  3510. // 同步状态
  3511. func (self *SGuest) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerPerformStatusInput) (jsonutils.JSONObject, error) {
  3512. if input.HostId != "" && self.BackupHostId != "" && input.HostId == self.BackupHostId {
  3513. // perform status called from slave guest
  3514. return nil, self.SetBackupGuestStatus(userCred, input.Status, input.Reason)
  3515. }
  3516. if input.HostId != "" && input.HostId != self.HostId {
  3517. // perform status called from volatile host, eg: migrate dest host
  3518. return nil, nil
  3519. }
  3520. if input.PowerStates != "" {
  3521. if err := self.SetPowerStates(input.PowerStates); err != nil {
  3522. return nil, errors.Wrap(err, "set power states")
  3523. }
  3524. }
  3525. // Do not override kickstart_installing with running
  3526. if input.Status == api.VM_RUNNING && self.Status == api.VM_KICKSTART_INSTALLING {
  3527. log.Debugf("guest %s is in kickstart_installing state, skip set running status", self.Name)
  3528. return nil, nil
  3529. }
  3530. preStatus := self.Status
  3531. _, err := self.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input.PerformStatusInput)
  3532. if err != nil {
  3533. return nil, errors.Wrap(err, "SVirtualResourceBase.PerformStatus")
  3534. }
  3535. if self.HasBackupGuest() {
  3536. if input.Status == api.VM_READY {
  3537. if err := self.ResetGuestQuorumChildIndex(ctx, userCred); err != nil {
  3538. return nil, errors.Wrap(err, "reset guest quorum child index")
  3539. }
  3540. }
  3541. }
  3542. if input.Status == api.VM_RUNNING && input.BlockJobsCount == 0 {
  3543. if ispId := self.GetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, userCred); len(ispId) > 0 {
  3544. var disksMerged = true
  3545. disks, _ := self.GetDisks()
  3546. for _, disk := range disks {
  3547. if disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
  3548. disksMerged = false
  3549. }
  3550. }
  3551. if disksMerged {
  3552. ispM, err := InstanceSnapshotManager.FetchById(ispId)
  3553. if err == nil {
  3554. isp := ispM.(*SInstanceSnapshot)
  3555. isp.DecRefCount(ctx, userCred)
  3556. }
  3557. self.SetMetadata(ctx, api.BASE_INSTANCE_SNAPSHOT_ID, "", userCred)
  3558. }
  3559. }
  3560. }
  3561. if preStatus != self.Status && !self.isNotRunningStatus(preStatus) && self.isNotRunningStatus(self.Status) {
  3562. db.OpsLog.LogEvent(self, db.ACT_STOP, "", userCred)
  3563. if self.Status == api.VM_READY && !self.DisableDelete.Bool() && self.ShutdownBehavior == api.SHUTDOWN_TERMINATE {
  3564. err = self.StartAutoDeleteGuestTask(ctx, userCred, "")
  3565. return nil, err
  3566. }
  3567. }
  3568. for cId, cStatus := range input.Containers {
  3569. ctr, err := GetContainerManager().FetchById(cId)
  3570. if err != nil {
  3571. return nil, errors.Wrapf(err, "GetContainerManager(%s)", cId)
  3572. }
  3573. if _, err := ctr.(*SContainer).PerformStatus(ctx, userCred, query, *cStatus); err != nil {
  3574. return nil, errors.Wrapf(err, "PerformStatus(%s) of container", cId)
  3575. }
  3576. }
  3577. return nil, nil
  3578. }
  3579. // 关机
  3580. func (self *SGuest) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  3581. input api.ServerStopInput) (jsonutils.JSONObject, error) {
  3582. drv, err := self.GetDriver()
  3583. if err != nil {
  3584. return nil, errors.Wrap(err, "GetDriver")
  3585. }
  3586. if !input.IsForce {
  3587. err := drv.CanStop(self)
  3588. if err != nil {
  3589. return nil, errors.Wrap(err, "Cannot stop server")
  3590. }
  3591. }
  3592. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  3593. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  3594. }
  3595. return nil, self.StartGuestStopTask(ctx, userCred, input.TimeoutSecs, input.IsForce, input.StopCharging, "")
  3596. }
  3597. // 冻结虚拟机
  3598. func (self *SGuest) PerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) (jsonutils.JSONObject, error) {
  3599. if self.Freezed {
  3600. return nil, httperrors.NewBadRequestError("virtual resource already freezed")
  3601. }
  3602. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_STOP_FAILED}) {
  3603. return nil, self.StartGuestStopAndFreezeTask(ctx, userCred)
  3604. } else {
  3605. return self.SVirtualResourceBase.PerformFreeze(ctx, userCred, query, input)
  3606. }
  3607. }
  3608. func (self *SGuest) StartGuestStopAndFreezeTask(ctx context.Context, userCred mcclient.TokenCredential) error {
  3609. self.SetStatus(ctx, userCred, api.VM_START_STOP, "")
  3610. task, err := taskman.TaskManager.NewTask(ctx, "GuestStopAndFreezeTask", self, userCred, nil, "", "", nil)
  3611. if err != nil {
  3612. return err
  3613. }
  3614. task.ScheduleRun(nil)
  3615. return nil
  3616. }
  3617. // 重启
  3618. func (self *SGuest) PerformRestart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3619. isForce := jsonutils.QueryBoolean(data, "is_force", false)
  3620. 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) {
  3621. driver, err := self.GetDriver()
  3622. if err != nil {
  3623. return nil, err
  3624. }
  3625. return nil, driver.StartGuestRestartTask(self, ctx, userCred, isForce, "")
  3626. }
  3627. return nil, httperrors.NewInvalidStatusError("Cannot do restart server in status %s", self.Status)
  3628. }
  3629. // 发送远程命令
  3630. func (self *SGuest) PerformSendkeys(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3631. if self.Hypervisor != api.HYPERVISOR_KVM {
  3632. return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
  3633. }
  3634. if self.Status != api.VM_RUNNING {
  3635. return nil, httperrors.NewInvalidStatusError("Cannot send keys in status %s", self.Status)
  3636. }
  3637. keys, err := data.GetString("keys")
  3638. if err != nil {
  3639. log.Errorln(err)
  3640. return nil, httperrors.NewMissingParameterError("keys")
  3641. }
  3642. err = self.VerifySendKeys(keys)
  3643. if err != nil {
  3644. return nil, httperrors.NewBadRequestError("%v", err)
  3645. }
  3646. cmd := fmt.Sprintf("sendkey %s", keys)
  3647. duration, err := data.Int("duration")
  3648. if err == nil {
  3649. cmd = fmt.Sprintf("%s %d", cmd, duration)
  3650. }
  3651. _, err = self.SendMonitorCommand(ctx, userCred, &api.ServerMonitorInput{COMMAND: cmd})
  3652. return nil, err
  3653. }
  3654. func (self *SGuest) VerifySendKeys(keyStr string) error {
  3655. keys := strings.Split(keyStr, "-")
  3656. for _, key := range keys {
  3657. if !self.IsLegalKey(key) {
  3658. return fmt.Errorf("Unknown key '%s'", key)
  3659. }
  3660. }
  3661. return nil
  3662. }
  3663. func (self *SGuest) IsLegalKey(key string) bool {
  3664. singleKeys := "1234567890abcdefghijklmnopqrstuvwxyz"
  3665. legalKeys := []string{"ctrl", "ctrl_r", "alt", "alt_r", "shift", "shift_r",
  3666. "delete", "esc", "insert", "print", "spc",
  3667. "f1", "f2", "f3", "f4", "f5", "f6",
  3668. "f7", "f8", "f9", "f10", "f11", "f12",
  3669. "home", "pgup", "pgdn", "end",
  3670. "up", "down", "left", "right",
  3671. "tab", "minus", "equal", "backspace", "backslash",
  3672. "bracket_left", "bracket_right", "backslash",
  3673. "semicolon", "apostrophe", "grave_accent", "ret",
  3674. "comma", "dot", "slash",
  3675. "caps_lock", "num_lock", "scroll_lock"}
  3676. if len(key) > 1 && !utils.IsInStringArray(key, legalKeys) {
  3677. return false
  3678. } else if len(key) == 1 && !strings.Contains(singleKeys, key) {
  3679. return false
  3680. }
  3681. return true
  3682. }
  3683. func (self *SGuest) SendMonitorCommand(ctx context.Context, userCred mcclient.TokenCredential, cmd *api.ServerMonitorInput) (jsonutils.JSONObject, error) {
  3684. host, _ := self.GetHost()
  3685. url := fmt.Sprintf("%s/servers/%s/monitor", host.ManagerUri, self.Id)
  3686. header := http.Header{}
  3687. header.Add("X-Auth-Token", userCred.GetTokenString())
  3688. body := jsonutils.NewDict()
  3689. body.Add(jsonutils.NewString(cmd.COMMAND), "cmd")
  3690. body.Add(jsonutils.NewBool(cmd.QMP), "qmp")
  3691. _, res, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
  3692. if err != nil {
  3693. return nil, err
  3694. }
  3695. ret := res.(*jsonutils.JSONDict)
  3696. return ret, nil
  3697. }
  3698. // 绑定EIP
  3699. func (self *SGuest) PerformAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerAssociateEipInput) (jsonutils.JSONObject, error) {
  3700. err := self.IsEipAssociable()
  3701. if err != nil {
  3702. return nil, httperrors.NewGeneralError(err)
  3703. }
  3704. eipStr := input.EipId
  3705. if len(eipStr) == 0 {
  3706. return nil, httperrors.NewMissingParameterError("eip_id")
  3707. }
  3708. eipObj, err := ElasticipManager.FetchByIdOrName(ctx, userCred, eipStr)
  3709. if err != nil {
  3710. if err == sql.ErrNoRows {
  3711. return nil, httperrors.NewResourceNotFoundError("eip %s not found", eipStr)
  3712. } else {
  3713. return nil, httperrors.NewGeneralError(err)
  3714. }
  3715. }
  3716. eip := eipObj.(*SElasticip)
  3717. eipRegion, err := eip.GetRegion()
  3718. if err != nil {
  3719. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "eip.GetRegion"))
  3720. }
  3721. instRegion, _ := self.getRegion()
  3722. if eip.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  3723. return nil, httperrors.NewUnsupportOperationError("fixed eip cannot be associated")
  3724. }
  3725. if eip.IsAssociated() {
  3726. return nil, httperrors.NewConflictError("eip has been associated")
  3727. }
  3728. if eipRegion.Id != instRegion.Id {
  3729. return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different region")
  3730. }
  3731. if len(eip.NetworkId) > 0 {
  3732. gns, err := self.GetNetworks("")
  3733. if err != nil {
  3734. return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
  3735. }
  3736. for _, gn := range gns {
  3737. if gn.NetworkId == eip.NetworkId {
  3738. return nil, httperrors.NewInputParameterError("cannot associate eip with same network")
  3739. }
  3740. }
  3741. }
  3742. eipZone, _ := eip.GetZone()
  3743. if eipZone != nil {
  3744. insZone, _ := self.getZone()
  3745. if eipZone.Id != insZone.Id {
  3746. return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different zone")
  3747. }
  3748. }
  3749. host, _ := self.GetHost()
  3750. if host == nil {
  3751. return nil, httperrors.NewInputParameterError("server host is not found???")
  3752. }
  3753. if host.ManagerId != eip.ManagerId {
  3754. return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different provider")
  3755. }
  3756. self.SetStatus(ctx, userCred, api.INSTANCE_ASSOCIATE_EIP, "associate eip")
  3757. params := jsonutils.NewDict()
  3758. params.Add(jsonutils.NewString(self.ExternalId), "instance_external_id")
  3759. params.Add(jsonutils.NewString(self.Id), "instance_id")
  3760. params.Add(jsonutils.NewString(api.EIP_ASSOCIATE_TYPE_SERVER), "instance_type")
  3761. if len(input.IpAddr) > 0 {
  3762. params.Add(jsonutils.NewString(input.IpAddr), "ip_addr")
  3763. }
  3764. err = eip.StartEipAssociateTask(ctx, userCred, params, "")
  3765. return nil, err
  3766. }
  3767. // 解绑EIP
  3768. func (self *SGuest) PerformDissociateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDissociateEipInput) (jsonutils.JSONObject, error) {
  3769. eip, err := self.GetElasticIp()
  3770. if err != nil {
  3771. log.Errorf("Fail to get Eip %s", err)
  3772. return nil, httperrors.NewGeneralError(err)
  3773. }
  3774. if eip == nil {
  3775. return nil, httperrors.NewInvalidStatusError("No eip to dissociate")
  3776. }
  3777. err = db.IsObjectRbacAllowed(ctx, eip, userCred, policy.PolicyActionGet)
  3778. if err != nil {
  3779. return nil, errors.Wrap(err, "eip is not accessible")
  3780. }
  3781. self.SetStatus(ctx, userCred, api.INSTANCE_DISSOCIATE_EIP, "associate eip")
  3782. autoDelete := (input.AudoDelete != nil && *input.AudoDelete)
  3783. err = eip.StartEipDissociateTask(ctx, userCred, autoDelete, "")
  3784. if err != nil {
  3785. log.Errorf("fail to start dissociate task %s", err)
  3786. return nil, httperrors.NewGeneralError(err)
  3787. }
  3788. return nil, nil
  3789. }
  3790. // 创建EIP
  3791. func (self *SGuest) PerformCreateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerCreateEipInput) (jsonutils.JSONObject, error) {
  3792. var (
  3793. host, _ = self.GetHost()
  3794. region, _ = host.GetRegion()
  3795. regionDriver = region.GetDriver()
  3796. bw = input.Bandwidth
  3797. chargeType = input.ChargeType
  3798. bgpType = input.BgpType
  3799. autoDellocate = (input.AutoDellocate != nil && *input.AutoDellocate)
  3800. )
  3801. err := self.IsEipAssociable()
  3802. if err != nil {
  3803. return nil, httperrors.NewGeneralError(err)
  3804. }
  3805. if len(chargeType) == 0 {
  3806. chargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
  3807. }
  3808. if chargeType == billing_api.NET_CHARGE_TYPE_BY_BANDWIDTH {
  3809. if bw == 0 {
  3810. return nil, httperrors.NewMissingParameterError("bandwidth")
  3811. }
  3812. }
  3813. driver, err := self.GetDriver()
  3814. if err != nil {
  3815. return nil, err
  3816. }
  3817. err = driver.ValidateCreateEip(ctx, userCred, input)
  3818. if err != nil {
  3819. return nil, err
  3820. }
  3821. eipPendingUsage := &SRegionQuota{Eip: 1}
  3822. keys, err := self.GetRegionalQuotaKeys()
  3823. if err != nil {
  3824. return nil, err
  3825. }
  3826. eipPendingUsage.SetKeys(keys)
  3827. err = quotas.CheckSetPendingQuota(ctx, userCred, eipPendingUsage)
  3828. if err != nil {
  3829. return nil, httperrors.NewOutOfQuotaError("Out of eip quota: %s", err)
  3830. }
  3831. eip, err := ElasticipManager.NewEipForVMOnHost(ctx, userCred, &NewEipForVMOnHostArgs{
  3832. Bandwidth: int(bw),
  3833. BgpType: bgpType,
  3834. ChargeType: chargeType,
  3835. AutoDellocate: autoDellocate,
  3836. Guest: self,
  3837. Host: host,
  3838. PendingUsage: eipPendingUsage,
  3839. })
  3840. if err != nil {
  3841. quotas.CancelPendingUsage(ctx, userCred, eipPendingUsage, eipPendingUsage, false)
  3842. return nil, httperrors.NewGeneralError(err)
  3843. }
  3844. opts := api.ElasticipAssociateInput{
  3845. InstanceId: self.Id,
  3846. InstanceExternalId: self.ExternalId,
  3847. InstanceType: api.EIP_ASSOCIATE_TYPE_SERVER,
  3848. }
  3849. err = eip.AllocateAndAssociateInstance(ctx, userCred, self, opts, "")
  3850. if err != nil {
  3851. return nil, httperrors.NewGeneralError(err)
  3852. }
  3853. return nil, nil
  3854. }
  3855. func (self *SGuest) setUserData(ctx context.Context, userCred mcclient.TokenCredential, data string) error {
  3856. if err := userdata.ValidateUserdata(data, self.OsType); err != nil {
  3857. return err
  3858. }
  3859. encodeData, err := userdata.Encode(data)
  3860. if err != nil {
  3861. return errors.Wrap(err, "encode guest userdata")
  3862. }
  3863. err = self.SetMetadata(ctx, "user_data", encodeData, userCred)
  3864. if err != nil {
  3865. return err
  3866. }
  3867. return nil
  3868. }
  3869. func (self *SGuest) GetUserData(ctx context.Context, userCred mcclient.TokenCredential) string {
  3870. userData := self.GetMetadata(ctx, "user_data", userCred)
  3871. if len(userData) == 0 {
  3872. return userData
  3873. }
  3874. decodeData, _ := userdata.Decode(userData)
  3875. return decodeData
  3876. }
  3877. // 更新自定义脚本数据
  3878. func (self *SGuest) PerformUserData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerUserDataInput) (jsonutils.JSONObject, error) {
  3879. if len(input.UserData) == 0 {
  3880. return nil, httperrors.NewMissingParameterError("user_data")
  3881. }
  3882. // validate UserData
  3883. if err := userdata.ValidateUserdata(input.UserData, self.OsType); err != nil {
  3884. return nil, httperrors.NewInputParameterError("Invalid userdata: %v", err)
  3885. }
  3886. err := self.setUserData(ctx, userCred, input.UserData)
  3887. if err != nil {
  3888. return nil, httperrors.NewGeneralError(err)
  3889. }
  3890. if len(self.HostId) > 0 {
  3891. return nil, self.StartSyncTask(ctx, userCred, false, "")
  3892. }
  3893. return nil, nil
  3894. }
  3895. // 设置Qemu参数
  3896. func (self *SGuest) PerformSetQemuParams(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  3897. isaSerial, err := data.GetString("disable_isa_serial")
  3898. if err == nil {
  3899. err = self.SetMetadata(ctx, "disable_isa_serial", isaSerial, userCred)
  3900. if err != nil {
  3901. return nil, err
  3902. }
  3903. }
  3904. pvpanic, err := data.GetString("disable_pvpanic")
  3905. if err == nil {
  3906. err = self.SetMetadata(ctx, "disable_pvpanic", pvpanic, userCred)
  3907. if err != nil {
  3908. return nil, err
  3909. }
  3910. }
  3911. usbKbd, err := data.GetString("disable_usb_kbd")
  3912. if err == nil {
  3913. err = self.SetMetadata(ctx, "disable_usb_kbd", usbKbd, userCred)
  3914. if err != nil {
  3915. return nil, err
  3916. }
  3917. }
  3918. usbContType, err := data.GetString("usb_controller_type")
  3919. if err == nil {
  3920. err = self.SetMetadata(ctx, "usb_controller_type", usbContType, userCred)
  3921. if err != nil {
  3922. return nil, err
  3923. }
  3924. }
  3925. return nil, nil
  3926. }
  3927. func (self *SGuest) SwitchToBackup(userCred mcclient.TokenCredential) error {
  3928. diff, err := db.Update(self, func() error {
  3929. self.HostId, self.BackupHostId = self.BackupHostId, self.HostId
  3930. return nil
  3931. })
  3932. if err != nil {
  3933. return err
  3934. }
  3935. db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
  3936. return nil
  3937. }
  3938. // 主备机切换
  3939. func (self *SGuest) PerformSwitchToBackup(
  3940. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
  3941. ) (jsonutils.JSONObject, error) {
  3942. if self.Status == api.VM_BLOCK_STREAM {
  3943. return nil, httperrors.NewBadRequestError("Cannot swith to backup when guest in status %s", self.Status)
  3944. }
  3945. if len(self.BackupHostId) == 0 {
  3946. return nil, httperrors.NewBadRequestError("Guest no backup host")
  3947. }
  3948. backupHost := HostManager.FetchHostById(self.BackupHostId)
  3949. if backupHost.HostStatus != api.HOST_ONLINE {
  3950. return nil, httperrors.NewBadRequestError("Can't switch to backup host on host status %s", backupHost.HostStatus)
  3951. }
  3952. if !self.IsGuestBackupMirrorJobReady(ctx, userCred) {
  3953. return nil, httperrors.NewBadRequestError("Guest can't switch to backup, mirror job not ready")
  3954. }
  3955. if !utils.IsInStringArray(self.BackupGuestStatus, []string{api.VM_RUNNING, api.VM_READY, api.VM_UNKNOWN}) {
  3956. return nil, httperrors.NewInvalidStatusError("Guest can't switch to backup with backup status %s", self.BackupGuestStatus)
  3957. }
  3958. oldStatus := self.Status
  3959. taskData := jsonutils.NewDict()
  3960. taskData.Set("old_status", jsonutils.NewString(oldStatus))
  3961. taskData.Set("auto_start", jsonutils.NewBool(jsonutils.QueryBoolean(data, "auto_start", false)))
  3962. if task, err := taskman.TaskManager.NewTask(ctx, "GuestSwitchToBackupTask", self, userCred, taskData, "", "", nil); err != nil {
  3963. log.Errorln(err)
  3964. return nil, err
  3965. } else {
  3966. self.SetStatus(ctx, userCred, api.VM_SWITCH_TO_BACKUP, "Switch to backup")
  3967. task.ScheduleRun(nil)
  3968. }
  3969. return nil, nil
  3970. }
  3971. func (manager *SGuestManager) getGuests(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) ([]SGuest, error) {
  3972. _guests := []string{}
  3973. data.Unmarshal(&_guests, "guests")
  3974. if len(_guests) == 0 {
  3975. return nil, httperrors.NewMissingParameterError("guests")
  3976. }
  3977. guests := []SGuest{}
  3978. q1 := manager.Query().In("id", _guests)
  3979. q2 := manager.Query().In("name", _guests)
  3980. q2 = manager.FilterByOwner(ctx, q2, manager, userCred, userCred, manager.NamespaceScope())
  3981. q2 = manager.FilterBySystemAttributes(q2, userCred, data, manager.ResourceScope())
  3982. q := sqlchemy.Union(q1, q2).Query().Distinct()
  3983. err := db.FetchModelObjects(manager, q, &guests)
  3984. if err != nil {
  3985. return nil, err
  3986. }
  3987. guestStr := []string{}
  3988. for _, guest := range guests {
  3989. guestStr = append(guestStr, guest.Id)
  3990. guestStr = append(guestStr, guest.Name)
  3991. }
  3992. for _, guest := range _guests {
  3993. if !utils.IsInStringArray(guest, guestStr) {
  3994. return nil, httperrors.NewResourceNotFoundError("failed to found guest %s", guest)
  3995. }
  3996. }
  3997. return guests, nil
  3998. }
  3999. func (manager *SGuestManager) getUserMetadata(data jsonutils.JSONObject) (map[string]string, error) {
  4000. if !data.Contains("metadata") {
  4001. return nil, httperrors.NewMissingParameterError("metadata")
  4002. }
  4003. metadata, err := data.GetMap("metadata")
  4004. if err != nil {
  4005. return nil, httperrors.NewInputParameterError("input data not key value dict")
  4006. }
  4007. dictStore := make(map[string]string)
  4008. for k, v := range metadata {
  4009. dictStore[db.USER_TAG_PREFIX+k], _ = v.GetString()
  4010. }
  4011. return dictStore, nil
  4012. }
  4013. func (manager *SGuestManager) PerformBatchUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4014. guests, err := manager.getGuests(ctx, userCred, data)
  4015. if err != nil {
  4016. return nil, err
  4017. }
  4018. metadata, err := manager.getUserMetadata(data)
  4019. if err != nil {
  4020. return nil, err
  4021. }
  4022. for _, guest := range guests {
  4023. err := guest.SetUserMetadataValues(ctx, metadata, userCred)
  4024. if err != nil {
  4025. msg := fmt.Errorf("set guest %s(%s) user-metadata error: %v", guest.Name, guest.Id, err)
  4026. return nil, httperrors.NewGeneralError(msg)
  4027. }
  4028. }
  4029. return jsonutils.Marshal(guests), nil
  4030. }
  4031. func (manager *SGuestManager) PerformBatchSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4032. guests, err := manager.getGuests(ctx, userCred, data)
  4033. if err != nil {
  4034. return nil, err
  4035. }
  4036. metadata, err := manager.getUserMetadata(data)
  4037. if err != nil {
  4038. return nil, err
  4039. }
  4040. for _, guest := range guests {
  4041. err := guest.SetUserMetadataAll(ctx, metadata, userCred)
  4042. if err != nil {
  4043. msg := fmt.Errorf("set guest %s(%s) user-metadata error: %v", guest.Name, guest.Id, err)
  4044. return nil, httperrors.NewGeneralError(msg)
  4045. }
  4046. }
  4047. return jsonutils.Marshal(guests), nil
  4048. }
  4049. func (self *SGuest) PerformBlockStreamFailed(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4050. if len(self.BackupHostId) > 0 {
  4051. if err := self.SetGuestBackupMirrorJobFailed(ctx, userCred); err != nil {
  4052. return nil, errors.Wrap(err, "set guest backup mirror job failed")
  4053. }
  4054. }
  4055. if self.Status == api.VM_BLOCK_STREAM || self.Status == api.VM_RUNNING {
  4056. reason, _ := data.GetString("reason")
  4057. logclient.AddSimpleActionLog(self, logclient.ACT_VM_BLOCK_STREAM, reason, userCred, false)
  4058. return nil, self.SetStatus(ctx, userCred, api.VM_BLOCK_STREAM_FAIL, reason)
  4059. }
  4060. return nil, nil
  4061. }
  4062. func (self *SGuest) PerformSlaveBlockStreamReady(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4063. if len(self.BackupHostId) > 0 {
  4064. if err := self.TrySetGuestBackupMirrorJobReady(ctx, userCred); err != nil {
  4065. return nil, errors.Wrap(err, "set guest backup mirror job status ready")
  4066. }
  4067. self.SetBackupGuestStatus(userCred, api.VM_RUNNING, "perform slave block stream ready")
  4068. }
  4069. return nil, nil
  4070. }
  4071. func (self *SGuest) PerformBlockMirrorReady(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4072. if self.Status == api.VM_BLOCK_STREAM || self.Status == api.VM_RUNNING {
  4073. diskId, err := data.GetString("disk_id")
  4074. if err != nil {
  4075. return nil, httperrors.NewMissingParameterError("disk_id")
  4076. }
  4077. log.Infof("disk_id %s", diskId)
  4078. disk := DiskManager.FetchDiskById(diskId)
  4079. if disk == nil {
  4080. return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
  4081. }
  4082. if taskId := disk.GetMetadata(ctx, api.DISK_CLONE_TASK_ID, userCred); len(taskId) > 0 {
  4083. log.Infof("task_id %s", taskId)
  4084. if err := self.startSwitchToClonedDisk(ctx, userCred, taskId); err != nil {
  4085. return nil, errors.Wrap(err, "startSwitchToClonedDisk")
  4086. }
  4087. }
  4088. }
  4089. return nil, nil
  4090. }
  4091. func (self *SGuest) StartMirrorJob(ctx context.Context, userCred mcclient.TokenCredential, nbdServerPort int64, parentTaskId string) error {
  4092. taskData := jsonutils.NewDict()
  4093. taskData.Set("nbd_server_port", jsonutils.NewInt(nbdServerPort))
  4094. if task, err := taskman.TaskManager.NewTask(
  4095. ctx, "GuestReSyncToBackup", self, userCred, taskData, parentTaskId, "", nil); err != nil {
  4096. log.Errorln(err)
  4097. return err
  4098. } else {
  4099. task.ScheduleRun(nil)
  4100. }
  4101. return nil
  4102. }
  4103. func (manager *SGuestManager) PerformDirtyServerStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4104. guestId, err := data.GetString("guest_id")
  4105. if err != nil {
  4106. return nil, httperrors.NewMissingParameterError("guest_id")
  4107. }
  4108. guest := manager.FetchGuestById(guestId)
  4109. if guest == nil {
  4110. return nil, httperrors.NewNotFoundError("Guest %s not found", guestId)
  4111. }
  4112. hostId, _ := data.GetString("host_id")
  4113. if len(hostId) == 0 {
  4114. return nil, httperrors.NewMissingParameterError("host_id")
  4115. }
  4116. if guest.HostId == hostId {
  4117. // master guest
  4118. err := guest.StartGueststartTask(ctx, userCred, nil, "")
  4119. return nil, err
  4120. } else if guest.BackupHostId == hostId {
  4121. // slave guest
  4122. err := guest.GuestStartAndSyncToBackup(ctx, userCred, "", guest.Status)
  4123. return nil, err
  4124. }
  4125. return nil, nil
  4126. }
  4127. func (manager *SGuestManager) PerformDirtyServerVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4128. guestId, err := data.GetString("guest_id")
  4129. if err != nil {
  4130. return nil, httperrors.NewMissingParameterError("guest_id")
  4131. }
  4132. iguest, err := manager.FetchById(guestId)
  4133. if err == sql.ErrNoRows {
  4134. ret := jsonutils.NewDict()
  4135. ret.Set("guest_unknown_need_clean", jsonutils.JSONTrue)
  4136. return ret, nil
  4137. } else {
  4138. if err != nil {
  4139. return nil, httperrors.NewInternalServerError("Fetch guest error %s", err)
  4140. } else {
  4141. guest := iguest.(*SGuest)
  4142. hostId, _ := data.GetString("host_id")
  4143. if len(hostId) == 0 {
  4144. return nil, httperrors.NewMissingParameterError("host_id")
  4145. }
  4146. if guest.HostId != hostId && guest.BackupHostId != hostId {
  4147. return nil, guest.StartGuestDeleteOnHostTask(ctx, userCred, hostId, false, "")
  4148. }
  4149. }
  4150. }
  4151. return nil, nil
  4152. }
  4153. func (self *SGuest) StartGuestDeleteOnHostTask(ctx context.Context, userCred mcclient.TokenCredential, hostId string, purge bool, parentTaskId string) error {
  4154. taskData := jsonutils.NewDict()
  4155. taskData.Set("host_id", jsonutils.NewString(hostId))
  4156. taskData.Set("purge", jsonutils.NewBool(purge))
  4157. if task, err := taskman.TaskManager.NewTask(
  4158. ctx, "GuestDeleteOnHostTask", self, userCred, taskData, parentTaskId, "", nil); err != nil {
  4159. log.Errorln(err)
  4160. return err
  4161. } else {
  4162. task.ScheduleRun(nil)
  4163. }
  4164. return nil
  4165. }
  4166. func (guest *SGuest) GuestStartAndSyncToBackup(
  4167. ctx context.Context, userCred mcclient.TokenCredential, parentTaskId, guestStatus string,
  4168. ) error {
  4169. data := jsonutils.NewDict()
  4170. data.Set("guest_status", jsonutils.NewString(guestStatus))
  4171. task, err := taskman.TaskManager.NewTask(
  4172. ctx, "GuestStartAndSyncToBackupTask", guest, userCred, data, parentTaskId, "", nil)
  4173. if err != nil {
  4174. log.Errorln(err)
  4175. return err
  4176. } else {
  4177. task.ScheduleRun(nil)
  4178. }
  4179. return nil
  4180. }
  4181. func (self *SGuest) guestDisksStorageTypeIsLocal() bool {
  4182. disks, _ := self.GetDisks()
  4183. for _, disk := range disks {
  4184. storage, _ := disk.GetStorage()
  4185. if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
  4186. return false
  4187. }
  4188. }
  4189. return true
  4190. }
  4191. func (self *SGuest) guestDisksStorageTypeIsShared() bool {
  4192. disks, _ := self.GetDisks()
  4193. for _, disk := range disks {
  4194. storage, _ := disk.GetStorage()
  4195. if storage.StorageType == api.STORAGE_LOCAL || storage.StorageType == api.STORAGE_LVM {
  4196. return false
  4197. }
  4198. }
  4199. return true
  4200. }
  4201. // 创建备机
  4202. func (self *SGuest) PerformCreateBackup(
  4203. ctx context.Context, userCred mcclient.TokenCredential,
  4204. query jsonutils.JSONObject, data jsonutils.JSONObject,
  4205. ) (jsonutils.JSONObject, error) {
  4206. if len(self.BackupHostId) > 0 {
  4207. return nil, httperrors.NewBadRequestError("Already have backup server")
  4208. }
  4209. if self.Status != api.VM_READY {
  4210. return nil, httperrors.NewBadRequestError("Can't create backup in guest status %s", self.Status)
  4211. }
  4212. if !self.guestDisksStorageTypeIsLocal() {
  4213. return nil, httperrors.NewBadRequestError("Cannot create backup with shared storage")
  4214. }
  4215. if self.Hypervisor != api.HYPERVISOR_KVM {
  4216. return nil, httperrors.NewBadRequestError("Backup only support hypervisor kvm")
  4217. }
  4218. devs, _ := self.GetIsolatedDevices()
  4219. if len(devs) > 0 {
  4220. return nil, httperrors.NewBadRequestError("Cannot create backup with isolated devices")
  4221. }
  4222. hasSnapshot, err := self.GuestDisksHasSnapshot()
  4223. if err != nil {
  4224. return nil, httperrors.NewInternalServerError("GuestDisksHasSnapshot fail %s", err)
  4225. }
  4226. if hasSnapshot {
  4227. return nil, httperrors.NewBadRequestError("Cannot create backup with snapshot")
  4228. }
  4229. return self.StartGuestCreateBackupTask(ctx, userCred, "", data)
  4230. }
  4231. func (self *SGuest) StartGuestCreateBackupTask(
  4232. ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, data jsonutils.JSONObject,
  4233. ) (jsonutils.JSONObject, error) {
  4234. req := self.getGuestBackupResourceRequirements(ctx, userCred)
  4235. keys, err := self.GetQuotaKeys()
  4236. if err != nil {
  4237. return nil, err
  4238. }
  4239. req.SetKeys(keys)
  4240. err = quotas.CheckSetPendingQuota(ctx, userCred, &req)
  4241. if err != nil {
  4242. return nil, httperrors.NewOutOfQuotaError("%v", err)
  4243. }
  4244. params := data.(*jsonutils.JSONDict)
  4245. params.Set("guest_status", jsonutils.NewString(self.Status))
  4246. self.SetStatus(ctx, userCred, api.VM_BACKUP_CREATING, "")
  4247. task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateBackupTask", self, userCred, params, parentTaskId, "", &req)
  4248. if err != nil {
  4249. quotas.CancelPendingUsage(ctx, userCred, &req, &req, false)
  4250. log.Errorln(err)
  4251. return nil, err
  4252. } else {
  4253. task.ScheduleRun(nil)
  4254. }
  4255. return nil, nil
  4256. }
  4257. // 删除备机
  4258. func (self *SGuest) PerformDeleteBackup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4259. if len(self.BackupHostId) == 0 {
  4260. return nil, httperrors.NewBadRequestError("Guest without backup")
  4261. }
  4262. backupHost := HostManager.FetchHostById(self.BackupHostId)
  4263. if backupHost == nil {
  4264. return nil, httperrors.NewNotFoundError("Guest backup host not found")
  4265. }
  4266. if backupHost.Status == api.HOST_OFFLINE && !jsonutils.QueryBoolean(data, "purge", false) {
  4267. return nil, httperrors.NewBadRequestError("Backup host is offline")
  4268. }
  4269. taskData := jsonutils.NewDict()
  4270. taskData.Set("purge", jsonutils.NewBool(jsonutils.QueryBoolean(data, "purge", false)))
  4271. taskData.Set("create", jsonutils.NewBool(jsonutils.QueryBoolean(data, "create", false)))
  4272. self.SetStatus(ctx, userCred, api.VM_DELETING_BACKUP, "delete backup server")
  4273. if task, err := taskman.TaskManager.NewTask(
  4274. ctx, "GuestDeleteBackupTask", self, userCred, taskData, "", "", nil); err != nil {
  4275. log.Errorln(err)
  4276. return nil, err
  4277. } else {
  4278. task.ScheduleRun(nil)
  4279. }
  4280. return nil, nil
  4281. }
  4282. func (self *SGuest) CreateBackupDisks(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  4283. if task, err := taskman.TaskManager.NewTask(ctx, "GuestCreateBackupDisksTask", self, userCred, nil, parentTaskId, "", nil); err != nil {
  4284. log.Errorln(err)
  4285. return err
  4286. } else {
  4287. task.ScheduleRun(nil)
  4288. }
  4289. return nil
  4290. }
  4291. func (self *SGuest) StartCreateBackup(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, kwargs *jsonutils.JSONDict) error {
  4292. if kwargs == nil {
  4293. kwargs = jsonutils.NewDict()
  4294. }
  4295. kwargs.Add(jsonutils.NewString("create"), "deploy_action")
  4296. if task, err := taskman.TaskManager.NewTask(ctx, "GuestDeployBackupTask", self, userCred, kwargs, parentTaskId, "", nil); err != nil {
  4297. log.Errorln(err)
  4298. return err
  4299. } else {
  4300. task.ScheduleRun(nil)
  4301. }
  4302. return nil
  4303. }
  4304. // 备机开机
  4305. func (self *SGuest) PerformStartBackup(
  4306. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
  4307. ) (jsonutils.JSONObject, error) {
  4308. if !self.HasBackupGuest() {
  4309. return nil, httperrors.NewBadRequestError("guest has no backup guest")
  4310. }
  4311. if host := HostManager.FetchHostById(self.BackupHostId); host.HostStatus != api.HOST_ONLINE {
  4312. return nil, httperrors.NewBadRequestError("can't start backup guest on host status %s", host.HostStatus)
  4313. }
  4314. if self.Status != api.VM_RUNNING || self.BackupGuestStatus == api.VM_RUNNING {
  4315. return nil, httperrors.NewBadRequestError("can't start backup guest on backup guest status %s", self.BackupGuestStatus)
  4316. }
  4317. return nil, self.GuestStartAndSyncToBackup(ctx, userCred, "", self.Status)
  4318. }
  4319. func (self *SGuest) PerformSetExtraOption(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetExtraOptionInput) (jsonutils.JSONObject, error) {
  4320. err := input.Validate()
  4321. if err != nil {
  4322. return nil, errors.Wrap(err, "input.Validate")
  4323. }
  4324. extraOptions := self.GetExtraOptions(ctx, userCred)
  4325. optVal := make([]string, 0)
  4326. extraOptions.Unmarshal(&optVal, input.Key)
  4327. if !utils.IsInStringArray(input.Value, optVal) {
  4328. optVal = append(optVal, input.Value)
  4329. }
  4330. extraOptions.Set(input.Key, jsonutils.Marshal(optVal))
  4331. return nil, self.SetExtraOptions(ctx, userCred, extraOptions)
  4332. }
  4333. func (self *SGuest) GetExtraOptions(ctx context.Context, userCred mcclient.TokenCredential) *jsonutils.JSONDict {
  4334. options := self.GetMetadataJson(ctx, "extra_options", userCred)
  4335. o, ok := options.(*jsonutils.JSONDict)
  4336. if ok {
  4337. return o
  4338. }
  4339. return jsonutils.NewDict()
  4340. }
  4341. func (self *SGuest) SetExtraOptions(ctx context.Context, userCred mcclient.TokenCredential, extraOptions *jsonutils.JSONDict) error {
  4342. return self.SetMetadata(ctx, "extra_options", extraOptions, userCred)
  4343. }
  4344. func (self *SGuest) PerformDelExtraOption(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerDelExtraOptionInput) (jsonutils.JSONObject, error) {
  4345. err := input.Validate()
  4346. if err != nil {
  4347. return nil, errors.Wrap(err, "input.Validate")
  4348. }
  4349. extraOptions := self.GetExtraOptions(ctx, userCred)
  4350. var newOpt []string
  4351. if len(input.Value) > 0 {
  4352. optVal := make([]string, 0)
  4353. extraOptions.Unmarshal(&optVal, input.Key)
  4354. for _, v := range optVal {
  4355. if v != input.Value {
  4356. newOpt = append(newOpt, v)
  4357. }
  4358. }
  4359. }
  4360. if len(newOpt) > 0 {
  4361. extraOptions.Set(input.Key, jsonutils.Marshal(newOpt))
  4362. } else if extraOptions.Contains(input.Key) {
  4363. extraOptions.Remove(input.Key)
  4364. }
  4365. return nil, self.SetExtraOptions(ctx, userCred, extraOptions)
  4366. }
  4367. // 取消定时删除
  4368. func (self *SGuest) PerformCancelExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4369. if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
  4370. return nil, httperrors.NewBadRequestError("guest billing type %s not support cancel expire", self.BillingType)
  4371. }
  4372. err := SaveReleaseAt(ctx, self, userCred, time.Time{})
  4373. if err != nil {
  4374. return nil, err
  4375. }
  4376. disks, err := self.GetDisks()
  4377. if err != nil {
  4378. return nil, err
  4379. }
  4380. for i := 0; i < len(disks); i += 1 {
  4381. if disks[i].BillingType == billing_api.BILLING_TYPE_POSTPAID {
  4382. err := SaveReleaseAt(ctx, &disks[i], userCred, time.Time{})
  4383. if err != nil {
  4384. return nil, err
  4385. }
  4386. }
  4387. }
  4388. return nil, nil
  4389. }
  4390. // 设置定时删除
  4391. func (self *SGuest) PerformPostpaidExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PostpaidExpireInput) (jsonutils.JSONObject, error) {
  4392. if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
  4393. return nil, httperrors.NewBadRequestError("guest billing type is %s", self.BillingType)
  4394. }
  4395. driver, err := self.GetDriver()
  4396. if err != nil {
  4397. return nil, err
  4398. }
  4399. if !driver.IsSupportPostpaidExpire() {
  4400. return nil, httperrors.NewBadRequestError("guest %s unsupport postpaid expire", self.Hypervisor)
  4401. }
  4402. releaseAt, err := input.GetReleaseAt()
  4403. if err != nil {
  4404. return nil, err
  4405. }
  4406. err = SaveReleaseAt(ctx, self, userCred, releaseAt)
  4407. if err != nil {
  4408. return nil, err
  4409. }
  4410. return nil, nil
  4411. }
  4412. // 续费
  4413. func (self *SGuest) PerformRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4414. durationStr, _ := data.GetString("duration")
  4415. if len(durationStr) == 0 {
  4416. return nil, httperrors.NewInputParameterError("missong duration")
  4417. }
  4418. bc, err := billing.ParseBillingCycle(durationStr)
  4419. if err != nil {
  4420. return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err)
  4421. }
  4422. driver, err := self.GetDriver()
  4423. if err != nil {
  4424. return nil, err
  4425. }
  4426. if !driver.IsSupportedBillingCycle(bc) {
  4427. return nil, httperrors.NewInputParameterError("unsupported duration %s", durationStr)
  4428. }
  4429. err = self.startGuestRenewTask(ctx, userCred, durationStr, "")
  4430. if err != nil {
  4431. return nil, err
  4432. }
  4433. return nil, nil
  4434. }
  4435. func (self *SGuest) GetStorages() ([]SStorage, error) {
  4436. q := StorageManager.Query().Distinct()
  4437. disks := DiskManager.Query().SubQuery()
  4438. guestdisks := GuestdiskManager.Query().Equals("guest_id", self.Id).SubQuery()
  4439. q = q.Join(disks, sqlchemy.Equals(disks.Field("storage_id"), q.Field("id")))
  4440. q = q.Join(guestdisks, sqlchemy.Equals(guestdisks.Field("disk_id"), disks.Field("id")))
  4441. ret := []SStorage{}
  4442. err := db.FetchModelObjects(StorageManager, q, &ret)
  4443. if err != nil {
  4444. return nil, err
  4445. }
  4446. return ret, nil
  4447. }
  4448. func (self *SGuest) startGuestRenewTask(ctx context.Context, userCred mcclient.TokenCredential, duration string, parentTaskId string) error {
  4449. self.SetStatus(ctx, userCred, api.VM_RENEWING, "")
  4450. data := jsonutils.NewDict()
  4451. data.Add(jsonutils.NewString(duration), "duration")
  4452. task, err := taskman.TaskManager.NewTask(ctx, "GuestRenewTask", self, userCred, data, parentTaskId, "", nil)
  4453. if err != nil {
  4454. log.Errorf("fail to crate GuestRenewTask %s", err)
  4455. return err
  4456. }
  4457. task.ScheduleRun(nil)
  4458. return nil
  4459. }
  4460. func (self *SGuest) SaveRenewInfo(
  4461. ctx context.Context, userCred mcclient.TokenCredential,
  4462. bc *billing.SBillingCycle, expireAt *time.Time, billingType billing_api.TBillingType,
  4463. ) error {
  4464. err := SaveRenewInfo(ctx, userCred, self, bc, expireAt, billingType)
  4465. if err != nil {
  4466. return err
  4467. }
  4468. disks, err := self.GetDisks()
  4469. if err != nil {
  4470. return err
  4471. }
  4472. for i := 0; i < len(disks); i += 1 {
  4473. if disks[i].AutoDelete {
  4474. err = SaveRenewInfo(ctx, userCred, &disks[i], bc, expireAt, billingType)
  4475. if err != nil {
  4476. return err
  4477. }
  4478. }
  4479. }
  4480. return nil
  4481. }
  4482. func (self *SGuest) PerformSetNetworkNumQueues(
  4483. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetNetworkNumQueuesInput,
  4484. ) (jsonutils.JSONObject, error) {
  4485. if self.Status != api.VM_READY {
  4486. return nil, httperrors.NewInvalidStatusError("can't set network num_queues on vm %s", self.Status)
  4487. }
  4488. if input.NumQueues < 1 {
  4489. return nil, httperrors.NewInputParameterError("invalid num_queues %d", input.NumQueues)
  4490. }
  4491. gn, err := self.GetGuestnetworkByMac(input.MacAddr)
  4492. if err != nil {
  4493. if errors.Cause(err) == sql.ErrNoRows {
  4494. return nil, httperrors.NewNotFoundError("guest network mac %s not found", input.MacAddr)
  4495. }
  4496. }
  4497. _, err = db.Update(gn, func() error {
  4498. gn.NumQueues = input.NumQueues
  4499. return nil
  4500. })
  4501. return nil, err
  4502. }
  4503. func (self *SGuest) PerformStreamDisksComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4504. disks, err := self.GetDisks()
  4505. if err != nil {
  4506. return nil, err
  4507. }
  4508. for _, disk := range disks {
  4509. if len(disk.SnapshotId) > 0 && disk.GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
  4510. SnapshotManager.AddRefCount(disk.SnapshotId, -1)
  4511. disk.SetMetadata(ctx, "merge_snapshot", jsonutils.JSONFalse, userCred)
  4512. }
  4513. if len(disk.GetMetadata(ctx, api.DISK_META_REMOTE_ACCESS_PATH, nil)) > 0 {
  4514. disk.SetMetadata(ctx, api.DISK_META_REMOTE_ACCESS_PATH, "", userCred)
  4515. }
  4516. }
  4517. return nil, nil
  4518. }
  4519. // 导入虚拟机
  4520. func (man *SGuestManager) PerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4521. desc := &api.SImportGuestDesc{}
  4522. if err := data.Unmarshal(desc); err != nil {
  4523. return nil, httperrors.NewInputParameterError("Invalid desc: %s", data.String())
  4524. }
  4525. if len(desc.Id) == 0 {
  4526. return nil, httperrors.NewInputParameterError("Server Id is empty")
  4527. }
  4528. if len(desc.Name) == 0 {
  4529. return nil, httperrors.NewInputParameterError("Server Name is empty")
  4530. }
  4531. if obj, _ := man.FetchByIdOrName(ctx, userCred, desc.Id); obj != nil {
  4532. return nil, httperrors.NewInputParameterError("Server %s already exists", desc.Id)
  4533. }
  4534. if err := db.NewNameValidator(ctx, man, userCred, desc.Name, nil); err != nil {
  4535. return nil, err
  4536. }
  4537. if hostObj, _ := HostManager.FetchByIdOrName(ctx, userCred, desc.HostId); hostObj == nil {
  4538. return nil, httperrors.NewNotFoundError("Host %s not found", desc.HostId)
  4539. } else {
  4540. desc.HostId = hostObj.GetId()
  4541. }
  4542. guset, err := man.DoImport(ctx, userCred, desc)
  4543. if err != nil {
  4544. return nil, err
  4545. }
  4546. return jsonutils.Marshal(guset), nil
  4547. }
  4548. func (man *SGuestManager) DoImport(
  4549. ctx context.Context, userCred mcclient.TokenCredential, desc *api.SImportGuestDesc,
  4550. ) (*SGuest, error) {
  4551. // 1. create import guest on host
  4552. gst, err := man.createImportGuest(ctx, userCred, desc)
  4553. if err != nil {
  4554. return nil, err
  4555. }
  4556. // 2. import networks
  4557. if err := gst.importNics(ctx, userCred, desc.Nics); err != nil {
  4558. return gst, err
  4559. }
  4560. // 3. import disks
  4561. if err := gst.importDisks(ctx, userCred, desc.Disks); err != nil {
  4562. return gst, err
  4563. }
  4564. // 4. set metadata
  4565. for k, v := range desc.Metadata {
  4566. gst.SetMetadata(ctx, k, v, userCred)
  4567. }
  4568. return gst, nil
  4569. }
  4570. func (man *SGuestManager) createImportGuest(ctx context.Context, userCred mcclient.TokenCredential, desc *api.SImportGuestDesc) (*SGuest, error) {
  4571. model, err := db.NewModelObject(man)
  4572. if err != nil {
  4573. return nil, httperrors.NewGeneralError(err)
  4574. }
  4575. gst, ok := model.(*SGuest)
  4576. if !ok {
  4577. return nil, httperrors.NewGeneralError(fmt.Errorf("Can't convert %#v to *SGuest model", model))
  4578. }
  4579. gst.ProjectId = userCred.GetProjectId()
  4580. gst.DomainId = userCred.GetProjectDomainId()
  4581. gst.IsSystem = desc.IsSystem
  4582. gst.Id = desc.Id
  4583. gst.Name = desc.Name
  4584. gst.HostId = desc.HostId
  4585. gst.Status = api.VM_IMPORT
  4586. gst.Hypervisor = desc.Hypervisor
  4587. gst.VmemSize = desc.MemSizeMb
  4588. gst.VcpuCount = desc.Cpu
  4589. gst.BootOrder = desc.BootOrder
  4590. gst.Description = desc.Description
  4591. err = man.TableSpec().Insert(ctx, gst)
  4592. return gst, err
  4593. }
  4594. func ToNetConfig(n *api.SImportNic, net *SNetwork) *api.NetworkConfig {
  4595. return &api.NetworkConfig{
  4596. Network: net.Id,
  4597. Wire: net.WireId,
  4598. Address: n.Ip,
  4599. Mac: n.Mac,
  4600. Driver: n.Driver,
  4601. BwLimit: n.BandWidth,
  4602. }
  4603. }
  4604. func (self *SGuest) importNics(ctx context.Context, userCred mcclient.TokenCredential, nics []api.SImportNic) error {
  4605. if len(nics) == 0 {
  4606. return httperrors.NewInputParameterError("Empty import nics")
  4607. }
  4608. for _, nic := range nics {
  4609. q := GuestnetworkManager.Query()
  4610. count := q.Filter(sqlchemy.OR(
  4611. sqlchemy.Equals(q.Field("mac_addr"), nic.Mac),
  4612. sqlchemy.Equals(q.Field("ip_addr"), nic.Ip)),
  4613. ).Count()
  4614. if count > 0 {
  4615. return httperrors.NewInputParameterError("ip %s or mac %s has been registered", nic.Ip, nic.Mac)
  4616. }
  4617. net, err := NetworkManager.GetOnPremiseNetworkOfIP(nic.Ip, "", tristate.None)
  4618. if err != nil {
  4619. return httperrors.NewNotFoundError("Not found network by ip %s", nic.Ip)
  4620. }
  4621. host, _ := self.GetHost()
  4622. _, err = self.attach2NetworkDesc(ctx, userCred, host, ToNetConfig(&nic, net), nil, nil)
  4623. if err != nil {
  4624. return err
  4625. }
  4626. }
  4627. return nil
  4628. }
  4629. func ToDiskConfig(d *api.SImportDisk) *api.DiskConfig {
  4630. ret := &api.DiskConfig{
  4631. SizeMb: d.SizeMb,
  4632. ImageId: d.TemplateId,
  4633. Format: d.Format,
  4634. Driver: d.Driver,
  4635. DiskId: d.DiskId,
  4636. Cache: d.CacheMode,
  4637. Backend: d.Backend,
  4638. }
  4639. if len(d.Mountpoint) > 0 {
  4640. ret.Mountpoint = d.Mountpoint
  4641. }
  4642. if len(d.Fs) > 0 {
  4643. ret.Fs = d.Fs
  4644. }
  4645. return ret
  4646. }
  4647. func (self *SGuest) importDisks(ctx context.Context, userCred mcclient.TokenCredential, disks []api.SImportDisk) error {
  4648. if len(disks) == 0 {
  4649. return httperrors.NewInputParameterError("Empty import disks")
  4650. }
  4651. host, _ := self.GetHost()
  4652. for _, disk := range disks {
  4653. disk, err := self.createDiskOnHost(ctx, userCred, host, ToDiskConfig(&disk), nil, true, true, nil, nil, true)
  4654. if err != nil {
  4655. return err
  4656. }
  4657. disk.SetStatus(ctx, userCred, api.DISK_READY, "")
  4658. }
  4659. return nil
  4660. }
  4661. // 从Libvirt导入虚拟机
  4662. func (manager *SGuestManager) PerformImportFromLibvirt(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  4663. host := &api.SLibvirtHostConfig{}
  4664. if err := data.Unmarshal(host); err != nil {
  4665. return nil, httperrors.NewInputParameterError("Unmarshal data error %s", err)
  4666. }
  4667. if len(host.XmlFilePath) == 0 {
  4668. return nil, httperrors.NewInputParameterError("Some host config missing xml_file_path")
  4669. }
  4670. if len(host.HostIp) == 0 {
  4671. return nil, httperrors.NewInputParameterError("Some host config missing host ip")
  4672. }
  4673. sHost, err := HostManager.GetHostByIp("", api.HOST_TYPE_HYPERVISOR, host.HostIp)
  4674. if err != nil {
  4675. return nil, httperrors.NewInputParameterError("Invalid host ip %s", host.HostIp)
  4676. }
  4677. for _, server := range host.Servers {
  4678. for mac, ip := range server.MacIp {
  4679. _, err = net.ParseMAC(mac)
  4680. if err != nil {
  4681. return nil, httperrors.NewBadRequestError("Invalid server mac address %s", mac)
  4682. }
  4683. nIp := net.ParseIP(ip)
  4684. if nIp == nil {
  4685. return nil, httperrors.NewBadRequestError("Invalid server ip address %s", ip)
  4686. }
  4687. q := GuestnetworkManager.Query()
  4688. count := q.Filter(sqlchemy.OR(
  4689. sqlchemy.Equals(q.Field("mac_addr"), mac),
  4690. sqlchemy.Equals(q.Field("ip_addr"), ip)),
  4691. ).Count()
  4692. if count > 0 {
  4693. return nil, httperrors.NewInputParameterError("ip %s or mac %s has been registered", mac, ip)
  4694. }
  4695. }
  4696. }
  4697. taskData := jsonutils.NewDict()
  4698. taskData.Set("xml_file_path", jsonutils.NewString(host.XmlFilePath))
  4699. taskData.Set("servers", jsonutils.Marshal(host.Servers))
  4700. taskData.Set("monitor_path", jsonutils.NewString(host.MonitorPath))
  4701. task, err := taskman.TaskManager.NewTask(ctx, "HostImportLibvirtServersTask", sHost, userCred,
  4702. taskData, "", "", nil)
  4703. if err != nil {
  4704. return nil, httperrors.NewInternalServerError("NewTask error: %s", err)
  4705. }
  4706. task.ScheduleRun(nil)
  4707. return nil, nil
  4708. }
  4709. func (self *SGuest) GetDetailsVirtInstall(
  4710. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  4711. ) (jsonutils.JSONObject, error) {
  4712. if self.Hypervisor != api.HYPERVISOR_KVM {
  4713. return nil, httperrors.NewBadRequestError("Hypervisor %s can't generate libvirt xml", self.Hypervisor)
  4714. }
  4715. var (
  4716. vdiProtocol string
  4717. vdiListenPort int64
  4718. )
  4719. driver, err := self.GetDriver()
  4720. if err != nil {
  4721. return nil, err
  4722. }
  4723. host, _ := self.GetHost()
  4724. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM}) {
  4725. vncInfo, err := driver.GetGuestVncInfo(ctx, userCred, self, host, nil)
  4726. if err != nil {
  4727. log.Errorln(err)
  4728. return nil, err
  4729. }
  4730. vdiProtocol = vncInfo.Protocol
  4731. vdiListenPort = int64(vncInfo.Port)
  4732. }
  4733. extraCmdline, _ := query.GetArray("extra_cmdline")
  4734. libvirtBridge, _ := query.GetString("libvirt_bridge")
  4735. if len(libvirtBridge) == 0 {
  4736. libvirtBridge = "virbr0"
  4737. }
  4738. virtInstallCmd, err := self.GenerateVirtInstallCommandLine(
  4739. vdiProtocol, vdiListenPort, extraCmdline, libvirtBridge)
  4740. if err != nil {
  4741. return nil, httperrors.NewInternalServerError("Generate xml failed: %s", err)
  4742. }
  4743. res := jsonutils.NewDict()
  4744. res.Set("virt-install-command-line", jsonutils.NewString(virtInstallCmd))
  4745. return res, nil
  4746. }
  4747. func (self *SGuest) GenerateVirtInstallCommandLine(
  4748. vdiProtocol string, vdiListenPort int64, extraCmdline []jsonutils.JSONObject, libvirtBridge string,
  4749. ) (string, error) {
  4750. L := func(s string) string { return s + " \\\n" }
  4751. cmd := L("virt-install")
  4752. cmd += L("--cpu host")
  4753. cmd += L("--boot cdrom,hd,network")
  4754. cmd += L(fmt.Sprintf("--name %s", self.Name))
  4755. cmd += L(fmt.Sprintf("--ram %d", self.VmemSize))
  4756. cmd += L(fmt.Sprintf("--vcpus %d", self.VcpuCount))
  4757. host, _ := self.GetHost()
  4758. // disks
  4759. guestDisks, err := self.GetGuestDisks()
  4760. if err != nil {
  4761. return "", errors.Wrapf(err, "GetGuestDisks")
  4762. }
  4763. for _, guestDisk := range guestDisks {
  4764. disk := guestDisk.GetDisk()
  4765. cmd += L(
  4766. fmt.Sprintf("--disk path=%s,bus=%s,cache=%s,io=%s",
  4767. disk.GetPathAtHost(host),
  4768. guestDisk.Driver,
  4769. guestDisk.CacheMode,
  4770. guestDisk.AioMode),
  4771. )
  4772. }
  4773. // networks
  4774. guestNetworks, err := self.GetNetworks("")
  4775. if err != nil {
  4776. return "", err
  4777. }
  4778. for _, guestNetwork := range guestNetworks {
  4779. cmd += L(
  4780. fmt.Sprintf("--network bridge,model=%s,source=%s,mac=%s",
  4781. guestNetwork.Driver, libvirtBridge, guestNetwork.MacAddr),
  4782. )
  4783. }
  4784. // isolated devices
  4785. isolatedDevices, _ := self.GetIsolatedDevices()
  4786. for _, isolatedDev := range isolatedDevices {
  4787. cmd += L(fmt.Sprintf("--hostdev %s", isolatedDev.Addr))
  4788. }
  4789. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_BLOCK_STREAM}) {
  4790. // spice or vnc
  4791. cmd += L(fmt.Sprintf("--graphics %s,listen=0.0.0.0,port=%d", vdiProtocol, vdiListenPort))
  4792. } else {
  4793. // generate xml, and need virsh define xml
  4794. cmd += L("--print-xml")
  4795. }
  4796. cmd += L("--video vga")
  4797. cmd += L("--wait 0")
  4798. // some customized options, not verify
  4799. for _, cmdline := range extraCmdline {
  4800. cmd += L(cmdline.String())
  4801. }
  4802. // debug print
  4803. cmd += "-d"
  4804. return cmd, nil
  4805. }
  4806. func (self *SGuest) PerformSyncFixNics(ctx context.Context,
  4807. userCred mcclient.TokenCredential,
  4808. query jsonutils.JSONObject,
  4809. input api.GuestSyncFixNicsInput) (jsonutils.JSONObject, error) {
  4810. iVM, err := self.GetIVM(ctx)
  4811. if err != nil {
  4812. return nil, httperrors.NewGeneralError(err)
  4813. }
  4814. vnics, err := iVM.GetINics()
  4815. if err != nil {
  4816. return nil, httperrors.NewGeneralError(err)
  4817. }
  4818. host, _ := self.GetHost()
  4819. if host == nil {
  4820. return nil, httperrors.NewInternalServerError("host not found???")
  4821. }
  4822. iplist := input.Ip
  4823. // validate iplist
  4824. if len(iplist) == 0 {
  4825. return nil, httperrors.NewInputParameterError("empty ip list")
  4826. }
  4827. for _, ip := range iplist {
  4828. if !regutils.MatchIP4Addr(ip) {
  4829. return nil, httperrors.NewInputParameterError("invalid IPv4 address %s", ip)
  4830. }
  4831. // ip is reachable on host
  4832. net, err := host.getNetworkOfIPOnHost(ctx, ip)
  4833. if err != nil {
  4834. return nil, httperrors.NewInputParameterError("Unreachable IP %s: %s", ip, err)
  4835. }
  4836. // check ip is reserved or free
  4837. rip := ReservedipManager.GetReservedIP(net, ip, api.AddressTypeIPv4)
  4838. if rip == nil {
  4839. // check ip is free
  4840. nip, err := net.GetFreeIPWithLock(ctx, userCred, nil, nil, ip, "", false, api.AddressTypeIPv4)
  4841. if err != nil {
  4842. return nil, httperrors.NewInputParameterError("Unavailable IP %s: occupied", ip)
  4843. }
  4844. if nip != ip {
  4845. return nil, httperrors.NewInputParameterError("Unavailable IP %s: occupied", ip)
  4846. }
  4847. }
  4848. }
  4849. errs := make([]error, 0)
  4850. for i := range vnics {
  4851. ip := vnics[i].GetIP()
  4852. if len(ip) == 0 {
  4853. continue
  4854. }
  4855. _, err := host.getNetworkOfIPOnHost(ctx, ip)
  4856. if err != nil {
  4857. errs = append(errs, errors.Wrap(err, ip))
  4858. }
  4859. }
  4860. if len(errs) > 0 {
  4861. return nil, httperrors.NewInvalidStatusError("%v", errors.NewAggregate(errs))
  4862. }
  4863. result := self.SyncVMNics(ctx, userCred, host, vnics, iplist)
  4864. if result.IsError() {
  4865. return nil, httperrors.NewInternalServerError("%s", result.Result())
  4866. }
  4867. return nil, nil
  4868. }
  4869. // 更改项目
  4870. func (guest *SGuest) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
  4871. disks, err := guest.GetDisks()
  4872. if err != nil {
  4873. return nil, errors.Wrapf(err, "GetDisks")
  4874. }
  4875. for i := range disks {
  4876. disk := disks[i]
  4877. _, err := disk.PerformChangeOwner(ctx, userCred, query, input)
  4878. if err != nil {
  4879. return nil, err
  4880. }
  4881. }
  4882. if eip, _ := guest.GetEipOrPublicIp(); eip != nil {
  4883. _, err := eip.PerformChangeOwner(ctx, userCred, query, input)
  4884. if err != nil {
  4885. return nil, err
  4886. }
  4887. }
  4888. // change owner for instance snapshot
  4889. isps, err := guest.GetInstanceSnapshots()
  4890. if err != nil {
  4891. return nil, errors.Wrap(err, "unable to GetInstanceSnapshots")
  4892. }
  4893. for i := range isps {
  4894. _, err := isps[i].PerformChangeOwner(ctx, userCred, query, input)
  4895. if err != nil {
  4896. return nil, errors.Wrapf(err, "unable to change owner for instance snapshot %s", isps[i].GetId())
  4897. }
  4898. }
  4899. ctrs, err := GetContainerManager().GetContainersByPod(guest.GetId())
  4900. if err != nil {
  4901. return nil, errors.Wrapf(err, "get containers by guest_id %s", guest.GetId())
  4902. }
  4903. for _, ctr := range ctrs {
  4904. if _, err := ctr.PerformChangeOwner(ctx, userCred, query, input); err != nil {
  4905. return nil, errors.Wrapf(err, "unable to change owner for container %s", ctr.GetName())
  4906. }
  4907. }
  4908. changOwner, err := guest.SVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input)
  4909. if err != nil {
  4910. return nil, err
  4911. }
  4912. err = guest.StartSyncTask(ctx, userCred, false, "")
  4913. if err != nil {
  4914. return nil, errors.Wrap(err, "PerformChangeOwner StartSyncTask err")
  4915. }
  4916. return changOwner, nil
  4917. }
  4918. // 磁盘扩容
  4919. func (guest *SGuest) PerformResizeDisk(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResizeDiskInput) (jsonutils.JSONObject, error) {
  4920. if guest.Hypervisor == api.HYPERVISOR_ESXI {
  4921. c, err := guest.GetInstanceSnapshotCount()
  4922. if err != nil {
  4923. return nil, errors.Wrapf(err, "unable to GetInstanceSnapshotCount for guest %s", guest.GetName())
  4924. }
  4925. if c > 0 {
  4926. return nil, httperrors.NewUnsupportOperationError("the disk of a esxi virtual machine with instance snapshots does not support resizing")
  4927. }
  4928. }
  4929. if len(input.DiskId) == 0 {
  4930. return nil, httperrors.NewMissingParameterError("disk_id")
  4931. }
  4932. diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.DiskId)
  4933. if err != nil {
  4934. return nil, err
  4935. }
  4936. guestdisk := guest.GetGuestDisk(input.DiskId)
  4937. if guestdisk == nil {
  4938. return nil, httperrors.NewInvalidStatusError("disk %s not attached to server", input.DiskId)
  4939. }
  4940. disk := diskObj.(*SDisk)
  4941. sizeMb, err := input.SizeMb()
  4942. if err != nil {
  4943. return nil, err
  4944. }
  4945. err = disk.doResize(ctx, userCred, sizeMb, guest)
  4946. if err != nil {
  4947. return nil, err
  4948. }
  4949. return nil, nil
  4950. }
  4951. func (guest *SGuest) StartGuestDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, diskId string, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota) error {
  4952. guest.SetStatus(ctx, userCred, api.VM_START_RESIZE_DISK, "StartGuestDiskResizeTask")
  4953. params := jsonutils.NewDict()
  4954. params.Add(jsonutils.NewInt(sizeMb), "size")
  4955. params.Add(jsonutils.NewString(diskId), "disk_id")
  4956. task, err := taskman.TaskManager.NewTask(ctx, "GuestResizeDiskTask", guest, userCred, params, parentTaskId, "", pendingUsage)
  4957. if err != nil {
  4958. return err
  4959. }
  4960. task.ScheduleRun(nil)
  4961. return nil
  4962. }
  4963. // 磁盘限速
  4964. func (self *SGuest) PerformIoThrottle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSetDiskIoThrottleInput) (jsonutils.JSONObject, error) {
  4965. if self.Hypervisor != api.HYPERVISOR_KVM {
  4966. return nil, httperrors.NewBadRequestError("Hypervisor %s can't do io throttle", self.Hypervisor)
  4967. }
  4968. if self.Status != api.VM_RUNNING && self.Status != api.VM_READY {
  4969. return nil, httperrors.NewServerStatusError("Cannot do io throttle in status %s", self.Status)
  4970. }
  4971. for diskId, bpsMb := range input.Bps {
  4972. if bpsMb < 0 {
  4973. return nil, httperrors.NewInputParameterError("disk %s bps must > 0", diskId)
  4974. }
  4975. disk := DiskManager.FetchDiskById(diskId)
  4976. if disk == nil {
  4977. return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
  4978. }
  4979. }
  4980. for diskId, iops := range input.IOPS {
  4981. if iops < 0 {
  4982. return nil, httperrors.NewInputParameterError("disk %s iops must > 0", diskId)
  4983. }
  4984. disk := DiskManager.FetchDiskById(diskId)
  4985. if disk == nil {
  4986. return nil, httperrors.NewNotFoundError("disk %s not found", diskId)
  4987. }
  4988. }
  4989. if err := self.UpdateIoThrottle(input); err != nil {
  4990. return nil, errors.Wrap(err, "update io throttles")
  4991. }
  4992. return nil, self.StartBlockIoThrottleTask(ctx, userCred, input)
  4993. }
  4994. func (self *SGuest) UpdateIoThrottle(input *api.ServerSetDiskIoThrottleInput) error {
  4995. gds, err := self.GetGuestDisks()
  4996. if err != nil {
  4997. return err
  4998. }
  4999. for i := 0; i < len(gds); i++ {
  5000. _, err := db.Update(&gds[i], func() error {
  5001. if bps, ok := input.Bps[gds[i].DiskId]; ok {
  5002. gds[i].Bps = bps
  5003. }
  5004. if iops, ok := input.IOPS[gds[i].DiskId]; ok {
  5005. gds[i].Iops = iops
  5006. }
  5007. return nil
  5008. })
  5009. if err != nil {
  5010. return nil
  5011. }
  5012. }
  5013. return nil
  5014. }
  5015. func (self *SGuest) StartBlockIoThrottleTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerSetDiskIoThrottleInput) error {
  5016. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  5017. params.Set("old_status", jsonutils.NewString(self.Status))
  5018. self.SetStatus(ctx, userCred, api.VM_IO_THROTTLE, "start block io throttle task")
  5019. task, err := taskman.TaskManager.NewTask(ctx, "GuestBlockIoThrottleTask", self, userCred, params, "", "", nil)
  5020. if err != nil {
  5021. log.Errorf("%s", err)
  5022. return err
  5023. }
  5024. task.ScheduleRun(nil)
  5025. return nil
  5026. }
  5027. func (self *SGuest) validateForBatchMigrate(ctx context.Context, rescueMode bool) (*SGuest, error) {
  5028. guest := GuestManager.FetchGuestById(self.Id)
  5029. if guest.Hypervisor != api.HYPERVISOR_KVM {
  5030. return guest, httperrors.NewBadRequestError("guest %s hypervisor %s can't migrate",
  5031. guest.Name, guest.Hypervisor)
  5032. }
  5033. if len(guest.BackupHostId) > 0 {
  5034. return guest, httperrors.NewBadRequestError("guest %s has backup, can't migrate", guest.Name)
  5035. }
  5036. devs, _ := guest.GetIsolatedDevices()
  5037. if len(devs) > 0 {
  5038. return guest, httperrors.NewBadRequestError("guest %s has isolated device, can't migrate", guest.Name)
  5039. }
  5040. if rescueMode {
  5041. if !guest.guestDisksStorageTypeIsShared() {
  5042. return guest, httperrors.NewBadRequestError("can't rescue geust %s with local storage", guest.Name)
  5043. }
  5044. return guest, nil
  5045. }
  5046. if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY, api.VM_UNKNOWN}) {
  5047. return guest, httperrors.NewBadRequestError("guest %s status %s can't migrate", guest.Name, guest.Status)
  5048. }
  5049. if guest.Status == api.VM_RUNNING {
  5050. cdroms, _ := guest.getCdroms()
  5051. for _, cdrom := range cdroms {
  5052. if len(cdrom.ImageId) > 0 {
  5053. return guest, httperrors.NewBadRequestError("cannot migrate with cdrom")
  5054. }
  5055. }
  5056. floppys, _ := guest.getFloppys()
  5057. for _, floppy := range floppys {
  5058. if len(floppy.ImageId) > 0 {
  5059. return guest, httperrors.NewBadRequestError("cannot migrate with floppy")
  5060. }
  5061. }
  5062. } else if guest.Status == api.VM_UNKNOWN {
  5063. if guest.getDefaultStorageType() == api.STORAGE_LOCAL {
  5064. return guest, httperrors.NewBadRequestError(
  5065. "guest %s status %s can't migrate with local storage",
  5066. guest.Name, guest.Status,
  5067. )
  5068. }
  5069. }
  5070. return guest, nil
  5071. }
  5072. // 批量迁移
  5073. func (manager *SGuestManager) PerformBatchMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5074. params := new(api.GuestBatchMigrateRequest)
  5075. err := data.Unmarshal(params)
  5076. if err != nil {
  5077. return nil, httperrors.NewInputParameterError("Unmarshal input error %s", err)
  5078. }
  5079. if len(params.GuestIds) == 0 {
  5080. return nil, httperrors.NewInputParameterError("missing guest id")
  5081. }
  5082. var preferHostId string
  5083. if len(params.PreferHostId) > 0 {
  5084. iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, params.PreferHostId)
  5085. if iHost == nil {
  5086. return nil, httperrors.NewBadRequestError("Host %s not found", params.PreferHostId)
  5087. }
  5088. host := iHost.(*SHost)
  5089. preferHostId = host.Id
  5090. err := host.IsAssignable(ctx, userCred)
  5091. if err != nil {
  5092. return nil, errors.Wrap(err, "IsAssignable")
  5093. }
  5094. }
  5095. if params.EnableTLS == nil {
  5096. params.EnableTLS = &options.Options.EnableTlsMigration
  5097. }
  5098. guests := make([]SGuest, 0)
  5099. q := GuestManager.Query().In("id", params.GuestIds)
  5100. err = db.FetchModelObjects(GuestManager, q, &guests)
  5101. if err != nil {
  5102. return nil, httperrors.NewInternalServerError("%v", err)
  5103. }
  5104. if len(guests) != len(params.GuestIds) {
  5105. return nil, httperrors.NewBadRequestError("Check input guests is exist")
  5106. }
  5107. errs := []error{}
  5108. for i := 0; i < len(guests); i++ {
  5109. func() {
  5110. lockman.LockObject(ctx, &guests[i])
  5111. defer lockman.ReleaseObject(ctx, &guests[i])
  5112. _, err = guests[i].validateForBatchMigrate(ctx, false)
  5113. if err != nil {
  5114. errs = append(errs, errors.Wrapf(err, "Guest %s", guests[i].Name))
  5115. return
  5116. }
  5117. if guests[i].Status == api.VM_RUNNING {
  5118. err = guests[i].StartGuestLiveMigrateTask(ctx, userCred,
  5119. guests[i].Status, preferHostId, &params.SkipCpuCheck, &params.SkipKernelCheck,
  5120. params.EnableTLS, params.QuciklyFinish, params.MaxBandwidthMb, nil, "",
  5121. )
  5122. } else {
  5123. err = guests[i].StartMigrateTask(ctx, userCred, guests[i].Status == api.VM_UNKNOWN,
  5124. false, guests[i].Status, preferHostId, "")
  5125. }
  5126. if err != nil {
  5127. errs = append(errs, errors.Wrapf(err, "Guest %s", guests[i].Name))
  5128. }
  5129. }()
  5130. }
  5131. return nil, errors.NewAggregate(errs)
  5132. }
  5133. func (manager *SGuestManager) StartHostGuestsMigrateTask(
  5134. ctx context.Context, userCred mcclient.TokenCredential,
  5135. guests []*SGuest, kwargs *jsonutils.JSONDict, parentTaskId string,
  5136. ) error {
  5137. taskItems := make([]db.IStandaloneModel, len(guests))
  5138. for i := range guests {
  5139. taskItems[i] = guests[i]
  5140. }
  5141. task, err := taskman.TaskManager.NewParallelTask(ctx, "HostGuestsMigrateTask", taskItems, userCred, kwargs, parentTaskId, "", nil)
  5142. if err != nil {
  5143. log.Errorln(err)
  5144. return err
  5145. }
  5146. task.ScheduleRun(nil)
  5147. return nil
  5148. }
  5149. var supportInstanceSnapshotHypervisors = []string{
  5150. api.HYPERVISOR_KVM,
  5151. api.HYPERVISOR_ESXI,
  5152. api.HYPERVISOR_CNWARE,
  5153. }
  5154. func (self *SGuest) validateCreateInstanceSnapshot(
  5155. ctx context.Context,
  5156. userCred mcclient.TokenCredential,
  5157. query jsonutils.JSONObject,
  5158. input api.ServerCreateSnapshotParams,
  5159. ) (*SRegionQuota, api.ServerCreateSnapshotParams, error) {
  5160. if !utils.IsInStringArray(self.Hypervisor, supportInstanceSnapshotHypervisors) {
  5161. return nil, input, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
  5162. }
  5163. disks, err := self.GetDisks()
  5164. if err != nil {
  5165. return nil, input, err
  5166. }
  5167. for i := range disks {
  5168. if len(disks[i].SnapshotId) > 0 {
  5169. if disks[i].GetMetadata(ctx, "merge_snapshot", userCred) == "true" {
  5170. return nil, input, httperrors.NewBadRequestError("disk %s backing snapshot not merged", disks[i].Id)
  5171. }
  5172. }
  5173. }
  5174. if len(self.BackupHostId) > 0 {
  5175. return nil, input, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
  5176. }
  5177. if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  5178. return nil, input, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
  5179. }
  5180. ownerId := self.GetOwnerId()
  5181. // dataDict := data.(*jsonutils.JSONDict)
  5182. // nameHint, err := dataDict.GetString("generate_name")
  5183. if len(input.GenerateName) > 0 {
  5184. name, err := db.GenerateName(ctx, InstanceSnapshotManager, ownerId, input.GenerateName)
  5185. if err != nil {
  5186. return nil, input, errors.Wrap(err, "GenerateName")
  5187. }
  5188. input.Name = name
  5189. } else if len(input.Name) == 0 {
  5190. return nil, input, httperrors.NewMissingParameterError("name")
  5191. }
  5192. err = db.NewNameValidator(ctx, InstanceSnapshotManager, ownerId, input.Name, nil)
  5193. if err != nil {
  5194. return nil, input, errors.Wrap(err, "NewNameValidator")
  5195. }
  5196. // construct Quota
  5197. pendingUsage := &SRegionQuota{InstanceSnapshot: 1}
  5198. host, _ := self.GetHost()
  5199. provider := host.GetProviderName()
  5200. if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
  5201. disks, err := self.GetDisks()
  5202. if err != nil {
  5203. return nil, input, errors.Wrapf(err, "GetDisks")
  5204. }
  5205. //for i := 0; i < len(disks); i++ {
  5206. // if storage, _ := disks[i].GetStorage(); utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
  5207. // count, err := SnapshotManager.GetDiskManualSnapshotCount(disks[i].Id)
  5208. // if err != nil {
  5209. // return nil, input, httperrors.NewInternalServerError("%v", err)
  5210. // }
  5211. // if count >= options.Options.DefaultMaxManualSnapshotCount {
  5212. // return nil, input, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
  5213. // }
  5214. // }
  5215. //}
  5216. pendingUsage.Snapshot = len(disks)
  5217. }
  5218. keys, err := self.GetRegionalQuotaKeys()
  5219. if err != nil {
  5220. return nil, input, errors.Wrap(err, "GetRegionalQuotaKeys")
  5221. }
  5222. pendingUsage.SetKeys(keys)
  5223. err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
  5224. if err != nil {
  5225. return nil, input, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
  5226. }
  5227. return pendingUsage, input, nil
  5228. }
  5229. func (self *SGuest) validateCreateInstanceBackup(
  5230. ctx context.Context,
  5231. userCred mcclient.TokenCredential,
  5232. query jsonutils.JSONObject,
  5233. input api.ServerCreateInstanceBackupInput,
  5234. ) (api.ServerCreateInstanceBackupInput, error) {
  5235. if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM}) {
  5236. return input, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
  5237. }
  5238. if len(self.BackupHostId) > 0 {
  5239. return input, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
  5240. }
  5241. if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  5242. return input, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
  5243. }
  5244. ownerId := self.GetOwnerId()
  5245. if len(input.GenerateName) > 0 {
  5246. nameHint := input.GenerateName
  5247. name, err := db.GenerateName(ctx, InstanceBackupManager, ownerId, nameHint)
  5248. if err != nil {
  5249. return input, errors.Wrap(err, "db.GenerateName")
  5250. }
  5251. input.Name = name
  5252. } else if len(input.Name) == 0 {
  5253. return input, httperrors.NewMissingParameterError("name")
  5254. }
  5255. err := db.NewNameValidator(ctx, InstanceBackupManager, ownerId, input.Name, nil)
  5256. if err != nil {
  5257. return input, errors.Wrap(err, "db.NewNameValidator")
  5258. }
  5259. return input, nil
  5260. }
  5261. // 创建主机快照
  5262. // 1. validate guest status, guest hypervisor
  5263. // 2. validate every disk manual snapshot count
  5264. // 3. validate snapshot quota with disk count
  5265. func (self *SGuest) PerformInstanceSnapshot(
  5266. ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerInstanceSnapshot,
  5267. ) (jsonutils.JSONObject, error) {
  5268. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  5269. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  5270. }
  5271. lockman.LockClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
  5272. defer lockman.ReleaseClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
  5273. pendingUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
  5274. if err != nil {
  5275. return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
  5276. }
  5277. input.ServerCreateSnapshotParams = params
  5278. if input.WithMemory {
  5279. if self.Status != api.VM_RUNNING {
  5280. return nil, httperrors.NewUnsupportOperationError("Can't save memory state when guest status is %q", self.Status)
  5281. }
  5282. }
  5283. instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, input.Name, false, input.WithMemory)
  5284. if err != nil {
  5285. quotas.CancelPendingUsage(
  5286. ctx, userCred, pendingUsage, pendingUsage, false)
  5287. return nil, httperrors.NewInternalServerError("create instance snapshot failed: %s", err)
  5288. }
  5289. err = self.InheritTo(ctx, userCred, instanceSnapshot)
  5290. if err != nil {
  5291. return nil, errors.Wrapf(err, "unable to inherit from guest %s to instance snapshot %s", self.GetId(), instanceSnapshot.GetId())
  5292. }
  5293. err = self.InstaceCreateSnapshot(ctx, userCred, instanceSnapshot, pendingUsage)
  5294. if err != nil {
  5295. quotas.CancelPendingUsage(
  5296. ctx, userCred, pendingUsage, pendingUsage, false)
  5297. return nil, httperrors.NewInternalServerError("start create snapshot task failed: %s", err)
  5298. }
  5299. return nil, nil
  5300. }
  5301. func (self *SGuest) PerformInstanceBackup(
  5302. ctx context.Context,
  5303. userCred mcclient.TokenCredential,
  5304. query jsonutils.JSONObject,
  5305. input api.ServerCreateInstanceBackupInput,
  5306. ) (jsonutils.JSONObject, error) {
  5307. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  5308. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  5309. }
  5310. lockman.LockClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
  5311. defer lockman.ReleaseClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
  5312. var err error
  5313. input, err = self.validateCreateInstanceBackup(ctx, userCred, query, input)
  5314. if err != nil {
  5315. return nil, errors.Wrap(err, "validateCreateInstanceBackup")
  5316. }
  5317. name := input.Name
  5318. backupStorageId := input.BackupStorageId
  5319. if backupStorageId == "" {
  5320. return nil, httperrors.NewMissingParameterError("backup_storage_id")
  5321. }
  5322. ibs, err := BackupStorageManager.FetchByIdOrName(ctx, userCred, backupStorageId)
  5323. if err != nil {
  5324. if errors.Cause(err) == sql.ErrNoRows {
  5325. return nil, httperrors.NewResourceNotFoundError2(BackupStorageManager.Keyword(), backupStorageId)
  5326. }
  5327. if errors.Cause(err) == sqlchemy.ErrDuplicateEntry {
  5328. return nil, httperrors.NewDuplicateResourceError(BackupStorageManager.Keyword(), backupStorageId)
  5329. }
  5330. return nil, httperrors.NewGeneralError(err)
  5331. }
  5332. bs := ibs.(*SBackupStorage)
  5333. if bs.Status != api.BACKUPSTORAGE_STATUS_ONLINE {
  5334. return nil, httperrors.NewForbiddenError("can't backup guest to backup storage with status %s", bs.Status)
  5335. }
  5336. instanceBackup, err := InstanceBackupManager.CreateInstanceBackup(ctx, userCred, self, name, backupStorageId)
  5337. if err != nil {
  5338. return nil, httperrors.NewInternalServerError("create instance backup failed: %s", err)
  5339. }
  5340. err = self.InheritTo(ctx, userCred, instanceBackup)
  5341. if err != nil {
  5342. return nil, errors.Wrapf(err, "unable to inherit from guest %s to instance backup %s", self.GetId(), instanceBackup.GetId())
  5343. }
  5344. err = self.InstanceCreateBackup(ctx, userCred, instanceBackup)
  5345. if err != nil {
  5346. return nil, httperrors.NewInternalServerError("start create backup task failed: %s", err)
  5347. }
  5348. return nil, nil
  5349. }
  5350. func (self *SGuest) InstaceCreateSnapshot(
  5351. ctx context.Context,
  5352. userCred mcclient.TokenCredential,
  5353. instanceSnapshot *SInstanceSnapshot,
  5354. pendingUsage *SRegionQuota,
  5355. ) error {
  5356. self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_SNAPSHOT, "instance snapshot")
  5357. return instanceSnapshot.StartCreateInstanceSnapshotTask(ctx, userCred, pendingUsage, "")
  5358. }
  5359. func (self *SGuest) InstanceCreateBackup(ctx context.Context, userCred mcclient.TokenCredential, instanceBackup *SInstanceBackup) error {
  5360. self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_BACKUP, "instance backup")
  5361. return instanceBackup.StartCreateInstanceBackupTask(ctx, userCred, "")
  5362. }
  5363. func (self *SGuest) PerformInstanceSnapshotReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerResetInput) (jsonutils.JSONObject, error) {
  5364. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  5365. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  5366. }
  5367. if self.Status != api.VM_READY {
  5368. return nil, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
  5369. }
  5370. obj, err := InstanceSnapshotManager.FetchByIdOrName(ctx, userCred, input.InstanceSnapshot)
  5371. if err != nil {
  5372. return nil, errors.Wrapf(err, "unable to fetch instance snapshot %q", input.InstanceSnapshot)
  5373. }
  5374. instanceSnapshot := obj.(*SInstanceSnapshot)
  5375. if instanceSnapshot.GuestId != self.GetId() {
  5376. return nil, httperrors.NewBadRequestError("instance snapshot %q not belong server %q", instanceSnapshot.GetName(), self.GetId())
  5377. }
  5378. if instanceSnapshot.Status != api.INSTANCE_SNAPSHOT_READY {
  5379. return nil, httperrors.NewBadRequestError("Instance snapshot not ready")
  5380. }
  5381. if input.WithMemory && !instanceSnapshot.WithMemory {
  5382. return nil, httperrors.NewBadRequestError("Instance snapshot not with memory statefile")
  5383. }
  5384. err = self.StartSnapshotResetTask(ctx, userCred, instanceSnapshot, input.AutoStart, input.WithMemory)
  5385. if err != nil {
  5386. return nil, httperrors.NewInternalServerError("start snapshot reset failed %s", err)
  5387. }
  5388. return nil, nil
  5389. }
  5390. func (self *SGuest) StartSnapshotResetTask(ctx context.Context, userCred mcclient.TokenCredential, instanceSnapshot *SInstanceSnapshot, autoStart *bool, withMemory bool) error {
  5391. data := jsonutils.NewDict()
  5392. if autoStart != nil && *autoStart {
  5393. data.Set("auto_start", jsonutils.JSONTrue)
  5394. }
  5395. data.Add(jsonutils.NewBool(withMemory), "with_memory")
  5396. self.SetStatus(ctx, userCred, api.VM_START_SNAPSHOT_RESET, "start snapshot reset task")
  5397. instanceSnapshot.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_RESET, "start snapshot reset task")
  5398. log.Errorf("====data: %s", data)
  5399. if task, err := taskman.TaskManager.NewTask(
  5400. ctx, "InstanceSnapshotResetTask", instanceSnapshot, userCred, data, "", "", nil,
  5401. ); err != nil {
  5402. return err
  5403. } else {
  5404. task.ScheduleRun(nil)
  5405. }
  5406. return nil
  5407. }
  5408. func (self *SGuest) PerformSnapshotAndClone(
  5409. ctx context.Context,
  5410. userCred mcclient.TokenCredential,
  5411. query jsonutils.JSONObject,
  5412. input api.ServerSnapshotAndCloneInput,
  5413. ) (jsonutils.JSONObject, error) {
  5414. if err := self.ValidateEncryption(ctx, userCred); err != nil {
  5415. return nil, errors.Wrap(httperrors.ErrForbidden, "encryption key not accessible")
  5416. }
  5417. newlyGuestName := input.Name
  5418. if len(input.Name) == 0 {
  5419. return nil, httperrors.NewMissingParameterError("name")
  5420. }
  5421. count := 1
  5422. if input.Count != nil {
  5423. count = *input.Count
  5424. if count <= 0 {
  5425. return nil, httperrors.NewInputParameterError("count must > 0")
  5426. }
  5427. }
  5428. if len(input.PreferHostId) > 0 {
  5429. if len(input.PreferHostId) > 0 {
  5430. iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, input.PreferHostId)
  5431. if iHost == nil {
  5432. return nil, httperrors.NewBadRequestError("Host %s not found", input.PreferHostId)
  5433. }
  5434. host := iHost.(*SHost)
  5435. input.PreferHostId = host.Id
  5436. }
  5437. }
  5438. lockman.LockRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
  5439. defer lockman.ReleaseRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
  5440. // validate create instance snapshot and set snapshot pending usage
  5441. snapshotUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
  5442. if err != nil {
  5443. return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
  5444. }
  5445. input.ServerCreateSnapshotParams = params
  5446. // set guest pending usage
  5447. pendingUsage, pendingRegionUsage, err := self.getGuestUsage(count)
  5448. keys, err := self.GetQuotaKeys()
  5449. if err != nil {
  5450. quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
  5451. return nil, errors.Wrap(err, "GetQuotaKeys")
  5452. }
  5453. pendingUsage.SetKeys(keys)
  5454. err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage)
  5455. if err != nil {
  5456. quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
  5457. return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
  5458. }
  5459. regionKeys, err := self.GetRegionalQuotaKeys()
  5460. if err != nil {
  5461. quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
  5462. quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
  5463. return nil, errors.Wrap(err, "GetRegionalQuotaKeys")
  5464. }
  5465. pendingRegionUsage.SetKeys(regionKeys)
  5466. err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingRegionUsage)
  5467. if err != nil {
  5468. quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
  5469. quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
  5470. return nil, errors.Wrap(err, "CheckSetPendingQuota")
  5471. }
  5472. // migrate snapshotUsage into regionUsage, then discard snapshotUsage
  5473. pendingRegionUsage.Snapshot = snapshotUsage.Snapshot
  5474. instanceSnapshotName, err := db.GenerateName(ctx, InstanceSnapshotManager, self.GetOwnerId(),
  5475. fmt.Sprintf("%s-%s", input.Name, rand.String(8)))
  5476. if err != nil {
  5477. quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
  5478. quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
  5479. return nil, httperrors.NewInternalServerError("Generate snapshot name failed %s", err)
  5480. }
  5481. instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(
  5482. ctx, userCred, self, instanceSnapshotName,
  5483. input.AutoDeleteInstanceSnapshot != nil && *input.AutoDeleteInstanceSnapshot, false)
  5484. if err != nil {
  5485. quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
  5486. quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
  5487. return nil, httperrors.NewInternalServerError("create instance snapshot failed: %s", err)
  5488. } else {
  5489. cancelRegionUsage := &SRegionQuota{Snapshot: snapshotUsage.Snapshot}
  5490. quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, cancelRegionUsage, true)
  5491. }
  5492. err = self.StartInstanceSnapshotAndCloneTask(
  5493. ctx, userCred, newlyGuestName, &pendingUsage, &pendingRegionUsage, instanceSnapshot, input)
  5494. if err != nil {
  5495. quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
  5496. quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
  5497. return nil, err
  5498. }
  5499. return nil, nil
  5500. }
  5501. func (self *SGuest) StartInstanceSnapshotAndCloneTask(
  5502. ctx context.Context, userCred mcclient.TokenCredential, newlyGuestName string,
  5503. pendingUsage *SQuota, pendingRegionUsage *SRegionQuota, instanceSnapshot *SInstanceSnapshot,
  5504. input api.ServerSnapshotAndCloneInput,
  5505. ) error {
  5506. params := jsonutils.NewDict()
  5507. params.Set("guest_params", jsonutils.Marshal(input))
  5508. if task, err := taskman.TaskManager.NewTask(
  5509. ctx, "InstanceSnapshotAndCloneTask", instanceSnapshot, userCred, params, "", "", pendingUsage, pendingRegionUsage); err != nil {
  5510. return err
  5511. } else {
  5512. self.SetStatus(ctx, userCred, api.VM_START_INSTANCE_SNAPSHOT, "instance snapshot")
  5513. task.ScheduleRun(nil)
  5514. return nil
  5515. }
  5516. }
  5517. func (manager *SGuestManager) CreateGuestFromInstanceSnapshot(
  5518. ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSnapshotAndCloneInput, isp *SInstanceSnapshot,
  5519. ) (*SGuest, *jsonutils.JSONDict, error) {
  5520. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  5521. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  5522. if guestName, err := db.GenerateName(ctx, manager, isp.GetOwnerId(), input.Name); err != nil {
  5523. return nil, nil, errors.Wrap(err, "db.GenerateName")
  5524. } else {
  5525. input.Name = guestName
  5526. }
  5527. input.InstanceSnapshotId = isp.Id
  5528. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  5529. iGuest, err := db.DoCreate(manager, ctx, userCred, nil, params, isp.GetOwnerId())
  5530. if err != nil {
  5531. return nil, nil, errors.Wrap(err, "db.DoCreate")
  5532. }
  5533. guest := iGuest.(*SGuest)
  5534. notes := map[string]string{
  5535. "instance_snapshot_id": isp.Id,
  5536. "guest_id": isp.GuestId,
  5537. }
  5538. logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_SNAPSHOT_AND_CLONE, notes, userCred, true)
  5539. func() {
  5540. lockman.LockObject(ctx, guest)
  5541. defer lockman.ReleaseObject(ctx, guest)
  5542. guest.PostCreate(ctx, userCred, guest.GetOwnerId(), nil, params)
  5543. }()
  5544. return guest, params, nil
  5545. }
  5546. func (self *SGuest) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5547. if self.Hypervisor != api.HYPERVISOR_BAREMETAL {
  5548. return nil, httperrors.NewInvalidStatusError("not a baremetal server")
  5549. }
  5550. host, _ := self.GetHost()
  5551. if host == nil {
  5552. return nil, httperrors.NewInvalidStatusError("no valid host")
  5553. }
  5554. if !host.IsBaremetal {
  5555. return nil, httperrors.NewInvalidStatusError("host is not a baremetal")
  5556. }
  5557. return host.GetDetailsJnlp(ctx, userCred, query)
  5558. }
  5559. func (guest *SGuest) StartDeleteGuestSnapshots(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  5560. task, err := taskman.TaskManager.NewTask(ctx, "GuestDeleteSnapshotsTask", guest, userCred, nil, parentTaskId, "", nil)
  5561. if err != nil {
  5562. return err
  5563. }
  5564. task.ScheduleRun(nil)
  5565. return nil
  5566. }
  5567. func (guest *SGuest) ValidateChangeNicBillingModeInput(ctx context.Context, input api.ServerNicTrafficLimit, needResetTraffic bool) (api.ServerNicTrafficLimit, bool, error) {
  5568. var err error
  5569. var gn *SGuestnetwork
  5570. if len(input.Mac) == 0 {
  5571. gns, err := guest.GetNetworks("")
  5572. if err != nil {
  5573. return input, needResetTraffic, errors.Wrap(err, "get guest networks")
  5574. }
  5575. if len(gns) == 0 {
  5576. return input, needResetTraffic, httperrors.NewBadRequestError("no guest network found")
  5577. }
  5578. if len(gns) > 1 {
  5579. return input, needResetTraffic, httperrors.NewBadRequestError("multiple guest networks found, please specify the mac")
  5580. }
  5581. input.Mac = gns[0].MacAddr
  5582. gn = &gns[0]
  5583. } else {
  5584. input.Mac = netutils2.FormatMac(input.Mac)
  5585. gn, err = guest.GetGuestnetworkByMac(input.Mac)
  5586. if err != nil {
  5587. return input, needResetTraffic, errors.Wrap(err, "get guest network by mac")
  5588. }
  5589. }
  5590. if input.BillingType == "" {
  5591. input.BillingType = gn.BillingType
  5592. }
  5593. if input.ChargeType == "" {
  5594. input.ChargeType = gn.ChargeType
  5595. }
  5596. var changed bool
  5597. input, changed, err = input.Validate(gn.BillingType, gn.ChargeType, gn.TxTrafficLimit, gn.RxTrafficLimit)
  5598. if err != nil {
  5599. return input, needResetTraffic, errors.Wrap(err, "validate input")
  5600. }
  5601. if changed {
  5602. if gn.BillingType == billing_api.BILLING_TYPE_POSTPAID && gn.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
  5603. // used to be postpaid traffic billing, need to stop the traffic log
  5604. // do nothing
  5605. }
  5606. if input.BillingType == billing_api.BILLING_TYPE_POSTPAID && input.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
  5607. // need to start the traffic log
  5608. GuestNetworkTrafficLogManager.logTraffic(ctx, guest, gn, nil, time.Now(), true)
  5609. }
  5610. if input.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
  5611. // need to measure the traffic from zero
  5612. needResetTraffic = true
  5613. }
  5614. }
  5615. return input, needResetTraffic, nil
  5616. }
  5617. func (guest *SGuest) doChangeNicBillingMode(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerNicTrafficLimit, needResetTraffic bool, action string) error {
  5618. if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  5619. return httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, guest.Status)
  5620. }
  5621. var err error
  5622. input, needResetTraffic, err = guest.ValidateChangeNicBillingModeInput(ctx, input, needResetTraffic)
  5623. if err != nil {
  5624. return errors.Wrap(err, "validateChangeNicBillingModeInput")
  5625. }
  5626. taskName := "GuestSetNicTrafficsTask"
  5627. if needResetTraffic {
  5628. taskName = "GuestResetNicTrafficsTask"
  5629. }
  5630. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  5631. params.Set("old_status", jsonutils.NewString(guest.Status))
  5632. guest.SetStatus(ctx, userCred, api.VM_SYNC_TRAFFIC_LIMIT, action)
  5633. task, err := taskman.TaskManager.NewTask(ctx, taskName, guest, userCred, params, "", "", nil)
  5634. if err != nil {
  5635. return errors.Wrap(err, "NewTask")
  5636. }
  5637. task.ScheduleRun(nil)
  5638. return nil
  5639. }
  5640. // 重置网卡计费方式
  5641. func (guest *SGuest) PerformResetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential,
  5642. query jsonutils.JSONObject, input api.ServerNicTrafficLimit) (jsonutils.JSONObject, error) {
  5643. err := guest.doChangeNicBillingMode(ctx, userCred, input, true, "PerformResetNicTrafficLimit")
  5644. if err != nil {
  5645. return nil, errors.Wrap(err, "doChangeNicBillingMode")
  5646. }
  5647. return nil, nil
  5648. }
  5649. // 设置网卡计费方式
  5650. func (guest *SGuest) PerformSetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential,
  5651. query jsonutils.JSONObject, input api.ServerNicTrafficLimit) (jsonutils.JSONObject, error) {
  5652. err := guest.doChangeNicBillingMode(ctx, userCred, input, false, "PerformSetNicTrafficLimit")
  5653. if err != nil {
  5654. return nil, errors.Wrap(err, "doChangeNicBillingMode")
  5655. }
  5656. return nil, nil
  5657. }
  5658. func (self *SGuest) PerformBindGroups(ctx context.Context, userCred mcclient.TokenCredential,
  5659. query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5660. groupIdSet, err := self.checkGroups(ctx, userCred, query, data)
  5661. if err != nil {
  5662. return nil, err
  5663. }
  5664. groupGuests, err := GroupguestManager.FetchByGuestId(self.Id)
  5665. if err != nil {
  5666. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, false)
  5667. return nil, err
  5668. }
  5669. for i := range groupGuests {
  5670. groupId := groupGuests[i].GroupId
  5671. if groupIdSet.Has(groupId) {
  5672. groupIdSet.Delete(groupId)
  5673. }
  5674. }
  5675. for _, groupId := range groupIdSet.UnsortedList() {
  5676. _, err := GroupguestManager.Attach(ctx, groupId, self.Id)
  5677. if err != nil {
  5678. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, false)
  5679. return nil, errors.Wrapf(err, "fail to attch group %s to guest %s", groupId, self.Id)
  5680. }
  5681. }
  5682. // ignore error
  5683. err = self.ClearSchedDescCache()
  5684. if err != nil {
  5685. log.Errorf("fail to clear scheduler desc cache after unbinding groups successfully")
  5686. }
  5687. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_BIND, nil, userCred, true)
  5688. return nil, nil
  5689. }
  5690. func (self *SGuest) PerformUnbindGroups(ctx context.Context, userCred mcclient.TokenCredential,
  5691. query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5692. groupIdSet, err := self.checkGroups(ctx, userCred, query, data)
  5693. if err != nil {
  5694. return nil, err
  5695. }
  5696. groupGuests, err := GroupguestManager.FetchByGuestId(self.Id)
  5697. if err != nil {
  5698. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, false)
  5699. return nil, err
  5700. }
  5701. for i := range groupGuests {
  5702. joint := groupGuests[i]
  5703. if !groupIdSet.Has(joint.GroupId) {
  5704. continue
  5705. }
  5706. err := joint.Detach(ctx, userCred)
  5707. if err != nil {
  5708. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, false)
  5709. return nil, errors.Wrapf(err, "fail to detach group %s to guest %s", joint.GroupId, self.Id)
  5710. }
  5711. }
  5712. // ignore error
  5713. err = self.ClearSchedDescCache()
  5714. if err != nil {
  5715. log.Errorf("fail to clear scheduler desc cache after binding groups successfully")
  5716. }
  5717. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_INSTANCE_GROUP_UNBIND, nil, userCred, true)
  5718. return nil, nil
  5719. }
  5720. // 绑定主机快照策略
  5721. // 主机只能绑定一个快照策略,已绑定时报错
  5722. // 若主机下任意磁盘已绑定快照策略则报错
  5723. func (self *SGuest) PerformBindSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
  5724. query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
  5725. if len(input.SnapshotpolicyId) == 0 {
  5726. return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
  5727. }
  5728. spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
  5729. if err != nil {
  5730. return nil, err
  5731. }
  5732. sp := spObj.(*SSnapshotPolicy)
  5733. if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
  5734. return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
  5735. }
  5736. // 主机只能绑定一个快照策略
  5737. cnt, err := SnapshotPolicyResourceManager.GetBindingCount(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
  5738. if err != nil {
  5739. return nil, errors.Wrap(err, "GetBindingCount")
  5740. }
  5741. if cnt > 0 {
  5742. return nil, httperrors.NewConflictError("guest already bound to a snapshot policy")
  5743. }
  5744. // 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
  5745. disks, err := self.GetDisks()
  5746. if err != nil {
  5747. return nil, errors.Wrap(err, "GetDisks")
  5748. }
  5749. for _, d := range disks {
  5750. diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  5751. if err != nil {
  5752. return nil, errors.Wrap(err, "GetBindingCount for disk")
  5753. }
  5754. if diskCnt > 0 {
  5755. return nil, httperrors.NewConflictError("guest has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", d.Name)
  5756. }
  5757. }
  5758. sr := &SSnapshotPolicyResource{}
  5759. sr.SetModelManager(SnapshotPolicyResourceManager, sr)
  5760. sr.SnapshotpolicyId = sp.Id
  5761. sr.ResourceId = self.Id
  5762. sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
  5763. if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
  5764. return nil, errors.Wrap(err, "Insert")
  5765. }
  5766. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_BIND, input, userCred, true)
  5767. return nil, nil
  5768. }
  5769. // 设置主机快照策略
  5770. // 可覆盖当前主机绑定的快照策略,若主机下任意磁盘已绑定快照策略,则自动解除磁盘快照策略
  5771. func (self *SGuest) PerformSetSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
  5772. query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
  5773. if len(input.SnapshotpolicyId) == 0 {
  5774. return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
  5775. }
  5776. spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
  5777. if err != nil {
  5778. return nil, err
  5779. }
  5780. sp := spObj.(*SSnapshotPolicy)
  5781. if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
  5782. return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
  5783. }
  5784. if err := SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
  5785. return nil, errors.Wrap(err, "RemoveByResource")
  5786. }
  5787. // 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
  5788. disks, err := self.GetDisks()
  5789. if err != nil {
  5790. return nil, errors.Wrap(err, "GetDisks")
  5791. }
  5792. for _, d := range disks {
  5793. if err := SnapshotPolicyResourceManager.RemoveByResource(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
  5794. return nil, errors.Wrap(err, "RemoveByResource")
  5795. }
  5796. }
  5797. sr := &SSnapshotPolicyResource{}
  5798. sr.SetModelManager(SnapshotPolicyResourceManager, sr)
  5799. sr.SnapshotpolicyId = sp.Id
  5800. sr.ResourceId = self.Id
  5801. sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
  5802. if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
  5803. return nil, errors.Wrap(err, "Insert")
  5804. }
  5805. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UPDATE, input, userCred, true)
  5806. return nil, nil
  5807. }
  5808. func (self *SGuest) checkGroups(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
  5809. data jsonutils.JSONObject) (sets.String, error) {
  5810. groupIdArr := jsonutils.GetArrayOfPrefix(data, "group")
  5811. if len(groupIdArr) == 0 {
  5812. return nil, httperrors.NewMissingParameterError("group.0 group.1 ... ")
  5813. }
  5814. groupIdSet := sets.NewString()
  5815. for i := range groupIdArr {
  5816. groupIdStr, _ := groupIdArr[i].GetString()
  5817. model, err := GroupManager.FetchByIdOrName(ctx, userCred, groupIdStr)
  5818. if err == sql.ErrNoRows {
  5819. return nil, httperrors.NewInputParameterError("no such group %s", groupIdStr)
  5820. }
  5821. if err != nil {
  5822. return nil, errors.Wrapf(err, "fail to fetch group by id or name %s", groupIdStr)
  5823. }
  5824. group := model.(*SGroup)
  5825. if group.ProjectId != self.ProjectId {
  5826. return nil, httperrors.NewForbiddenError("group and guest should belong to same project")
  5827. }
  5828. if group.Enabled.IsFalse() {
  5829. return nil, httperrors.NewForbiddenError("can not bind or unbind disabled instance group")
  5830. }
  5831. groupIdSet.Insert(group.GetId())
  5832. }
  5833. return groupIdSet, nil
  5834. }
  5835. // 公网Ip转Eip
  5836. // 要求虚拟机有公网IP,并且虚拟机状态为running 或 ready
  5837. // 目前仅支持阿里云和腾讯云
  5838. func (self *SGuest) PerformPublicipToEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestPublicipToEipInput) (jsonutils.JSONObject, error) {
  5839. publicip, err := self.GetPublicIp()
  5840. if err != nil {
  5841. return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetPublicIp"))
  5842. }
  5843. if publicip == nil {
  5844. return nil, httperrors.NewInputParameterError("The guest %s does not have any public IP", self.Name)
  5845. }
  5846. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  5847. return nil, httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, self.Status)
  5848. }
  5849. driver, err := self.GetDriver()
  5850. if err != nil {
  5851. return nil, err
  5852. }
  5853. if !driver.IsSupportPublicipToEip() {
  5854. return nil, httperrors.NewUnsupportOperationError("The %s guest not support public ip to eip operation", self.Hypervisor)
  5855. }
  5856. return nil, self.StartPublicipToEipTask(ctx, userCred, input.AutoStart, "")
  5857. }
  5858. func (self *SGuest) StartPublicipToEipTask(ctx context.Context, userCred mcclient.TokenCredential, autoStart bool, parentTaskId string) error {
  5859. data := jsonutils.NewDict()
  5860. data.Set("auto_start", jsonutils.NewBool(autoStart))
  5861. task, err := taskman.TaskManager.NewTask(ctx, "GuestPublicipToEipTask", self, userCred, data, parentTaskId, "", nil)
  5862. if err != nil {
  5863. return errors.Wrap(err, "NewTask")
  5864. }
  5865. self.SetStatus(ctx, userCred, api.VM_START_EIP_CONVERT, "")
  5866. task.ScheduleRun(nil)
  5867. return nil
  5868. }
  5869. func (self *SGuest) SetAutoRenew(autoRenew bool) error {
  5870. _, err := db.Update(self, func() error {
  5871. self.AutoRenew = autoRenew
  5872. return nil
  5873. })
  5874. return err
  5875. }
  5876. // 设置自动续费
  5877. // 要求虚拟机状态为running 或 ready
  5878. // 要求虚拟机计费类型为包年包月(预付费)
  5879. func (self *SGuest) PerformSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAutoRenewInput) (jsonutils.JSONObject, error) {
  5880. if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
  5881. return nil, httperrors.NewUnsupportOperationError("The guest status need be %s or %s, current is %s", api.VM_READY, api.VM_RUNNING, self.Status)
  5882. }
  5883. if self.BillingType != billing_api.BILLING_TYPE_PREPAID {
  5884. return nil, httperrors.NewUnsupportOperationError("Only %s guest support this operation", billing_api.BILLING_TYPE_PREPAID)
  5885. }
  5886. if len(input.Duration) == 0 {
  5887. input.Duration = "1M"
  5888. }
  5889. _, err := billing.ParseBillingCycle(input.Duration)
  5890. if err != nil {
  5891. return nil, httperrors.NewInputParameterError("invalid duration %s: %s", input.Duration, err)
  5892. }
  5893. if self.AutoRenew == input.AutoRenew {
  5894. return nil, nil
  5895. }
  5896. driver, err := self.GetDriver()
  5897. if err != nil {
  5898. return nil, err
  5899. }
  5900. if !driver.IsSupportSetAutoRenew() {
  5901. err := self.SetAutoRenew(input.AutoRenew)
  5902. if err != nil {
  5903. return nil, httperrors.NewGeneralError(err)
  5904. }
  5905. logclient.AddSimpleActionLog(self, logclient.ACT_SET_AUTO_RENEW, jsonutils.Marshal(input), userCred, true)
  5906. return nil, nil
  5907. }
  5908. return nil, self.StartSetAutoRenewTask(ctx, userCred, input, "")
  5909. }
  5910. func (self *SGuest) StartSetAutoRenewTask(ctx context.Context, userCred mcclient.TokenCredential, input api.GuestAutoRenewInput, parentTaskId string) error {
  5911. data := jsonutils.NewDict()
  5912. data.Set("auto_renew", jsonutils.NewBool(input.AutoRenew))
  5913. data.Set("duration", jsonutils.NewString(input.Duration))
  5914. task, err := taskman.TaskManager.NewTask(ctx, "GuestSetAutoRenewTask", self, userCred, data, parentTaskId, "", nil)
  5915. if err != nil {
  5916. return errors.Wrap(err, "NewTask")
  5917. }
  5918. self.SetStatus(ctx, userCred, api.VM_SET_AUTO_RENEW, "")
  5919. task.ScheduleRun(nil)
  5920. return nil
  5921. }
  5922. func (self *SGuest) PerformRemoteUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerRemoteUpdateInput) (jsonutils.JSONObject, error) {
  5923. err := self.StartRemoteUpdateTask(ctx, userCred, (input.ReplaceTags != nil && *input.ReplaceTags), "")
  5924. if err != nil {
  5925. return nil, errors.Wrap(err, "StartRemoteUpdateTask")
  5926. }
  5927. return nil, nil
  5928. }
  5929. func (self *SGuest) PerformOpenForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5930. req, err := guestdriver_types.NewOpenForwardRequestFromJSON(ctx, data)
  5931. if err != nil {
  5932. return nil, err
  5933. }
  5934. for _, nicDesc := range self.fetchNICShortDesc(ctx) {
  5935. if nicDesc.VpcId != api.DEFAULT_VPC_ID {
  5936. if req.Addr == "" {
  5937. req.Addr = nicDesc.IpAddr
  5938. }
  5939. req.NetworkId = nicDesc.NetworkId
  5940. }
  5941. }
  5942. if req.NetworkId == "" {
  5943. return nil, httperrors.NewInputParameterError("guest has no vpc ip")
  5944. }
  5945. driver, err := self.GetDriver()
  5946. if err != nil {
  5947. return nil, errors.Wrapf(err, "GetDriver")
  5948. }
  5949. resp, err := driver.RequestOpenForward(ctx, userCred, self, req)
  5950. if err != nil {
  5951. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_WEBSSH, err, userCred, false)
  5952. return nil, httperrors.NewGeneralError(err)
  5953. }
  5954. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_WEBSSH, resp, userCred, true)
  5955. return resp.JSON(), nil
  5956. }
  5957. func (self *SGuest) PerformCloseForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5958. req, err := guestdriver_types.NewCloseForwardRequestFromJSON(ctx, data)
  5959. if err != nil {
  5960. return nil, err
  5961. }
  5962. for _, nicDesc := range self.fetchNICShortDesc(ctx) {
  5963. if nicDesc.VpcId != api.DEFAULT_VPC_ID {
  5964. req.NetworkId = nicDesc.NetworkId
  5965. }
  5966. }
  5967. if req.NetworkId == "" {
  5968. return nil, httperrors.NewInputParameterError("guest has no vpc ip")
  5969. }
  5970. driver, err := self.GetDriver()
  5971. if err != nil {
  5972. return nil, err
  5973. }
  5974. resp, err := driver.RequestCloseForward(ctx, userCred, self, req)
  5975. if err != nil {
  5976. return nil, httperrors.NewGeneralError(err)
  5977. }
  5978. return resp.JSON(), nil
  5979. }
  5980. func (self *SGuest) PerformListForward(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  5981. req, err := guestdriver_types.NewListForwardRequestFromJSON(ctx, data)
  5982. if err != nil {
  5983. return nil, err
  5984. }
  5985. for _, nicDesc := range self.fetchNICShortDesc(ctx) {
  5986. if nicDesc.VpcId != api.DEFAULT_VPC_ID {
  5987. if req.Addr == "" {
  5988. req.Addr = nicDesc.IpAddr
  5989. }
  5990. req.NetworkId = nicDesc.NetworkId
  5991. }
  5992. }
  5993. if req.NetworkId == "" {
  5994. return nil, httperrors.NewInputParameterError("guest has no vpc ip")
  5995. }
  5996. driver, err := self.GetDriver()
  5997. if err != nil {
  5998. return nil, err
  5999. }
  6000. resp, err := driver.RequestListForward(ctx, userCred, self, req)
  6001. if err != nil {
  6002. return nil, httperrors.NewGeneralError(err)
  6003. }
  6004. return resp.JSON(), nil
  6005. }
  6006. // 更改存储
  6007. func (self *SGuest) PerformChangeStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeStorageInput) (*api.ServerChangeStorageInput, error) {
  6008. // validate input
  6009. if input.TargetStorageId == "" {
  6010. return nil, httperrors.NewNotEmptyError("Storage id is empty")
  6011. }
  6012. // validate storage
  6013. storageObj, err := StorageManager.FetchByIdOrName(ctx, userCred, input.TargetStorageId)
  6014. if err != nil {
  6015. return nil, errors.Wrapf(err, "Found storage by %s", input.TargetStorageId)
  6016. }
  6017. storage := storageObj.(*SStorage)
  6018. input.TargetStorageId = storage.GetId()
  6019. // validate disk
  6020. disks, err := self.GetDisks()
  6021. if err != nil {
  6022. return nil, errors.Wrapf(err, "Get server %s disks", self.GetName())
  6023. }
  6024. var changeDisks = []string{}
  6025. for _, disk := range disks {
  6026. if disk.StorageId != input.TargetStorageId {
  6027. changeDisks = append(changeDisks, disk.Id)
  6028. }
  6029. }
  6030. // driver validate
  6031. drv, err := self.GetDriver()
  6032. if err != nil {
  6033. return nil, err
  6034. }
  6035. if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
  6036. return nil, err
  6037. }
  6038. return nil, self.StartGuestChangeStorageTask(ctx, userCred, input, changeDisks)
  6039. }
  6040. func (self *SGuest) StartGuestChangeStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeStorageInput, disks []string) error {
  6041. params := api.ServerChangeStorageInternalInput{
  6042. ServerChangeStorageInput: *input,
  6043. Disks: disks,
  6044. DiskCount: len(disks),
  6045. GuestRunning: self.Status == api.VM_RUNNING,
  6046. }
  6047. reason := fmt.Sprintf("Change guest disks storage to %s", input.TargetStorageId)
  6048. self.SetStatus(ctx, userCred, api.VM_DISK_CHANGE_STORAGE, reason)
  6049. if task, err := taskman.TaskManager.NewTask(
  6050. ctx, "GuestChangeDisksStorageTask", self, userCred, jsonutils.Marshal(params).(*jsonutils.JSONDict),
  6051. "", "", nil); err != nil {
  6052. return err
  6053. } else {
  6054. task.ScheduleRun(nil)
  6055. }
  6056. return nil
  6057. }
  6058. func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerChangeDiskStorageInput) (*api.ServerChangeDiskStorageInput, error) {
  6059. // validate input
  6060. if input.DiskId == "" {
  6061. return nil, httperrors.NewNotEmptyError("Disk id is empty")
  6062. }
  6063. if input.TargetStorageId == "" {
  6064. return nil, httperrors.NewNotEmptyError("Storage id is empty")
  6065. }
  6066. // validate disk
  6067. disks, err := self.GetDisks()
  6068. if err != nil {
  6069. return nil, errors.Wrapf(err, "Get server %s disks", self.GetName())
  6070. }
  6071. var srcDisk *SDisk
  6072. for _, disk := range disks {
  6073. if input.DiskId == disk.GetId() || input.DiskId == disk.GetName() {
  6074. srcDisk = &disk
  6075. input.DiskId = disk.GetId()
  6076. break
  6077. }
  6078. }
  6079. if srcDisk == nil {
  6080. return nil, httperrors.NewNotFoundError("Disk %s not found on server %s", input.DiskId, self.GetName())
  6081. }
  6082. // validate storage
  6083. storageObj, err := StorageManager.FetchByIdOrName(ctx, userCred, input.TargetStorageId)
  6084. if err != nil {
  6085. return nil, errors.Wrapf(err, "Found storage by %s", input.TargetStorageId)
  6086. }
  6087. storage := storageObj.(*SStorage)
  6088. input.TargetStorageId = storage.GetId()
  6089. // driver validate
  6090. drv, err := self.GetDriver()
  6091. if err != nil {
  6092. return nil, err
  6093. }
  6094. if err := drv.ValidateChangeDiskStorage(ctx, userCred, self, input.TargetStorageId); err != nil {
  6095. return nil, err
  6096. }
  6097. {
  6098. copyInput := api.ServerCopyDiskToStorageInput{
  6099. KeepOriginDisk: input.KeepOriginDisk,
  6100. GuestRunning: self.Status == api.VM_RUNNING,
  6101. }
  6102. err := self.CopyDiskToStorage(ctx, userCred, srcDisk, storage, copyInput, "")
  6103. if err != nil {
  6104. return nil, errors.Wrap(err, "CopyDiskToStorage")
  6105. }
  6106. }
  6107. return nil, nil
  6108. }
  6109. func (guest *SGuest) CopyDiskToStorage(ctx context.Context, userCred mcclient.TokenCredential, srcDisk *SDisk, storage *SStorage, input api.ServerCopyDiskToStorageInput, parentTaskId string) error {
  6110. diskConf := &api.DiskConfig{
  6111. Index: -1,
  6112. ImageId: srcDisk.TemplateId,
  6113. SizeMb: srcDisk.DiskSize,
  6114. Fs: srcDisk.FsFormat,
  6115. DiskType: srcDisk.DiskType,
  6116. }
  6117. targetDisk, err := guest.CreateDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
  6118. if err != nil {
  6119. return errors.Wrapf(err, "Create target disk on storage %s", storage.GetName())
  6120. }
  6121. {
  6122. err := db.InheritFromTo(ctx, userCred, srcDisk, targetDisk)
  6123. if err != nil {
  6124. return errors.Wrapf(err, "Inherit class metadata from src %s to target %s", srcDisk.GetName(), targetDisk.GetName())
  6125. }
  6126. }
  6127. internalInput := &api.ServerChangeDiskStorageInternalInput{
  6128. ServerChangeDiskStorageInput: api.ServerChangeDiskStorageInput{
  6129. DiskId: srcDisk.Id,
  6130. TargetStorageId: storage.Id,
  6131. KeepOriginDisk: input.KeepOriginDisk,
  6132. },
  6133. StorageId: srcDisk.StorageId,
  6134. TargetDiskId: targetDisk.GetId(),
  6135. GuestRunning: input.GuestRunning,
  6136. CloneDiskCount: input.CloneDiskCount,
  6137. CompletedDiskCount: input.CompletedDiskCount,
  6138. }
  6139. {
  6140. err := guest.StartChangeDiskStorageTask(ctx, userCred, internalInput, parentTaskId)
  6141. if err != nil {
  6142. return errors.Wrap(err, "StartChangeDiskStorageTask")
  6143. }
  6144. }
  6145. return nil
  6146. }
  6147. func (self *SGuest) StartChangeDiskStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeDiskStorageInternalInput, parentTaskId string) error {
  6148. reason := fmt.Sprintf("Change disk %s to storage %s", input.DiskId, input.TargetStorageId)
  6149. self.SetStatus(ctx, userCred, api.VM_DISK_CHANGE_STORAGE, reason)
  6150. driver, err := self.GetDriver()
  6151. if err != nil {
  6152. return err
  6153. }
  6154. return driver.StartChangeDiskStorageTask(self, ctx, userCred, input, parentTaskId)
  6155. }
  6156. func (self *SGuest) startSwitchToClonedDisk(ctx context.Context, userCred mcclient.TokenCredential, taskId string) error {
  6157. task := taskman.TaskManager.FetchTaskById(taskId)
  6158. if task == nil {
  6159. return errors.Errorf("no task %s found", taskId)
  6160. }
  6161. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  6162. log.Infof("guest %s start switch to cloned disk task", self.Id)
  6163. params := jsonutils.NewDict()
  6164. params.Set("block_jobs_ready", jsonutils.JSONTrue)
  6165. return params, nil
  6166. })
  6167. return nil
  6168. }
  6169. // 设置启动顺序
  6170. func (self *SGuest) PerformSetBootIndex(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSetBootIndexInput) (jsonutils.JSONObject, error) {
  6171. gds, err := self.GetGuestDisks()
  6172. if err != nil {
  6173. return nil, err
  6174. }
  6175. diskBootIndexes := map[int8]int8{}
  6176. for i := 0; i < len(gds); i++ {
  6177. diskBootIndexes[gds[i].Index] = gds[i].BootIndex
  6178. }
  6179. gcs, err := self.getCdroms()
  6180. if err != nil {
  6181. return nil, err
  6182. }
  6183. cdromBootIndexes := map[int]int8{}
  6184. for i := 0; i < len(gcs); i++ {
  6185. cdromBootIndexes[gcs[i].Ordinal] = gcs[i].BootIndex
  6186. }
  6187. for sDiskIndex, bootIndex := range input.Disks {
  6188. iDiskIndex, err := strconv.Atoi(sDiskIndex)
  6189. if err != nil {
  6190. return nil, httperrors.NewInputParameterError("failed parse disk index %s", sDiskIndex)
  6191. } else if iDiskIndex > 127 {
  6192. return nil, httperrors.NewInputParameterError("disk inex %s is exceed 127", sDiskIndex)
  6193. }
  6194. diskIndex := int8(iDiskIndex)
  6195. if _, ok := diskBootIndexes[diskIndex]; !ok {
  6196. return nil, httperrors.NewBadRequestError("disk has no index %d", diskIndex)
  6197. }
  6198. diskBootIndexes[diskIndex] = bootIndex
  6199. }
  6200. for sCdromOrdinal, bootIndex := range input.Cdroms {
  6201. cdromOrdinal, err := strconv.Atoi(sCdromOrdinal)
  6202. if err != nil {
  6203. return nil, httperrors.NewInputParameterError("failed parse cdrom ordinal %s", sCdromOrdinal)
  6204. }
  6205. if _, ok := cdromBootIndexes[cdromOrdinal]; !ok {
  6206. return nil, httperrors.NewBadRequestError("cdrom has no ordinal %d", cdromOrdinal)
  6207. }
  6208. cdromBootIndexes[cdromOrdinal] = bootIndex
  6209. }
  6210. bm := bitmap.NewBitMap(128)
  6211. for diskIndex, bootIndex := range diskBootIndexes {
  6212. if bootIndex < 0 {
  6213. continue
  6214. }
  6215. if bm.Has(int64(bootIndex)) {
  6216. return nil, httperrors.NewBadRequestError("disk index %d boot index %d is duplicated", diskIndex, bootIndex)
  6217. } else {
  6218. bm.Set(int64(bootIndex))
  6219. }
  6220. }
  6221. for cdromOrdinal, bootIndex := range cdromBootIndexes {
  6222. if bootIndex < 0 {
  6223. continue
  6224. }
  6225. if bm.Has(int64(bootIndex)) {
  6226. return nil, httperrors.NewBadRequestError("cdrom ordianl %d boot index %d is duplicated", cdromOrdinal, bootIndex)
  6227. } else {
  6228. bm.Set(int64(bootIndex))
  6229. }
  6230. }
  6231. for i := 0; i < len(gds); i++ {
  6232. if gds[i].BootIndex != diskBootIndexes[gds[i].Index] {
  6233. if err := gds[i].SetBootIndex(diskBootIndexes[gds[i].Index]); err != nil {
  6234. log.Errorf("gds[i].SetBootIndex: %s", err)
  6235. return nil, err
  6236. }
  6237. }
  6238. }
  6239. for i := 0; i < len(gcs); i++ {
  6240. if gcs[i].BootIndex != cdromBootIndexes[gcs[i].Ordinal] {
  6241. if err := gcs[i].SetBootIndex(cdromBootIndexes[gcs[i].Ordinal]); err != nil {
  6242. log.Errorf("gcs[i].SetBootIndex: %s", err)
  6243. return nil, err
  6244. }
  6245. }
  6246. }
  6247. if self.Bios == api.VM_BOOT_MODE_UEFI {
  6248. data := jsonutils.NewDict()
  6249. data.Set("set_uefi_boot_order", jsonutils.JSONTrue)
  6250. if err := self.startSyncTask(ctx, userCred, false, "", data); err != nil {
  6251. return nil, err
  6252. }
  6253. }
  6254. return nil, nil
  6255. }
  6256. // 探测透传设备
  6257. func (self *SGuest) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6258. host, err := self.GetHost()
  6259. if err != nil {
  6260. return nil, errors.Wrap(err, "GetHost")
  6261. }
  6262. driver, err := host.GetHostDriver()
  6263. if err != nil {
  6264. return nil, errors.Wrapf(err, "GetHostDriver")
  6265. }
  6266. hostDevs, err := driver.RequestProbeIsolatedDevices(ctx, userCred, host, data)
  6267. if err != nil {
  6268. return nil, errors.Wrap(err, "RequestProbeIsolatedDevices")
  6269. }
  6270. objs, err := hostDevs.GetArray()
  6271. if err != nil {
  6272. return nil, errors.Wrapf(err, "GetArray from %q", hostDevs)
  6273. }
  6274. devs := make([]*SIsolatedDevice, 0)
  6275. for _, obj := range objs {
  6276. id, err := obj.GetString("id")
  6277. if err != nil {
  6278. return nil, errors.Wrapf(err, "device %s", obj)
  6279. }
  6280. devObj, err := IsolatedDeviceManager.FetchById(id)
  6281. if err != nil {
  6282. return nil, errors.Wrapf(err, "FetchById %q", id)
  6283. }
  6284. dev := devObj.(*SIsolatedDevice)
  6285. if dev.GuestId == "" {
  6286. devs = append(devs, dev)
  6287. }
  6288. }
  6289. return jsonutils.Marshal(devs), nil
  6290. }
  6291. func (self *SGuest) PerformCpuset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ServerCPUSetInput) (jsonutils.JSONObject, error) {
  6292. host, err := self.GetHost()
  6293. if err != nil {
  6294. return nil, errors.Wrap(err, "get host model")
  6295. }
  6296. allCores, err := host.getHostLogicalCores()
  6297. if err != nil {
  6298. return nil, err
  6299. }
  6300. if !sets.NewInt(allCores...).HasAll(data.CPUS...) {
  6301. return nil, httperrors.NewInputParameterError("Host cores %v not contains input %v", allCores, data.CPUS)
  6302. }
  6303. hostReservedCpus, err := host.GetReservedCpus()
  6304. if err != nil {
  6305. return nil, errors.Wrap(err, "host get reserved cpus")
  6306. }
  6307. for i := range data.CPUS {
  6308. if hostReservedCpus.Contains(data.CPUS[i]) {
  6309. return nil, httperrors.NewBadRequestError("request cpu %d has been reserved", data.CPUS[i])
  6310. }
  6311. }
  6312. pinnedMap, err := host.GetPinnedCpusetCores(ctx, userCred, []string{self.Id})
  6313. if err != nil {
  6314. return nil, errors.Wrap(err, "Get host pinned cpu cores")
  6315. }
  6316. if pinnedMap != nil {
  6317. for i := range data.CPUS {
  6318. if pinnedMap.Contains(data.CPUS[i]) {
  6319. return nil, httperrors.NewBadRequestError("request cpu %d has been set by other guests", data.CPUS[i])
  6320. }
  6321. }
  6322. }
  6323. if err := self.SetMetadata(ctx, api.VM_METADATA_CGROUP_CPUSET, data, userCred); err != nil {
  6324. return nil, errors.Wrap(err, "set metadata")
  6325. }
  6326. if err := host.updateHostReservedCpus(ctx, userCred); err != nil {
  6327. return nil, errors.Wrap(err, "updateHostReservedCpus")
  6328. }
  6329. return nil, self.StartGuestCPUSetTask(ctx, userCred, data)
  6330. }
  6331. func (self *SGuest) StartGuestCPUSetTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCPUSetInput) error {
  6332. task, err := taskman.TaskManager.NewTask(ctx, "GuestCPUSetTask", self, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), "", "")
  6333. if err != nil {
  6334. return errors.Wrap(err, "New GuestCPUSetTask")
  6335. }
  6336. return task.ScheduleRun(nil)
  6337. }
  6338. func (self *SGuest) PerformCpusetRemove(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ServerCPUSetRemoveInput) (*api.ServerCPUSetRemoveResp, error) {
  6339. if err := self.RemoveMetadata(ctx, api.VM_METADATA_CGROUP_CPUSET, userCred); err != nil {
  6340. return nil, errors.Wrapf(err, "remove metadata %q", api.VM_METADATA_CGROUP_CPUSET)
  6341. }
  6342. host, err := self.GetHost()
  6343. if err != nil {
  6344. return nil, errors.Wrap(err, "get host model")
  6345. }
  6346. // TODO: maybe change to async task
  6347. db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE, nil, userCred)
  6348. resp := new(api.ServerCPUSetRemoveResp)
  6349. drv, err := self.GetDriver()
  6350. if err != nil {
  6351. return nil, err
  6352. }
  6353. if err := drv.RequestCPUSetRemove(ctx, userCred, host, self, data); err != nil {
  6354. db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE_FAIL, err, userCred)
  6355. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CPUSET_REMOVE, data, userCred, false)
  6356. resp.Error = err.Error()
  6357. } else {
  6358. db.OpsLog.LogEvent(self, db.ACT_GUEST_CPUSET_REMOVE, nil, userCred)
  6359. logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_CPUSET_REMOVE, data, userCred, true)
  6360. resp.Done = true
  6361. }
  6362. return resp, nil
  6363. }
  6364. func (self *SGuest) getPinnedCpusetCores(ctx context.Context, userCred mcclient.TokenCredential) ([]int, error) {
  6365. obj := self.GetMetadataJson(ctx, api.VM_METADATA_CGROUP_CPUSET, userCred)
  6366. if obj == nil {
  6367. return nil, nil
  6368. }
  6369. pinnedInput := new(api.ServerCPUSetInput)
  6370. if err := obj.Unmarshal(pinnedInput); err != nil {
  6371. return nil, errors.Wrap(err, "Unmarshal to ServerCPUSetInput")
  6372. }
  6373. return pinnedInput.CPUS, nil
  6374. }
  6375. func (self *SGuest) GetDetailsCpusetCores(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerGetCPUSetCoresInput) (*api.ServerGetCPUSetCoresResp, error) {
  6376. host, err := self.GetHost()
  6377. if err != nil {
  6378. return nil, err
  6379. }
  6380. allCores, err := host.getHostLogicalCores()
  6381. if err != nil {
  6382. return nil, err
  6383. }
  6384. usedMap, err := host.GetPinnedCpusetCores(ctx, userCred, nil)
  6385. if err != nil {
  6386. return nil, err
  6387. }
  6388. resp := &api.ServerGetCPUSetCoresResp{
  6389. HostCores: allCores,
  6390. }
  6391. if usedMap != nil {
  6392. resp.HostUsedCores = usedMap.ToSlice()
  6393. }
  6394. // fetch cpuset pinned
  6395. pinned, err := self.getPinnedCpusetCores(ctx, userCred)
  6396. if err != nil {
  6397. log.Errorf("getPinnedCpusetCores error: %v", err)
  6398. } else {
  6399. resp.PinnedCores = pinned
  6400. }
  6401. return resp, nil
  6402. }
  6403. func (self *SGuest) GetDetailsNumaInfo(ctx context.Context, userCred mcclient.TokenCredential, _ *api.ServerGetNumaInfoInput) (*api.ServerGetNumaInfoResp, error) {
  6404. ret := new(api.ServerGetNumaInfoResp)
  6405. devs, _ := self.GetIsolatedDevices()
  6406. if len(devs) > 0 {
  6407. ret.IsolatedDevicesNumaNode = make([]int8, 0)
  6408. for i := range devs {
  6409. if devs[i].NumaNode > 0 {
  6410. ret.IsolatedDevicesNumaNode = append(ret.IsolatedDevicesNumaNode, devs[i].NumaNode)
  6411. }
  6412. }
  6413. }
  6414. ret.CpuNumaPin = self.CpuNumaPin
  6415. return ret, nil
  6416. }
  6417. func (self *SGuest) GetDetailsHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ *api.ServerGetHardwareInfoInput) (*api.ServerGetHardwareInfoResp, error) {
  6418. host, err := self.GetHost()
  6419. if err != nil {
  6420. return nil, errors.Wrap(err, "GetHost")
  6421. }
  6422. hostSpecObj := host.GetSpec(false)
  6423. hostSpec := new(api.HostSpec)
  6424. if hostSpecObj != nil {
  6425. if err := hostSpecObj.Unmarshal(hostSpec); err != nil {
  6426. return nil, errors.Wrap(err, "unmarshal host spec")
  6427. }
  6428. }
  6429. motherboardInfo := &api.ServerHardwareInfoMotherboard{}
  6430. if host.SysInfo != nil && host.SysInfo.Contains("motherboard_info") {
  6431. if err := host.SysInfo.Unmarshal(motherboardInfo, "motherboard_info"); err != nil {
  6432. return nil, errors.Wrapf(err, "unmarshal motherboard_info from host system info")
  6433. }
  6434. }
  6435. cpuInfo := &api.ServerHardwareInfoCPU{
  6436. Model: host.CpuDesc,
  6437. Count: self.VcpuCount,
  6438. }
  6439. memInfo := &api.ServerHardwareInfoMemory{
  6440. SizeMB: self.VmemSize,
  6441. }
  6442. // fill disks info
  6443. diskInfos := make([]*api.ServerHardwareInfoDisk, 0)
  6444. disks, err := self.GetDisks()
  6445. if err != nil {
  6446. return nil, errors.Wrap(err, "get disks")
  6447. }
  6448. for _, disk := range disks {
  6449. storage, err := disk.GetStorage()
  6450. if err != nil {
  6451. return nil, errors.Wrapf(err, "get disk %s storage", disk.GetId())
  6452. }
  6453. hdInfo, err := storage.GetDetailsHardwareInfo(ctx, userCred, nil)
  6454. if err != nil {
  6455. return nil, errors.Wrapf(err, "get storage %s hardware info", storage.GetId())
  6456. }
  6457. info := &api.ServerHardwareInfoDisk{
  6458. Id: disk.GetId(),
  6459. StorageId: storage.GetId(),
  6460. SizeMB: disk.DiskSize,
  6461. Bandwidth: hdInfo.Bandwidth,
  6462. }
  6463. if hdInfo.Vendor != nil {
  6464. info.Model = *hdInfo.Vendor
  6465. }
  6466. if hdInfo.Model != nil {
  6467. info.Model = fmt.Sprintf("%s %s", info.Model, *hdInfo.Model)
  6468. }
  6469. diskInfos = append(diskInfos, info)
  6470. }
  6471. // fill GPU info
  6472. devs, err := self.GetIsolatedDevices()
  6473. if err != nil {
  6474. return nil, errors.Wrap(err, "get isolated devices")
  6475. }
  6476. gpuInfos := make([]*api.ServerHardwareInfoGPU, 0)
  6477. for _, dev := range devs {
  6478. if !dev.IsGPU() {
  6479. continue
  6480. }
  6481. info := &api.ServerHardwareInfoGPU{
  6482. Id: dev.GetId(),
  6483. Model: dev.Model,
  6484. }
  6485. if dev.PcieInfo != nil {
  6486. info.PCIEInfo = dev.PcieInfo
  6487. }
  6488. modelObj, _ := IsolatedDeviceModelManager.GetByDevModel(dev.Model)
  6489. if modelObj != nil {
  6490. hdInfo, err := modelObj.GetDetailsHardwareInfo(ctx, userCred, nil)
  6491. if err != nil {
  6492. return nil, errors.Wrapf(err, "get device model %s hardware info", modelObj.GetId())
  6493. }
  6494. info.IsolatedDeviceModelHardwareInfo = hdInfo
  6495. }
  6496. gpuInfos = append(gpuInfos, info)
  6497. }
  6498. result := &api.ServerGetHardwareInfoResp{
  6499. Motherboard: motherboardInfo,
  6500. CPU: cpuInfo,
  6501. Memory: memInfo,
  6502. Disks: diskInfos,
  6503. GPUs: gpuInfos,
  6504. }
  6505. return result, nil
  6506. }
  6507. func (self *SGuest) PerformCalculateRecordChecksum(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6508. checksum, err := db.CalculateModelChecksum(self)
  6509. if err != nil {
  6510. return nil, errors.Wrap(err, "CalculateModelChecksum")
  6511. }
  6512. return jsonutils.Marshal(map[string]string{
  6513. "checksum": checksum,
  6514. }), nil
  6515. }
  6516. func (self *SGuest) PerformEnableMemclean(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6517. return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred)
  6518. }
  6519. func (self *SGuest) PerformSetTpm(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6520. enableTpm := jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_TPM, false)
  6521. if enableTpm {
  6522. return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_TPM, enableTpm, userCred)
  6523. } else {
  6524. return nil, self.RemoveMetadata(ctx, api.VM_METADATA_ENABLE_TPM, userCred)
  6525. }
  6526. }
  6527. // 设置操作系统信息
  6528. func (self *SGuest) PerformSetOsInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetOSInfoInput) (jsonutils.JSONObject, error) {
  6529. drv, err := self.GetDriver()
  6530. if err != nil {
  6531. return nil, err
  6532. }
  6533. if err := drv.ValidateSetOSInfo(ctx, userCred, self, &input); err != nil {
  6534. return nil, err
  6535. }
  6536. if input.Type != "" {
  6537. if _, err := db.Update(self, func() error {
  6538. self.OsType = input.Type
  6539. return nil
  6540. }); err != nil {
  6541. return nil, errors.Wrapf(err, "update os_type")
  6542. }
  6543. }
  6544. for k, v := range map[string]string{
  6545. api.VM_METADATA_OS_NAME: input.Type,
  6546. api.VM_METADATA_OS_VERSION: input.Version,
  6547. api.VM_METADATA_OS_DISTRO: input.Distribution,
  6548. api.VM_METADATA_OS_ARCH: input.Arch,
  6549. } {
  6550. if len(v) == 0 {
  6551. continue
  6552. }
  6553. if err := self.SetMetadata(ctx, k, v, userCred); err != nil {
  6554. return nil, errors.Wrapf(err, "set metadata %s to %s", k, v)
  6555. }
  6556. }
  6557. return nil, nil
  6558. }
  6559. // 同步操作系统信息
  6560. func (self *SGuest) PerformSyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6561. drv, err := self.GetDriver()
  6562. if err != nil {
  6563. return nil, err
  6564. }
  6565. if err := drv.ValidateSyncOSInfo(ctx, userCred, self); err != nil {
  6566. return nil, err
  6567. }
  6568. if self.Status == api.VM_READY {
  6569. // start guest deploy task to sync os info
  6570. return nil, self.StartGuestDeployTask(ctx, userCred, nil, "deploy", "")
  6571. } else {
  6572. res, err := self.PerformQgaPing(ctx, userCred, nil, nil)
  6573. if err != nil || res.Contains("ping_error") {
  6574. return nil, httperrors.NewBadRequestError("qga ping failed is qga running?")
  6575. }
  6576. // try qga get os info
  6577. return nil, self.startQgaSyncOsInfoTask(ctx, userCred, "")
  6578. }
  6579. }
  6580. func (g *SGuest) PerformSetRootDiskMatcher(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, data *api.BaremetalRootDiskMatcher) (jsonutils.JSONObject, error) {
  6581. if g.GetHypervisor() != api.HYPERVISOR_BAREMETAL {
  6582. return nil, httperrors.NewNotAcceptableError("only %s support for setting root disk matcher", api.HYPERVISOR_BAREMETAL)
  6583. }
  6584. if err := baremetal.ValidateRootDiskMatcher(data); err != nil {
  6585. return nil, err
  6586. }
  6587. if err := g.SetMetadata(ctx, api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER, jsonutils.Marshal(data), userCred); err != nil {
  6588. return nil, errors.Wrapf(err, "set %s", api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
  6589. }
  6590. return nil, nil
  6591. }
  6592. func (g *SGuest) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerChangeBillingTypeInput) (jsonutils.JSONObject, error) {
  6593. if !utils.IsInStringArray(g.Status, []string{api.VM_RUNNING, api.VM_READY}) {
  6594. return nil, httperrors.NewServerStatusError("Cannot change guest billing type in status %s", g.Status)
  6595. }
  6596. if len(input.BillingType) == 0 {
  6597. return nil, httperrors.NewMissingParameterError("billing_type")
  6598. }
  6599. if !utils.IsInStringArray(string(input.BillingType), []string{string(billing_api.BILLING_TYPE_POSTPAID), string(billing_api.BILLING_TYPE_PREPAID)}) {
  6600. return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType)
  6601. }
  6602. if g.BillingType == input.BillingType {
  6603. return nil, nil
  6604. }
  6605. return nil, g.StartChangeBillingTypeTask(ctx, userCred, "")
  6606. }
  6607. func (self *SGuest) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  6608. self.SetStatus(ctx, userCred, apis.STATUS_CHANGE_BILLING_TYPE, "")
  6609. kwargs := jsonutils.NewDict()
  6610. task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeBillingTypeTask", self, userCred, kwargs, parentTaskId, "", nil)
  6611. if err != nil {
  6612. return err
  6613. }
  6614. return task.ScheduleRun(nil)
  6615. }
  6616. func (self *SGuest) PerformDisableAutoMergeSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6617. disableAutoMergeSnapshot := jsonutils.QueryBoolean(data, "disable_auto_merge_snapshot", false)
  6618. return nil, self.SetMetadata(ctx, api.VM_METADATA_DISABLE_AUTO_MERGE_SNAPSHOT, disableAutoMergeSnapshot, userCred)
  6619. }
  6620. func (self *SGuest) PerformSetKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.KickstartConfig) (jsonutils.JSONObject, error) {
  6621. // Check if kickstart has already been completed
  6622. currentFlag := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
  6623. if currentFlag == "true" {
  6624. return jsonutils.Marshal(map[string]string{
  6625. "message": "Kickstart has already been completed for this VM",
  6626. "status": "failed",
  6627. }), nil
  6628. }
  6629. if err := self.SetKickstartConfig(ctx, &input, userCred); err != nil {
  6630. return nil, errors.Wrap(err, "set kickstart config")
  6631. }
  6632. // Determine and set kickstart type based on input
  6633. kickstartType := determineKickstartType(&input)
  6634. if err := self.SetKickstartType(ctx, kickstartType, userCred); err != nil {
  6635. return nil, errors.Wrap(err, "set kickstart type")
  6636. }
  6637. if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, "0", userCred); err != nil {
  6638. return nil, errors.Wrap(err, "set kickstart attempt")
  6639. }
  6640. db.OpsLog.LogEvent(self, db.ACT_UPDATE, "set kickstart config", userCred)
  6641. // Check if VM needs to be restarted
  6642. needRestart := false
  6643. if utils.IsInStringArray(self.Status, []string{
  6644. api.VM_RUNNING,
  6645. api.VM_KICKSTART_INSTALLING,
  6646. api.VM_KICKSTART_COMPLETED,
  6647. api.VM_KICKSTART_FAILED,
  6648. api.VM_BLOCK_STREAM,
  6649. api.VM_MIGRATING,
  6650. }) {
  6651. needRestart = true
  6652. }
  6653. if err := self.SetKickstartStatus(ctx, api.VM_KICKSTART_PENDING, userCred); err != nil {
  6654. return nil, errors.Wrap(err, "set kickstart status")
  6655. }
  6656. // If VM is running, restart it to apply kickstart config
  6657. if needRestart {
  6658. driver, err := self.GetDriver()
  6659. if err != nil {
  6660. return nil, errors.Wrap(err, "get driver for restart")
  6661. }
  6662. if err := driver.StartGuestRestartTask(self, ctx, userCred, false, "kickstart config updated"); err != nil {
  6663. return nil, errors.Wrap(err, "start restart task")
  6664. }
  6665. return jsonutils.Marshal(map[string]string{
  6666. "status": "success",
  6667. "message": "kickstart config updated, restarting VM",
  6668. }), nil
  6669. }
  6670. return jsonutils.Marshal(map[string]string{
  6671. "status": "success",
  6672. }), nil
  6673. }
  6674. func (self *SGuest) PerformKickstartComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6675. // Check if kickstart_completed_flag is already set to true
  6676. currentFlag := self.GetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
  6677. if currentFlag == "true" {
  6678. return jsonutils.Marshal(map[string]string{
  6679. "status": "success",
  6680. "message": "kickstart is already marked as completed",
  6681. }), nil
  6682. }
  6683. // Set the kickstart_completed_flag metadata to true
  6684. if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, "true", userCred); err != nil {
  6685. return nil, errors.Wrap(err, "set kickstart completed flag")
  6686. }
  6687. // Update status to VM_KICKSTART_COMPLETED if in kickstart status
  6688. if self.IsInKickstartStatus() && self.Status != api.VM_KICKSTART_COMPLETED {
  6689. if err := self.SetStatus(ctx, userCred, api.VM_KICKSTART_COMPLETED, "kickstart manually marked as completed"); err != nil {
  6690. return nil, errors.Wrap(err, "update kickstart status")
  6691. }
  6692. }
  6693. // Check if restart is requested (default is true)
  6694. restart := jsonutils.QueryBoolean(input, "restart", true)
  6695. if restart {
  6696. // Trigger VM restart to complete the kickstart process
  6697. if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_KICKSTART_INSTALLING, api.VM_KICKSTART_COMPLETED, api.VM_KICKSTART_FAILED}) {
  6698. driver, err := self.GetDriver()
  6699. if err != nil {
  6700. return nil, errors.Wrap(err, "get driver")
  6701. }
  6702. driver.StartGuestRestartTask(self, ctx, userCred, false, "")
  6703. return jsonutils.Marshal(map[string]string{
  6704. "status": "success",
  6705. "message": "kickstart marked as completed, restarting VM",
  6706. }), nil
  6707. }
  6708. }
  6709. db.OpsLog.LogEvent(self, db.ACT_UPDATE, "kickstart manually marked as completed", userCred)
  6710. return jsonutils.Marshal(map[string]string{
  6711. "status": "success",
  6712. "message": "kickstart marked as completed",
  6713. }), nil
  6714. }
  6715. func (self *SGuest) PerformDeleteKickstart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  6716. if self.Status == api.VM_KICKSTART_INSTALLING {
  6717. return nil, httperrors.NewInvalidStatusError("cannot delete kickstart config while installation is in progress")
  6718. }
  6719. if err := self.SetKickstartConfig(ctx, nil, userCred); err != nil {
  6720. return nil, errors.Wrap(err, "delete kickstart config")
  6721. }
  6722. // 如果当前是kickstart状态,需要转换回适当的状态
  6723. if self.IsInKickstartStatus() {
  6724. if self.Status == api.VM_KICKSTART_PENDING {
  6725. self.SetStatus(ctx, userCred, api.VM_READY, "kickstart config deleted")
  6726. } else if self.Status == api.VM_KICKSTART_COMPLETED {
  6727. self.SetStatus(ctx, userCred, api.VM_RUNNING, "kickstart config deleted")
  6728. } else if self.Status == api.VM_KICKSTART_FAILED {
  6729. self.SetStatus(ctx, userCred, api.VM_READY, "kickstart config deleted")
  6730. }
  6731. }
  6732. // 清理kickstart相关metadata
  6733. self.RemoveMetadata(ctx, api.VM_METADATA_KICKSTART_ATTEMPT, userCred)
  6734. self.RemoveMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, userCred)
  6735. db.OpsLog.LogEvent(self, db.ACT_DELETE, "delete kickstart config", userCred)
  6736. return jsonutils.Marshal(map[string]string{
  6737. "status": "success",
  6738. }), nil
  6739. }
  6740. func (self *SGuest) PerformUpdateKickstartStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerUpdateKickstartStatusInput) (jsonutils.JSONObject, error) {
  6741. if err := self.SetKickstartStatus(ctx, input.Status, userCred); err != nil {
  6742. return nil, errors.Wrap(err, "update kickstart status")
  6743. }
  6744. if input.Status == api.VM_KICKSTART_COMPLETED {
  6745. if err := self.SetMetadata(ctx, api.VM_METADATA_KICKSTART_COMPLETED_FLAG, "true", userCred); err != nil {
  6746. log.Errorf("Failed to set kickstart completed flag for VM %s: %v", self.Name, err)
  6747. }
  6748. }
  6749. if input.ErrorMessage != "" {
  6750. db.OpsLog.LogEvent(self, db.ACT_UPDATE, fmt.Sprintf("kickstart status: %s, error: %s", input.Status, input.ErrorMessage), userCred)
  6751. } else {
  6752. db.OpsLog.LogEvent(self, db.ACT_UPDATE, fmt.Sprintf("kickstart status: %s", input.Status), userCred)
  6753. }
  6754. return jsonutils.Marshal(map[string]string{
  6755. "status": input.Status,
  6756. }), nil
  6757. }